Repository: helloqingfeng/Awsome-Front-End-learning-resource Branch: master Commit: 4a1603c5cd3c Files: 708 Total size: 39.3 MB Directory structure: gitextract_jr3vv7t_/ ├── 01-FE-learning-master/ │ └── README.md ├── 02-fedHandlebook-master/ │ ├── README.md │ ├── SUMMARY.md │ ├── learning/ │ │ ├── accessibility.md │ │ ├── animation.md │ │ ├── api.md │ │ ├── browsers.md │ │ ├── build.md │ │ ├── cli.md │ │ ├── courses.md │ │ ├── design.md │ │ ├── dev-tools.md │ │ ├── direct-learning.md │ │ ├── dns.md │ │ ├── dom.md │ │ ├── fonts.md │ │ ├── front-end-apps.md │ │ ├── general-front-end.md │ │ ├── headless-browsers.md │ │ ├── html-css.md │ │ ├── interface.md │ │ ├── internet.md │ │ ├── js.md │ │ ├── json.md │ │ ├── learn-from.md │ │ ├── module.md │ │ ├── multi-things-dev.md │ │ ├── networks.md │ │ ├── news-podcasts.md │ │ ├── node.md │ │ ├── offline.md │ │ ├── optimizing.md │ │ ├── package.md │ │ ├── react.md │ │ ├── security.md │ │ ├── self-direct-learning.md │ │ ├── seo.md │ │ ├── static.md │ │ ├── test.md │ │ ├── version.md │ │ └── web-hosting.md │ ├── learning.md │ ├── practice/ │ │ ├── fd-dev-for.md │ │ ├── front-end-interview.md │ │ ├── front-end-jobs-titles.md │ │ ├── front-end-jobs.md │ │ ├── front-end-skills.md │ │ ├── front-end-team-role.md │ │ ├── full-stack.md │ │ ├── making-fd.md │ │ ├── salaries.md │ │ └── tech-employed-by-fd.md │ ├── practice.md │ ├── styles/ │ │ ├── ebook.css │ │ └── website.css │ ├── tools/ │ │ ├── animation.md │ │ ├── app.md │ │ ├── bass.md │ │ ├── browser.md │ │ ├── browserdocs.md │ │ ├── charts.md │ │ ├── cms.md │ │ ├── code-editor.md │ │ ├── coll.md │ │ ├── css.md │ │ ├── data.md │ │ ├── deploy.md │ │ ├── diagram.md │ │ ├── dom.md │ │ ├── error.md │ │ ├── general-tools.md │ │ ├── graphics.md │ │ ├── hosting.md │ │ ├── html.md │ │ ├── http.md │ │ ├── js.md │ │ ├── json.md │ │ ├── loader.md │ │ ├── monitor.md │ │ ├── offline.md │ │ ├── perf.md │ │ ├── project.md │ │ ├── proto.md │ │ ├── repo.md │ │ ├── scaffolding.md │ │ ├── security.md │ │ ├── seo.md │ │ ├── static.md │ │ ├── svg.md │ │ ├── task.md │ │ ├── templates.md │ │ ├── test.md │ │ └── ui.md │ ├── tools.md │ └── what-is-a-fd.md ├── 03-FEND_Note-master/ │ ├── AUTHORS │ ├── Booklist.md │ ├── README.md │ ├── SUMMARY.md │ ├── SampleCode/ │ │ ├── CSS/ │ │ │ ├── Animation.html │ │ │ ├── AnimationPractice.html │ │ │ ├── Transform2D.html │ │ │ ├── Transform3D.html │ │ │ └── Transition.html │ │ └── Layout/ │ │ ├── 00_center_horizontal.html │ │ ├── 01_center_vertical.html │ │ └── 02multiple_column.html │ ├── TOC.md │ ├── assets/ │ │ └── Bookcover.sketch │ ├── book.json │ ├── chapter1/ │ │ ├── 00_intro.md │ │ ├── 01_01_tool_panel_view.md │ │ ├── 01_02_measurement_and_color.md │ │ ├── 01_03_slice.md │ │ ├── 01_04_save_image.md │ │ ├── 01_05_image_optimisation.md │ │ ├── 01_photoshop.md │ │ ├── 02_01_sublime.md │ │ ├── 02_02_atom.md │ │ ├── 02_dev_tools.md │ │ ├── 03_01_html_intro.md │ │ ├── 03_02_html_sytax.md │ │ ├── 03_03_html_ascii_encoding.md │ │ ├── 03_04_cross_browser.md │ │ ├── 03_05_html_tags.md │ │ ├── 03_html.md │ │ ├── 04_01_css_sytax.md │ │ ├── 04_02_selector.md │ │ ├── 04_03_text.md │ │ ├── 04_04_box_model.md │ │ ├── 04_05_background.md │ │ ├── 04_06_layout.md │ │ ├── 04_07_transform.md │ │ ├── 04_08_animation.md │ │ ├── 04_09_layout_demo.md │ │ └── 04_css_intro.md │ ├── chapter2/ │ │ ├── 00_intro.md │ │ ├── 01_javascript_intro.md │ │ ├── 02_dev_tools.md │ │ ├── 03_basic_syntax.md │ │ ├── 04_data_type.md │ │ ├── 05_internal_object.md │ │ ├── 06_scope.md │ │ ├── 07_statement_and_operator.md │ │ ├── 08_statement.md │ │ ├── 09_closure.md │ │ ├── 10_object.md │ │ ├── 11_js_type_determin.md │ │ └── 12_reg_exp.md │ ├── chapter3/ │ │ ├── 00_intro.md │ │ ├── 01_dom_tree.md │ │ ├── 02_node_manipulation.md │ │ ├── 03_attribute.md │ │ ├── 04_style_manipulation.md │ │ ├── 05_events.md │ │ ├── 06_animation.md │ │ ├── 07_canvas.md │ │ ├── 08_multimedia.md │ │ ├── 09_network.md │ │ ├── 10_bom.md │ │ ├── 11_storage.md │ │ ├── 12_form_manipulation.md │ │ └── 13_list_manipulation.md │ ├── chapter4/ │ │ ├── 00_intro.md │ │ ├── 01_CSS_Reset.md │ │ ├── 02_layout.md │ │ ├── 03_responsive.md │ │ ├── 04_page_optimisation.md │ │ └── 05_modulation.md │ ├── chapter5/ │ │ ├── 00_intro.md │ │ ├── 01_collaboration.md │ │ ├── 02_design_api.md │ │ ├── 03_version_control.md │ │ ├── 04_tech_selection.md │ │ └── 05_development.md │ └── config.json ├── 04-Front-end-tutorial-master/ │ ├── KnowledgeSystem.md │ ├── README/ │ │ ├── Cache/ │ │ │ ├── Cache.php │ │ │ ├── drivers/ │ │ │ │ ├── Cache_apc.php │ │ │ │ ├── Cache_dummy.php │ │ │ │ ├── Cache_file.php │ │ │ │ ├── Cache_memcached.php │ │ │ │ ├── Cache_redis.php │ │ │ │ ├── Cache_wincache.php │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── Calendar.php │ │ ├── Cart.php │ │ ├── Driver.php │ │ ├── Email.php │ │ ├── Encrypt.php │ │ ├── Encryption.php │ │ ├── Form_validation.php │ │ ├── Ftp.php │ │ ├── Image_lib.php │ │ ├── Javascript/ │ │ │ ├── Jquery.php │ │ │ └── index.html │ │ ├── Javascript.php │ │ ├── Migration.php │ │ ├── Pagination.php │ │ ├── Parser.php │ │ ├── Profiler.php │ │ ├── README.php │ │ ├── Session/ │ │ │ ├── Session.php │ │ │ ├── SessionHandlerInterface.php │ │ │ ├── Session_driver.php │ │ │ ├── drivers/ │ │ │ │ ├── Session_database_driver.php │ │ │ │ ├── Session_files_driver.php │ │ │ │ ├── Session_memcached_driver.php │ │ │ │ ├── Session_redis_driver.php │ │ │ │ └── index.html │ │ │ └── index.html │ │ ├── Table.php │ │ ├── Trackback.php │ │ ├── Typography.php │ │ ├── Unit_test.php │ │ ├── Upload.php │ │ ├── User_agent.php │ │ ├── Xmlrpc.php │ │ ├── Xmlrpcs.php │ │ ├── Zip.php │ │ └── index.html │ ├── README.md │ ├── README_old.md │ └── project.md ├── 05-fks-master/ │ ├── .gitignore │ ├── Makefile │ ├── README.en.md │ ├── README.md │ ├── bin/ │ │ └── generate.js │ ├── fks_chart/ │ │ ├── bower.json │ │ ├── bower_components/ │ │ │ ├── angular/ │ │ │ │ ├── .bower.json │ │ │ │ ├── README.md │ │ │ │ ├── angular-csp.css │ │ │ │ ├── angular.js │ │ │ │ ├── angular.min.js.gzip │ │ │ │ ├── bower.json │ │ │ │ └── package.json │ │ │ ├── angular-marked/ │ │ │ │ ├── .bower.json │ │ │ │ ├── README.md │ │ │ │ ├── angular-marked.js │ │ │ │ ├── bower.json │ │ │ │ ├── example/ │ │ │ │ │ └── index.html │ │ │ │ ├── gruntfile.js │ │ │ │ ├── karma.conf.js │ │ │ │ ├── package.json │ │ │ │ └── todo.md │ │ │ ├── d3/ │ │ │ │ ├── .bower.json │ │ │ │ ├── .spmignore │ │ │ │ ├── CONTRIBUTING.md │ │ │ │ ├── LICENSE │ │ │ │ ├── README.md │ │ │ │ ├── bower.json │ │ │ │ ├── composer.json │ │ │ │ └── d3.js │ │ │ ├── jquery/ │ │ │ │ ├── .bower.json │ │ │ │ ├── MIT-LICENSE.txt │ │ │ │ ├── bower.json │ │ │ │ ├── dist/ │ │ │ │ │ └── jquery.js │ │ │ │ └── src/ │ │ │ │ ├── ajax/ │ │ │ │ │ ├── jsonp.js │ │ │ │ │ ├── load.js │ │ │ │ │ ├── parseJSON.js │ │ │ │ │ ├── parseXML.js │ │ │ │ │ ├── script.js │ │ │ │ │ ├── var/ │ │ │ │ │ │ ├── nonce.js │ │ │ │ │ │ └── rquery.js │ │ │ │ │ └── xhr.js │ │ │ │ ├── ajax.js │ │ │ │ ├── attributes/ │ │ │ │ │ ├── attr.js │ │ │ │ │ ├── classes.js │ │ │ │ │ ├── prop.js │ │ │ │ │ ├── support.js │ │ │ │ │ └── val.js │ │ │ │ ├── attributes.js │ │ │ │ ├── callbacks.js │ │ │ │ ├── core/ │ │ │ │ │ ├── access.js │ │ │ │ │ ├── init.js │ │ │ │ │ ├── parseHTML.js │ │ │ │ │ ├── ready.js │ │ │ │ │ └── var/ │ │ │ │ │ └── rsingleTag.js │ │ │ │ ├── core.js │ │ │ │ ├── css/ │ │ │ │ │ ├── addGetHookIf.js │ │ │ │ │ ├── curCSS.js │ │ │ │ │ ├── defaultDisplay.js │ │ │ │ │ ├── hiddenVisibleSelectors.js │ │ │ │ │ ├── support.js │ │ │ │ │ ├── swap.js │ │ │ │ │ └── var/ │ │ │ │ │ ├── cssExpand.js │ │ │ │ │ ├── getStyles.js │ │ │ │ │ ├── isHidden.js │ │ │ │ │ ├── rmargin.js │ │ │ │ │ └── rnumnonpx.js │ │ │ │ ├── css.js │ │ │ │ ├── data/ │ │ │ │ │ ├── Data.js │ │ │ │ │ ├── accepts.js │ │ │ │ │ └── var/ │ │ │ │ │ ├── data_priv.js │ │ │ │ │ └── data_user.js │ │ │ │ ├── data.js │ │ │ │ ├── deferred.js │ │ │ │ ├── deprecated.js │ │ │ │ ├── dimensions.js │ │ │ │ ├── effects/ │ │ │ │ │ ├── Tween.js │ │ │ │ │ └── animatedSelector.js │ │ │ │ ├── effects.js │ │ │ │ ├── event/ │ │ │ │ │ ├── ajax.js │ │ │ │ │ ├── alias.js │ │ │ │ │ └── support.js │ │ │ │ ├── event.js │ │ │ │ ├── exports/ │ │ │ │ │ ├── amd.js │ │ │ │ │ └── global.js │ │ │ │ ├── intro.js │ │ │ │ ├── jquery.js │ │ │ │ ├── manipulation/ │ │ │ │ │ ├── _evalUrl.js │ │ │ │ │ ├── support.js │ │ │ │ │ └── var/ │ │ │ │ │ └── rcheckableType.js │ │ │ │ ├── manipulation.js │ │ │ │ ├── offset.js │ │ │ │ ├── outro.js │ │ │ │ ├── queue/ │ │ │ │ │ └── delay.js │ │ │ │ ├── queue.js │ │ │ │ ├── selector-native.js │ │ │ │ ├── selector-sizzle.js │ │ │ │ ├── selector.js │ │ │ │ ├── serialize.js │ │ │ │ ├── sizzle/ │ │ │ │ │ └── dist/ │ │ │ │ │ └── sizzle.js │ │ │ │ ├── traversing/ │ │ │ │ │ ├── findFilter.js │ │ │ │ │ └── var/ │ │ │ │ │ └── rneedsContext.js │ │ │ │ ├── traversing.js │ │ │ │ ├── var/ │ │ │ │ │ ├── arr.js │ │ │ │ │ ├── class2type.js │ │ │ │ │ ├── concat.js │ │ │ │ │ ├── hasOwn.js │ │ │ │ │ ├── indexOf.js │ │ │ │ │ ├── pnum.js │ │ │ │ │ ├── push.js │ │ │ │ │ ├── rnotwhite.js │ │ │ │ │ ├── slice.js │ │ │ │ │ ├── strundefined.js │ │ │ │ │ ├── support.js │ │ │ │ │ └── toString.js │ │ │ │ └── wrap.js │ │ │ └── marked/ │ │ │ ├── .bower.json │ │ │ ├── .gitignore │ │ │ ├── .npmignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── bin/ │ │ │ │ └── marked │ │ │ ├── component.json │ │ │ ├── doc/ │ │ │ │ ├── broken.md │ │ │ │ └── todo.md │ │ │ ├── index.js │ │ │ ├── lib/ │ │ │ │ └── marked.js │ │ │ ├── man/ │ │ │ │ └── marked.1 │ │ │ ├── package.json │ │ │ └── test/ │ │ │ ├── README │ │ │ ├── browser/ │ │ │ │ ├── index.html │ │ │ │ ├── index.js │ │ │ │ └── test.js │ │ │ ├── index.js │ │ │ ├── new/ │ │ │ │ ├── autolink_lines.html │ │ │ │ ├── autolink_lines.text │ │ │ │ ├── blockquote_list_item.html │ │ │ │ ├── blockquote_list_item.text │ │ │ │ ├── case_insensitive_refs.html │ │ │ │ ├── case_insensitive_refs.text │ │ │ │ ├── def_blocks.html │ │ │ │ ├── def_blocks.text │ │ │ │ ├── double_link.html │ │ │ │ ├── double_link.text │ │ │ │ ├── escaped_angles.html │ │ │ │ ├── escaped_angles.text │ │ │ │ ├── gfm_break.breaks.html │ │ │ │ ├── gfm_break.breaks.text │ │ │ │ ├── gfm_code.html │ │ │ │ ├── gfm_code.text │ │ │ │ ├── gfm_code_hr_list.html │ │ │ │ ├── gfm_code_hr_list.text │ │ │ │ ├── gfm_del.html │ │ │ │ ├── gfm_del.text │ │ │ │ ├── gfm_em.html │ │ │ │ ├── gfm_em.text │ │ │ │ ├── gfm_links.html │ │ │ │ ├── gfm_links.text │ │ │ │ ├── gfm_tables.html │ │ │ │ ├── gfm_tables.text │ │ │ │ ├── hr_list_break.html │ │ │ │ ├── hr_list_break.text │ │ │ │ ├── lazy_blockquotes.html │ │ │ │ ├── lazy_blockquotes.text │ │ │ │ ├── list_item_text.html │ │ │ │ ├── list_item_text.text │ │ │ │ ├── loose_lists.html │ │ │ │ ├── loose_lists.text │ │ │ │ ├── main.html │ │ │ │ ├── main.text │ │ │ │ ├── nested_code.html │ │ │ │ ├── nested_code.text │ │ │ │ ├── nested_em.html │ │ │ │ ├── nested_em.text │ │ │ │ ├── nested_square_link.html │ │ │ │ ├── nested_square_link.text │ │ │ │ ├── not_a_link.html │ │ │ │ ├── not_a_link.text │ │ │ │ ├── ref_paren.html │ │ │ │ ├── ref_paren.text │ │ │ │ ├── same_bullet.html │ │ │ │ ├── same_bullet.text │ │ │ │ ├── text.smartypants.html │ │ │ │ ├── text.smartypants.text │ │ │ │ ├── toplevel_paragraphs.gfm.html │ │ │ │ ├── toplevel_paragraphs.gfm.text │ │ │ │ ├── tricky_list.html │ │ │ │ └── tricky_list.text │ │ │ ├── original/ │ │ │ │ ├── amps_and_angles_encoding.html │ │ │ │ ├── amps_and_angles_encoding.text │ │ │ │ ├── auto_links.html │ │ │ │ ├── auto_links.text │ │ │ │ ├── backslash_escapes.html │ │ │ │ ├── backslash_escapes.text │ │ │ │ ├── blockquotes_with_code_blocks.html │ │ │ │ ├── blockquotes_with_code_blocks.text │ │ │ │ ├── code_blocks.html │ │ │ │ ├── code_blocks.text │ │ │ │ ├── code_spans.html │ │ │ │ ├── code_spans.text │ │ │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.html │ │ │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.text │ │ │ │ ├── horizontal_rules.html │ │ │ │ ├── horizontal_rules.text │ │ │ │ ├── inline_html_advanced.html │ │ │ │ ├── inline_html_advanced.text │ │ │ │ ├── inline_html_comments.html │ │ │ │ ├── inline_html_comments.text │ │ │ │ ├── inline_html_simple.html │ │ │ │ ├── inline_html_simple.text │ │ │ │ ├── links_inline_style.html │ │ │ │ ├── links_inline_style.text │ │ │ │ ├── links_reference_style.html │ │ │ │ ├── links_reference_style.text │ │ │ │ ├── links_shortcut_references.html │ │ │ │ ├── links_shortcut_references.text │ │ │ │ ├── literal_quotes_in_titles.html │ │ │ │ ├── literal_quotes_in_titles.text │ │ │ │ ├── markdown_documentation_basics.html │ │ │ │ ├── markdown_documentation_basics.text │ │ │ │ ├── markdown_documentation_syntax.html │ │ │ │ ├── markdown_documentation_syntax.text │ │ │ │ ├── nested_blockquotes.html │ │ │ │ ├── nested_blockquotes.text │ │ │ │ ├── ordered_and_unordered_lists.html │ │ │ │ ├── ordered_and_unordered_lists.text │ │ │ │ ├── strong_and_em_together.html │ │ │ │ ├── strong_and_em_together.text │ │ │ │ ├── tabs.html │ │ │ │ ├── tabs.text │ │ │ │ ├── tidyness.html │ │ │ │ └── tidyness.text │ │ │ └── tests/ │ │ │ ├── amps_and_angles_encoding.html │ │ │ ├── amps_and_angles_encoding.text │ │ │ ├── auto_links.html │ │ │ ├── auto_links.text │ │ │ ├── autolink_lines.html │ │ │ ├── autolink_lines.text │ │ │ ├── backslash_escapes.html │ │ │ ├── backslash_escapes.text │ │ │ ├── blockquote_list_item.html │ │ │ ├── blockquote_list_item.text │ │ │ ├── blockquotes_with_code_blocks.html │ │ │ ├── blockquotes_with_code_blocks.text │ │ │ ├── case_insensitive_refs.html │ │ │ ├── case_insensitive_refs.text │ │ │ ├── code_blocks.html │ │ │ ├── code_blocks.text │ │ │ ├── code_spans.html │ │ │ ├── code_spans.text │ │ │ ├── def_blocks.html │ │ │ ├── def_blocks.text │ │ │ ├── double_link.html │ │ │ ├── double_link.text │ │ │ ├── escaped_angles.html │ │ │ ├── escaped_angles.text │ │ │ ├── gfm_break.breaks.html │ │ │ ├── gfm_break.breaks.text │ │ │ ├── gfm_code.html │ │ │ ├── gfm_code.text │ │ │ ├── gfm_code_hr_list.html │ │ │ ├── gfm_code_hr_list.text │ │ │ ├── gfm_del.html │ │ │ ├── gfm_del.text │ │ │ ├── gfm_em.html │ │ │ ├── gfm_em.text │ │ │ ├── gfm_links.html │ │ │ ├── gfm_links.text │ │ │ ├── gfm_tables.html │ │ │ ├── gfm_tables.text │ │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.nogfm.html │ │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.nogfm.text │ │ │ ├── horizontal_rules.html │ │ │ ├── horizontal_rules.text │ │ │ ├── hr_list_break.html │ │ │ ├── hr_list_break.text │ │ │ ├── inline_html_advanced.html │ │ │ ├── inline_html_advanced.text │ │ │ ├── inline_html_comments.html │ │ │ ├── inline_html_comments.text │ │ │ ├── inline_html_simple.html │ │ │ ├── inline_html_simple.text │ │ │ ├── lazy_blockquotes.html │ │ │ ├── lazy_blockquotes.text │ │ │ ├── links_inline_style.html │ │ │ ├── links_inline_style.text │ │ │ ├── links_reference_style.html │ │ │ ├── links_reference_style.text │ │ │ ├── links_shortcut_references.html │ │ │ ├── links_shortcut_references.text │ │ │ ├── list_item_text.html │ │ │ ├── list_item_text.text │ │ │ ├── literal_quotes_in_titles.html │ │ │ ├── literal_quotes_in_titles.text │ │ │ ├── loose_lists.html │ │ │ ├── loose_lists.text │ │ │ ├── main.html │ │ │ ├── main.text │ │ │ ├── markdown_documentation_basics.html │ │ │ ├── markdown_documentation_basics.text │ │ │ ├── markdown_documentation_syntax.html │ │ │ ├── markdown_documentation_syntax.text │ │ │ ├── nested_blockquotes.html │ │ │ ├── nested_blockquotes.text │ │ │ ├── nested_code.html │ │ │ ├── nested_code.text │ │ │ ├── nested_em.html │ │ │ ├── nested_em.text │ │ │ ├── nested_square_link.html │ │ │ ├── nested_square_link.text │ │ │ ├── not_a_link.html │ │ │ ├── not_a_link.text │ │ │ ├── ordered_and_unordered_lists.html │ │ │ ├── ordered_and_unordered_lists.text │ │ │ ├── ref_paren.html │ │ │ ├── ref_paren.text │ │ │ ├── same_bullet.html │ │ │ ├── same_bullet.text │ │ │ ├── strong_and_em_together.html │ │ │ ├── strong_and_em_together.text │ │ │ ├── tabs.html │ │ │ ├── tabs.text │ │ │ ├── text.smartypants.html │ │ │ ├── text.smartypants.text │ │ │ ├── tidyness.html │ │ │ ├── tidyness.text │ │ │ ├── toplevel_paragraphs.gfm.html │ │ │ ├── toplevel_paragraphs.gfm.text │ │ │ ├── tricky_list.html │ │ │ └── tricky_list.text │ │ ├── css/ │ │ │ └── style.css │ │ ├── data/ │ │ │ └── front-end.json │ │ ├── index.html │ │ └── js/ │ │ └── app.js │ └── package.json ├── 06-WebFrontEndStack-master/ │ ├── .gitignore │ ├── LICENSE │ ├── README.es-es.md │ ├── README.md │ ├── README.zh-cn.md │ ├── index.js │ ├── package.json │ └── ux/ │ ├── WebFrontEndStack.htm │ ├── WebFrontEndStack.json │ └── dndTree.js ├── 07-Front-End-Develop-Guide-master/ │ ├── 2015letter.md │ ├── Articles.md │ ├── Featured-Articles.md │ ├── Featured.md │ ├── HelloWorld.swift │ ├── README.md │ ├── community.md │ ├── feedly.opml │ └── react/ │ └── React.md ├── 08-github-FE-project-master/ │ └── README.md ├── 09-front-end-collect-master/ │ ├── README.md │ ├── css/ │ │ └── style.css │ ├── data/ │ │ └── front-end.json │ ├── index.html │ ├── js/ │ │ └── app.js │ └── qq.md ├── 10-awesome-fe-team-master/ │ └── README.md ├── 11-about-reading/ │ └── Readme.md ├── 12-JavaScript-project/ │ └── README.md ├── 13-free-programming-books-zh_CN-master/ │ ├── .editorconfig │ ├── .travis.yml │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README.md │ ├── gulpfile.js │ └── what-non-programming-books-should-programmers-read.md ├── 14-frontend-dev-bookmarks-master/ │ ├── LICENSE │ ├── README.md │ └── about.md ├── 15-fun-front-end-tutorials-master/ │ └── README.md ├── 16-front-end-code-checklist-master/ │ ├── LICENSE │ └── README.md ├── 17-Mobile-front-end-tutorial-master/ │ └── README.md ├── 18-fun-front-end-tutorials-master/ │ └── README.md ├── 19-about-resume/ │ └── Readme.md ├── 20-about-front-end-interview/ │ └── Readme.md ├── 21-Front-end-Interview-questions/ │ ├── README.md │ ├── readme.html │ └── readme.js ├── 22-Front-end-Developer-Interview-Questions-master/ │ └── README.md ├── 23-FE-interview-master/ │ └── README.md ├── 24-100+Web-Development-Tools-and-Resources/ │ └── README.md ├── 25-web-develop-standard-master/ │ └── README.md ├── 26-front-end-learning-path/ │ └── README.MD ├── 27-front-end-style-guide/ │ └── README.md ├── 28-fetool-master/ │ └── README.md ├── 29-google-interview-university/ │ ├── LICENSE.txt │ ├── README-en.md │ ├── README.md │ └── programming-language-resources.md ├── 30-jstraining/ │ ├── README.md │ ├── demos/ │ │ ├── README.md │ │ ├── angular-demo/ │ │ │ └── index.html │ │ ├── backbone-demo/ │ │ │ ├── index.html │ │ │ └── js/ │ │ │ ├── backbone.js │ │ │ ├── jquery.js │ │ │ ├── main.js │ │ │ └── underscore.js │ │ ├── eslint-demo/ │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── express-demo/ │ │ │ ├── app1.js │ │ │ ├── app2.js │ │ │ ├── app3.js │ │ │ ├── app4.js │ │ │ └── package.json │ │ ├── jsx-demo/ │ │ │ ├── index.html │ │ │ ├── react-dom.js │ │ │ └── react.js │ │ ├── mobx-demo/ │ │ │ ├── .babelrc │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc │ │ │ ├── .gitignore │ │ │ ├── .jshintignore │ │ │ ├── .jshintrc │ │ │ ├── app/ │ │ │ │ ├── index.html │ │ │ │ ├── main.css │ │ │ │ ├── main.jsx │ │ │ │ └── store.js │ │ │ ├── package.json │ │ │ ├── webpack.config.js │ │ │ └── webpack.production.config.js │ │ ├── mocha-demo/ │ │ │ ├── add.js │ │ │ └── package.json │ │ ├── nightmare-demo/ │ │ │ ├── index.html │ │ │ ├── package.json │ │ │ ├── react-dom.js │ │ │ ├── react.js │ │ │ ├── server.js │ │ │ ├── taobao.test.js │ │ │ └── test.js │ │ ├── react-component-demo/ │ │ │ ├── index1.html │ │ │ ├── index2.html │ │ │ ├── index3.html │ │ │ ├── index4.html │ │ │ ├── react-dom.js │ │ │ └── react.js │ │ ├── react-lifecycle-demo/ │ │ │ ├── index.html │ │ │ ├── jquery.js │ │ │ ├── react-dom.js │ │ │ └── react.js │ │ ├── recharts-demo/ │ │ │ ├── index.html │ │ │ └── react-dom.js │ │ ├── redux-demo/ │ │ │ ├── .babelrc │ │ │ ├── .eslintignore │ │ │ ├── .eslintrc │ │ │ ├── .gitignore │ │ │ ├── .jshintignore │ │ │ ├── .jshintrc │ │ │ ├── app/ │ │ │ │ ├── App.js │ │ │ │ ├── index.html │ │ │ │ ├── main.css │ │ │ │ ├── main.jsx │ │ │ │ ├── myComponent.js │ │ │ │ └── reducer.js │ │ │ ├── package.json │ │ │ ├── webpack.config.js │ │ │ └── webpack.production.config.js │ │ ├── rest-api-demo/ │ │ │ ├── db.json │ │ │ └── package.json │ │ ├── simple-app-demo/ │ │ │ ├── app.js │ │ │ ├── bundle.js │ │ │ ├── index.html │ │ │ └── package.json │ │ └── vue-demo/ │ │ ├── app1.js │ │ ├── app2.js │ │ ├── app3.js │ │ ├── index1.html │ │ ├── index2.html │ │ └── index3.html │ └── docs/ │ ├── engineering.md │ ├── history.md │ ├── node.md │ ├── preparation.md │ └── react.md ├── 31-awesome-javascript-cn/ │ └── README.md └── Readme.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: 01-FE-learning-master/README.md ================================================ 转载请注明出处: [https://github.com/qiu-deqing/FE-learning](https://github.com/qiu-deqing/FE-learning) **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [FE-learning](#fe-learning) - [必备基础技能](#%E5%BF%85%E5%A4%87%E5%9F%BA%E7%A1%80%E6%8A%80%E8%83%BD) - [基本开发工具](#%E5%9F%BA%E6%9C%AC%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7) - [学习方法和学习目标](#%E5%AD%A6%E4%B9%A0%E6%96%B9%E6%B3%95%E5%92%8C%E5%AD%A6%E4%B9%A0%E7%9B%AE%E6%A0%87) - [入门之路](#%E5%85%A5%E9%97%A8%E4%B9%8B%E8%B7%AF) - [继续提高](#%E7%BB%A7%E7%BB%AD%E6%8F%90%E9%AB%98) - [一些个人经历](#%E4%B8%80%E4%BA%9B%E4%B8%AA%E4%BA%BA%E7%BB%8F%E5%8E%86) - [[LingyuCoder](https://github.com/LingyuCoder)的学习经历](#lingyucoderhttpsgithubcomlingyucoder%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%BB%8F%E5%8E%86) - [工具](#%E5%B7%A5%E5%85%B7) - [技能](#%E6%8A%80%E8%83%BD) - [语言基础](#%E8%AF%AD%E8%A8%80%E5%9F%BA%E7%A1%80) - [进阶](#%E8%BF%9B%E9%98%B6) - [项目](#%E9%A1%B9%E7%9B%AE) - [未来](#%E6%9C%AA%E6%9D%A5) - [其他](#%E5%85%B6%E4%BB%96) - [入门书](#%E5%85%A5%E9%97%A8%E4%B9%A6) - [一些不错的网站](#%E4%B8%80%E4%BA%9B%E4%B8%8D%E9%94%99%E7%9A%84%E7%BD%91%E7%AB%99) - [历程](#%E5%8E%86%E7%A8%8B) - [[MrRaindrop](https://github.com/MrRaindrop)的学习经历](#mrraindrophttpsgithubcommrraindrop%E7%9A%84%E5%AD%A6%E4%B9%A0%E7%BB%8F%E5%8E%86) - [缘起](#%E7%BC%98%E8%B5%B7) - [项目,下一个项目](#%E9%A1%B9%E7%9B%AE%EF%BC%8C%E4%B8%8B%E4%B8%80%E4%B8%AA%E9%A1%B9%E7%9B%AE) - [收集癖和知识管理](#%E6%94%B6%E9%9B%86%E7%99%96%E5%92%8C%E7%9F%A5%E8%AF%86%E7%AE%A1%E7%90%86) - [跟对神](#%E8%B7%9F%E5%AF%B9%E7%A5%9E) - [读书](#%E8%AF%BB%E4%B9%A6) - [前端的定位](#%E5%89%8D%E7%AB%AF%E7%9A%84%E5%AE%9A%E4%BD%8D) - [最后](#%E6%9C%80%E5%90%8E) - [byr论坛yiyizym的建议](#byr%E8%AE%BA%E5%9D%9Byiyizym%E7%9A%84%E5%BB%BA%E8%AE%AE) # FE-learning ![FE](./img/FE.jpg) 结合个人经历总结的前端入门方法,总结从零基础到具备前端基本技能的道路、学习方法、资料。由于能力有限,不能保证面面俱到,只是作为入门参考,面向初学者,让初学者少走弯路。 互联网的快速发展和激烈竞争,用户体验成为一个重要的关注点,导致专业前端工程师成为热门职业,各大公司对前端工程师的需求量都很大,要求也越来越高,优秀的前端工程师更是稀缺。个人感觉前端入门相对容易,但是也需要系统地认真学习,在打好基础后坚持学习,成为优秀前端工程师也只是时间问题。 学习任何知识最重要的都是**兴趣**,如果经过一段时间的学习感觉不喜欢,那可能强迫自己学习是很痛苦的,效果也不会好,毕竟这很可能就是以后很多年生存的技能。不过随着互联网行业的发展,前端必然是Web开发人员需要学习的知识,有时候是没有专业前端工程师一起合作的,所以即使不做专门的前端工程师,掌握基本的前端技能为工作带来方便。 后期邀请了一些同学分享学习经历。如果有同学愿意分享,欢迎push ## 必备基础技能 [前端技能汇总](https://github.com/JacksonTian/fks)这个项目详细记录 了前端工程师牵涉到的各方面知识。在具备基本技能之后可以在里面找到学习 的方向,完善技能和知识面。 [frontend-dev-bookmarks](https://github.com/dypsilon/frontend-dev-bookmarks)是老外总结的前端开发资源。覆盖面非常广。包括各种知识点、工具、技术,非常全面。 以下是个人觉得入门阶段应该熟练掌握的基础技能: - [HTML4](http://www.w3.org/TR/html401/cover.html#minitoc),[HTML5](http://www.w3.org/TR/html5/#contents)语法、标签、语义 - [CSS2.1](http://www.w3.org/TR/CSS2/#minitoc),[CSS3](http://www.w3.org/TR/2001/WD-css3-roadmap-20010523/#table)规范,与HTML结合实现各种布局、效果 - [Ecma-262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf)定义的javascript的语言核心,原生[客户端javascript](https://developer.mozilla.org/en-US/docs/Web/API),[DOM操作](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model),[HTML5新增功能](https://developer.mozilla.org/en/docs/web/Guide/HTML/HTML5) - 一个成熟的客户端javascript库,推荐[jquery](http://jquery.com/) - 一门服务器端语言:如果有服务器端开发经验,使用已经会的语言即可,如果没有服务器端开发经验,熟悉Java可以选择Servlet,不熟悉的可以选PHP,能实现简单登陆注册功能就足够支持前端开发了,后续可能需要继续学习,最基本要求是实现简单的功能模拟, - [HTTP](http://www.w3.org/Protocols/rfc2616/rfc2616.html) 在掌握以上基础技能之后,工作中遇到需要的技术也能快速学习。 ## 基本开发工具 恰当的工具能有效提高学习效率,将重点放在知识本身,在出现问题时能快速定位并 解决问题,以下是个人觉得必备的前端开发工具: - **文本编辑器**:推荐[Sublime Text](http://www.sublimetext.com/),支持各种插件、主题、设置,使用方便 - **浏览器**:推荐[Google Chrome](http://www.google.cn/chrome/?hl=zh-CN&standalone=1),更新快,对前端各种标准提供了非常好的支持 - **调试工具**:推荐Chrome自带的[Chrome develop tools](https://developer.chrome.com/devtools),可以轻松查看DOM结构、样式,通过控制台输出调试信息,调试javascript,查看网络等 - **辅助工具**:PhotoShop编辑图片、取色,fireworks量尺寸,AlloyDesigner对比尺寸,以及前面的到的Chrome develop tools, - **翻墙工具**:lantern, 壁虎漫步 ## 学习方法和学习目标 方法: 1. 入门阶段反复阅读**经典书籍的中文版**,书籍中的每一个例子都动手实现并在浏览器中查看效果 2. 在具备一定基础之后可以上网搜各种教程、demo,了解各种功能的实际用法和常见功能的实现方法 3. 阅读HTML,CSS,Javascript标准全面完善知识点 4. 阅读前端牛人的博客、文章提升对知识的理解 5. 善用搜索引擎 目标: 1. 熟记前面知识点部分的重要概念,结合学习经历得到自己的理解 2. 熟悉常见功能的实现方法,如常见CSS布局,Tab控件等。 ## 入门之路 以下是入门阶段不错的书籍和资料 1. HTML先看[《HTML & CSS: Design and Build Websites》][]1-9章,然后[《HTML5: The Missing Manual》][]1-4章。 2. CSS先看[《CSS: The Missing Manual》][],然后[《CSS权威指南》][] 3. javascript先看[《javascript高级程序设计》][],然后[《javascript权威指南》][] 4. HTTP看[HTTP权威指南][] 5. 在整个学习过程中HTML CSS JavaScript会有很多地方需要互相结合,实际工作中也是这样,一个简单的功能模块都需要三者结合才能实现。 6. 动手是学习的重要组成部分,书籍重点讲解知识点,例子可能不是很充足,这就需要利用搜索引擎寻找一些简单教程,照着教程实现功能。以下是一些比较好的教程网址 - 可以搜索各大公司前端校招笔试面试题作为练习题或者他人总结的[前端面试题][]还有[个人总结的面试题][](带参考答案) - 有各种各样的教程 - [MDN](https://developer.mozilla.org/en-US/docs/Web)也有很多教程,更重要的是里面有详细的文档,需要查找某个功能时在Google搜索:`xxx site:https://developer.mozilla.org` - 也有很多优质教程 - - 7. 原生javascript是需要重点掌握的技能,在掌握原生javascript的基础上推荐熟练掌握jQuery,在实际工作中用处很大,这方面的书籍有[《Learning jQuery》][]或者去[jQuery官网](http://learn.jquery.com/) 8. 建一个账号,保存平时学习中的各种代码和项目。 9. 有了一定基础之后可以搭建一个个人博客,记录学习过程中遇到的问题和解决方法,方便自己查阅也为其他人提供了帮助。也可以去或者这样的网站注册账号,方便实用 10. 经常实用Google搜索英文资料应该经常找到来自的高质量答案,与到问题可以直接在这里搜索,如果有精力,注册一个账号为别人解答问题也能极大提高个人能力。 11. 经典书籍熟读之后,可以打开前面必备基础技能部分的链接。认真读对应标准,全面掌握知识 [《HTML & CSS: Design and Build Websites》]: http://www.amazon.cn/gp/product/B00BMK4GKW/ref=s9_simh_gw_p14_d0_i2?pf_rd_m=A1AJ19PSB66TGU&pf_rd_s=center-2&pf_rd_r=1AH2NF64STS19GY8GR54&pf_rd_t=101&pf_rd_p=108773272&pf_rd_i=899254051 [《HTML5: The Missing Manual》]: http://www.amazon.cn/HTML5%E7%A7%98%E7%B1%8D-Matthew-MacDonald/dp/B009DFCZAQ/ref=sr_1_1?ie=UTF8&qid=1414740812&sr=8-1&keywords=html5+the+missing+manual [《CSS: The Missing Manual》]: http://www.amazon.cn/CSS-The-Missing-Manual-Mcfarland-David-Sawyer/dp/0596802447/ref=sr_1_1?ie=UTF8&qid=1414742710&sr=8-1&keywords=css+the+missing+manual+2 [《CSS权威指南》]: http://www.amazon.cn/CSS%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E8%BF%88%E8%80%B6/dp/B0011F5SIC/ref=sr_1_1?ie=UTF8&qid=1414744248&sr=8-1&keywords=css+%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97 [《javascript高级程序设计》]: http://www.amazon.cn/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E6%B3%BD%E5%8D%A1%E6%96%AF/dp/B007OQQVMY/ref=sr_1_1?s=books&ie=UTF8&qid=1414744358&sr=1-1&keywords=javascript+%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1 [《javascript权威指南》]: http://www.amazon.cn/O-Reilly%E7%B2%BE%E5%93%81%E5%9B%BE%E4%B9%A6%E7%B3%BB%E5%88%97-JavaScript%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E5%BC%97%E5%85%B0%E7%BA%B3%E6%A0%B9/dp/B007VISQ1Y/ref=sr_1_1?s=books&ie=UTF8&qid=1414744401&sr=1-1&keywords=javascript+%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97 [HTTP权威指南]: http://www.amazon.cn/HTTP%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E5%90%89%E5%B0%94%E5%88%A9/dp/B008XFDQ14/ref=sr_1_1?s=books&ie=UTF8&qid=1414744440&sr=1-1&keywords=HTTP+%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97 [前端面试题]: https://github.com/darcyclarke/Front-end-Developer-Interview-Questions/tree/master/Translations/Chinese [个人总结的面试题]: https://github.com/qiu-deqing/FE-interview [《Learning jQuery》]: http://www.amazon.com/Learning-jQuery-Fourth-Jonathan-Chaffer/dp/178216314X/ref=sr_1_1?s=books&ie=UTF8&qid=1410099243&sr=1-1&keywords=learning+jquery ## 继续提高 有了前面的基础之后,前端基本算是入门了,这时候可能每个人心中都有了一些学习方向,如果还是没有。 可以参考前面必备技能部分提到的那两个项目,从里面选一些进行发展学习。以下是一些不错的方面: - [Grunt](http://gruntjs.com/):前端自动化工具,提高工作效率 - [less css](http://lesscss.org/):优秀的CSS预处理器 - [bootstrap](http://getbootstrap.com/):优秀的CSS框架,对没有设计师的团队很不错,与less结合使用效果完美 - [requirejs](http://requirejs.org/):AMD规范的模块加载器,前端模块化趋势的必备工具 - [Node.js](http://nodejs.org/):JavaScript也可以做后台,前端工程师地位更上一步 - [AngularJS](https://angularjs.org/):做Single Page Application的好工具 - [移动端web开发](https://developer.mozilla.org/en-US/docs/Web/Guide/Mobile):智能手机的普及让移动端的流量正在逐步赶超PC端 - [Javascript内存管理](https://developer.chrome.com/devtools/docs/javascript-memory-profiling?hl=figoogle):SPA长期运行需要注意内存泄露的问题 - [High Performance JavaScript(Build Faster Web Application Interfaces)](http://www.amazon.com/Performance-JavaScript-Faster-Application-Interfaces/dp/059680279X/ref=sr_1_1?s=books&ie=UTF8&qid=undefined&sr=1-1&keywords=high+performance+javascript) - [Best Practices for Speeding Up Your Web Site](https://developer.yahoo.com/performance/rules.html):重要技能 ## 一些个人经历 ### [LingyuCoder](https://github.com/LingyuCoder)的学习经历 上面的大神都总结得差不多了,我这里就胡扯一些吧 ####工具 * chrome dev tools:前端开发调试利器,着重注意几个功能: - console(废话) - elements:元素样式调整,很常用 - sources:代码中添加断点,单步调试,以及单步调试过程中查看内存中的对象 + watch expression:通过表达式查看当前内存中的值 + call stack:查看调用栈,开启async,可以看异步调用栈(这个非常有用,尤其是ajax调试的时候) + scope variables:作用域链上的变量,非常有用 - network:抓包查看每个请求,非常重要,前后端联调必备 - timeline:分析渲染、js执行等等各个阶段,性能优化利器 - emulation:模拟移动端环境,mobile页面开发必备 - 一些插件: + liveload: 修改页面后自动刷新,不用按F5 + dimensions:直接在页面上测量的利器 + livestyle:css样式修改后自动起效果,不需要刷新,elements修改后也能同步到代码中 + image tool:测量,取色 + UC二维码:移动端调试扫码必备 + pagespeed,YSlow:页面性能分析和优化插件 + 马克飞象:优秀的在线markdown编辑器,快速写周报,做记录 * sublime text2:编码方便,插件多,速度快,性能好 - emmet:提升html编码速度必备 - sublimelinter + 各种语言的lint和hint:代码纠错 - 一些snippets:自动补全,提升开发效率 * Intellij IDEA和WebStorm:集成开发环境,集成了各种功能,开发比sublime要方便,但会比较吃性能 * Mark Men:测量、取色、标注利器,拿到视觉稿之后第一个打开的软件 * GFW Fucker:我用红杏,可以的话买个虚拟服务器当梯子 * iHosts:非常优秀的hosts管理软件,轻松修改hosts,开发调试必备 * Charles:Mac 平台最好用的抓包分析工具 * Rythem:AlloyTeam出品的代理抓包软件,非常轻量,安装简单,移动端(真机)开发调试很好用 * Wunderlist:一个非常不错的Todo List,任务、需求多的时候管理起来很方便 ####技能 前端的技能其实除了JavaScript(包括NodeJS)、HTML、CSS以外,还有很多。其实前端的技能树很大,这里只能列一些我开发中见到的说一说 #####语言基础 JavaScript: * 作用域链、闭包、运行时上下文、this * 原型链、继承 * NodeJS基础和常用API CSS: * 选择器 * 浏览器兼容性及常见的hack处理 * CSS布局的方式和原理(盒子模型、BFC、IFC等等) * CSS 3,如animation、gradient、等等 HTML: * 语义化标签 #####进阶 JavaScript: * 异步控制(Promise、ES6 generator、Async) * 模块化的开发方式(AMD、CMD、KMD等等) * JavaScript解释器的一些相关知识 - 异步IO实现 - 垃圾回收 - 事件队列 * 常用框架使用及其原理 - jQuery:基于选择器的框架,但个人认为不能叫框架,应该算工具库,因为不具备模块加载机制,其中源码很适合阅读钻研 - AngularJS/Avalon等MVVM框架:着重理解MVVM模式本身的理念和双向绑定的实现,如何解耦 - underscore:优秀的工具库,方便的理解常用工具代码片段的实现 - polymer/React: 组件化开发,面向未来,理解组件化开发的原理 CSS和HTML:主要是CSS3的特性和HTML5的特性,以及浏览器处理的流程和绘制原理 * DOM树、CSSOM树、渲染树的构建流程及页面渲染的过程 * 解析HTML、CSS、JavaScript时造成的阻塞 * HTML5相关 - SVG及矢量图原理 - Canvas开发及动画原理(帧动画) - Video和Audio * flex box布局方式 * icon fonts的使用 常用NodeJs的package: * koa * express * underscore * async * gulp * grunt * connect * request 一些理念: * 响应式Web * 优雅降级、渐进增强 * don`t make me think * 网页可用性、可访问性、其中的意义 * SEO搜索引擎优化,了解搜索引擎的原理 * SPA的好处和问题 性能优化: * 减少请求数量(sprite、combo) * 善用缓存(application cache、http缓存、CDN、localstorage、sessionstorage,备忘录模式) * 减少选择器消耗(从右到左),减少DOM操作(DOM和JavaScript解释器的分离) * CSS的回流与重绘 #####项目 * 版本管理:首推Git,用过Git都不会想用SVN了 - Git:本地版本管理的机制 - SVN:远程中心的版本管理机制 * 自动化构建:主要就是less、模板、coffee等的预处理以及对代码压缩和合并 - Gulp:基于流构建,速度快、模块质量好 - Grunt:独立任务构建,速度慢,配置蛋疼,灵活性高 * 预处理和模板引擎 - less:语法简单,但功能有限 - jade、ejs、velocity等模板引擎,各有各的长处 - coffee:python工程师最爱,我没用过 * 环境搭建:主要是将线上代码映射到本地,并在本地启动一个demo服务器,至于模拟数据的mock,见仁见智了 - 本地代理:ihosts * 自动化测试:在业务较为稳定的情况下,可以通过自动化测试来减少测试的事件,但需求较多的时候,维护测试用例的成本会很高,可能用自动化测试会起到反效果 - jasmine - mocha * 生态系统 - npm - bower - spm * 搭建一个属于自己的博客 - git pages - hexo - jekyll #####未来 * Web Componets:面向未来的组件化开发方式 - HTML模板 - Shadow DOM - Custom Elements - HTML Import * 移动端Native开发:这也是需要了解的,以后前端工程师会经常地和webview打交道,也要了解native开发 #####其他 有些东西不是考敲码就能弄好的,我参与实习的时候感受到了很多,这些是我遇到的也是我感觉自己做的不好的地方 * **对于业务的思考**:我个人这方面非常欠缺,所以放在最前面,在敲码前要多思考业务 * 交流和沟通能力:这个非常重要,前端同时需要与项目经理、产品、交互、后台打交道,沟通不善会导致很多无用功,延缓项目 * 知识管理、时间管理:input和output的平衡,output是最好的input。如何做好分享,参与社区,做好交流,作好记录 * 对新技术的渴望,以及敢于尝试 ####入门书 入门可以通过啃书,但书本上的东西很多都已经过时了,在啃书的同时,也要持续关注技术的新动态。这里推几本我觉着不错的书: * 《JavaScript高级编程》:可以作为入门书籍,但同时也是高级书籍,可以快速吸收基础,等到提升再回来重新看 * 《JavaScript权威指南》:不太适合入门,但是必备,不理解的地方就去查阅一下,很有帮助 * 《编写可维护的JavaScript》和: * 《Node.js开发指南》:不错的Nodejs入门书籍 * 《深入浅出Node.js》:Nodejs进阶书籍,必备 * 《JavaScript异步编程》:理解JS异步的编程理念 * 《JavaScript模式》和《JavaScript设计模式》:JavaScript的代码模式和设计模式,将开发思维转变到JavaScript,非常好的书 * 《JavaScript框架设计》:在用轮子同时,应当知道轮子是怎么转起来的,讲解很详细,从源码级别讲解框架的各个部分的实现,配合一个现有框架阅读,可以学到很多东西 * 《Don`t make me think》:网页设计的理念,了解用户行为,非常不错 * 《CSS禅意花园》:经久不衰的一部著作,同样传递了网页设计中的理念以及设计中需要注意的问题 * 《高性能JavaScript》和《高性能HTML5》:强调性能的书,其中不只是性能优化,还有很多原理层面的东西值得学习 * 《HTML5 Canvas核心技术》:我正在读的一本书,对于canvas的使用,动画的实现,以及动画框架的开发都非常有帮助 * 《HTTP权威指南》:HTTP协议相关必备,前端开发调试的时候也会经常涉及到其中的知识 * 《响应式Web设计》:技术本身不难,重要的是响应式网页的设计理念,以及移动先行的思想 * 《JavaScript语言精粹》:老道的书,也是普及JavaScript的开发思维的一本好书,非常适合入门 ####一些不错的网站 * [github](https://github.com):没啥好说的,多阅读别人的源码,多上传自己的源码,向世界各地的大牛学习 * [codepen](http://codepen.io/):感受前端之美的必选之地,里面有很多酷炫的效果和优秀的插件 * [echojs](http://www.echojs.com/):快速了解js新资讯的网站 * [stackoverflow](http://stackoverflow.com/)和[segmentfault](segmentfault.com):基本上各种问题都能在上面获得解答 * [google web fundamentals](https://developers.google.com/web/fundamentals/):每篇文章都适合仔细阅读 * [static files](http://www.staticfile.org/):开放的CDN,很好用 * [iconfont](http://www.iconfont.cn/):阿里的矢量图标库,非常不错,支持CDN而且支持项目 * [html5 rocks](http://www.html5rocks.com/): 一个不错的网站,很多浏览器的新特性以及前沿的技术,都能在这上面找到文章 * [css tricks](http://css-tricks.com/):如何活用CSS,以及了解CSS新特性,这里可以满足你 * [JavaScript 秘密花园](http://bonsaiden.github.io/JavaScript-Garden/zh/#object.general) JavaScript初学必看,非常不错 * [w3cplus](http://www.w3cplus.com/):一个前端学习的网站,里面的文章质量都挺不错的 * [node school](http://nodeschool.io/):一个不错的node学习网站 * [learn git branch](http://pcottle.github.io/learnGitBranching/?demo):一个git学习网站,交互很棒 * [前端乱炖](http://www.html-js.com/):一个前端文章分享的社区,有很多优秀文章 * [正则表达式](http://deerchao.net/tutorials/regex/regex.htm):一个正则表达式入门教程,非常值得一看 * [阮一峰的博客](http://www.ruanyifeng.com/blog/)和[张鑫旭的博客](http://www.zhangxinxu.com/wordpress/):快速了解某些知识的捷径,但是如果需要深挖,还需要其他的资源 * 各路大牛的博客:这个太多了,就不贴了,知乎上有很全的 * 各种规范的官方网站,不懂得时候读规范 ####历程 以前是做Java SSH的,半路出家做的前端,所以水平比较弱,遇到问题也比较多。基本上入门靠看书和[W3C School](http://www.w3school.com.cn/)上的教程,以及一些前端博客,如[汤姆大叔的博客](http://www.cnblogs.com/TomXu/)。以前也只是使用jQuery,原生js也没有太多的钻研,后来逐渐看了很多本动物书,比如老道的语言精粹等等。从这些书中学到了很多语言层面的知识。但这显然是不够的,所以我经常会去社区上看看大家在谈论什么,然后去看看相关的资料,感兴趣就会多找些资料看看,或者写一写demo。学CSS主要就是通过这种方式。后来开始更多的关注各路大牛的博客和一些比较深的书籍,以及关注一些新的知识和框架,并且不断地练手提交代码到github,这样也学到了很多知识。在实习的过程中,切身参与到实际项目开发之中,能学到很多在学校学不到的理念和思维,这点也有很大的帮助。不说了,我要去搬砖求offer了... ### [MrRaindrop](https://github.com/MrRaindrop)的学习经历 应[qiu神][1]的邀请分享一下前端学习经验,这里对前端知识体系架构就不做总结了,各位大神们的总结已经相当到位了,我就贡献几个个人认为还比较有用的链接大家研究研究就好,然后主要分享一下我在前端学习过程中遇到的问题和总结的经验教训吧,如果能帮到想要入门的FE初学者(我就姑且假定为本文的读者受众类型了),让他们少走点弯路,每走一步都知道自己下一步的方向,这是最好了。各位大神的总结和分享详见qiu神整理的[FE-learning][2]。 先说下,前端这个东西每个人都可以有适合自己的学习方法,这篇仅作参考,写的有点乱,各位凑合看。 #### 缘起 我是属于误打误撞进了前端,之前一直往做游戏的方向去来着,搞过游戏网站,玩过游戏引擎,比如unity,unreal这种商业引擎,捣鼓了几个游戏原型,不过自打研一进了实验室,直接就被导师派去写了js,导师给了我半个月时间让我写个基于百度地图api的数据展示页面,虽然这个时间还是相当宽裕的,不过之前没怎么写过js,也不会用地图api,于是我就一边啃着[《Javascript权威指南》][3](犀牛书)一边参考实验室前人留下的“代码”,总算是把功能都写出来了。那个页面算我的js入门作了,也是我前端学习路线的开始。 > 现在想来,虽然指派了去做前端,但是一直做下去并做好还是得靠兴趣维持,当然前端是一个趣味性十足的技术领域,而且社区每天都很“热闹”。 #### 项目,下一个项目 我个人认为前端的学习,初学阶段你可以完全脱离开书本,以**项目驱动**。虽然我个人是从犀牛书开始啃的,不过如果你没有充足的时间,或者觉得啃大部头乏而无味的话,还是别像我这样。当然了如果决定啃书最好是把书里的例子都跟着敲一遍的。我上研之前没接触过js,4月份还没开学呢就被直接被导师甩了个百度地图api的项目到脸上,接着就是各种ERP,地图数据展示,虽然换着花样来一点不重样,不过基本上都是前端的活,SSH和android开发也打过酱油,整个实验室就我一个人写前端敢信?富客户端SPA时代的后端就是一个restful接口,代码量基本都在前端啊,写的我一个人怎一个爽字了得...期间跟着导师感受了一把创业,每天从7点搞到晚上10点,也算是经历了一段快速成长期。 > 掌握一门技术先掌握它的大体框架,想一个能实现的点子,做一个能跑就行的demo,再去完善它的细节,等到demo完成了,对这门技术有了一个感性的认识,再去啃书,收获会大很多。我从开始原生js写到jquery,再到extjs,再到angularjs,从导师指定技术,到自己做技术选型,一个项目接着一个项目的练,就跟打怪升级似的。当然没有项目就去自己创造项目,动手实现自己的想法是件有乐趣和成就感的事。 #### 收集癖和知识管理 前端学习有个特点,很多东西都很零碎,分散,需要你自己去整理、归纳和总结。在微博、知乎上follow了众多的大神,你不仅仅是为了听八卦,大神们的只言片语有时候留下的是无尽的余味,很有可能一个不经意提到的一个词就成为你下一个学习的目标。**收集这些信息,善用google,提问,思考。**就像游戏里的收集要素,前端学习也是充满搜集要素的一个“游戏”,只不过你需要一个知识管理工具来充当物品栏和仓库,我所知道的大牛们无一不是知识管理工具的重度使用者。以前用的oneNote,那时候还没绑定到云存储,现在基本上用evernote,笔记已经累计到1200+篇。书签一直打算用delicious,因为它是基于tag管理的,但一直没用起来。当然重点不在于这些工具,但是趁手的工具可以提高你的学习效率。最关键当然是随时**保持旺盛的学习欲望**,你的目标是了解有关前端的一切(当然不是所有都要掌握,因为毕竟你的精力有限,而且现实的说这也不太可能)。 #### 跟对神 这个可控性貌似不大...跟对老大这个就不多说了,一定程度要看造化。不过话说回来,多跟身边的高手交流是王道,这个高手不一定要多高,但是一定要对技术有热情。研一的时候热情高涨,每天7点进实验室门,然后发现有个家伙居然比我还早到。后来发现这家伙上午就走了,下午又来了,而且导师对此习以为常,原来这家伙晚上不睡觉通宵写代码,上午才跑回去睡。后来经常和这位神讨论问题,每次感觉经验值蹭蹭蹭的往上涨。然后实验室还有一位神,被前面这位通宵神形容为“只能望其项背,一直在追赶,从来没赶上”,两位神的特点都是什么都了解一点,所以什么都能跟你讨论得起来,我有段时间做了个读书计划,从c/c++到vc/mfc再到unix网络编程,最后一路看到java核心技术和MSDN上的C#编程指南,和神们也能扯得很high了。 > 总之就是这两位神把我拉进了坑,或者说从一个坑跳进另一坑,虽然两位神都不是搞前端的,不过技术之间总有相通之处。 #### 读书 读书,多读书,读好书。在[刘未鹏的博客][4]里看到过一个公式,**你第一个月的工资等于之前买过(读过)的技术书价格总和**(这里说的技术书指那些经典的公认的好书)。讨论这个公式的正确性似乎没什么意义,然而它的合理性是毋庸置疑的,那就是多读经典技术书。最极端的一个例子,google的徐宥在[我的大学][5]里面说他扫荡了图书馆的整个TP312书架...对于前端的经典书籍,后面列了一个我收集的[前端书列][13](如果有遗漏的前端经典好书,还请留言告诉我),有条件可以尝试刷一遍这些书,我也是在找完整的时间去啃完它们。之前说的,前端知识点松散,收集零散的知识点,从博客里快速学习等,这些只是前端学习的一个方面,如果你要想深入理解一个知识体系,了解它的来龙去脉,对它建立系统认识,读经典书还是必不可少的。 我从最开始啃完犀牛书,然后接着去看了其他一些和前端干系不大的经典技术书,再后来通过实验室的项目和自己弄的一些小项目逐渐对前端领域比较上路以后,又看了《Javascript模式》、《Javascript设计模式》、《编写可维护的Javascript》,后来了解到node并开始用node搞点小玩意儿,又看了本《NodeJS up and run》和《Mongodb权威指南》,不过感觉前者略坑。那会儿朴灵那本深入浅出(晒书么么哒)还没出,后来出了就去图书馆借来看完,这么看下来感觉还不错,不过感觉看的还是偏少了,还需要继续刷(参照上面的书列)。 #### 前端的定位 前端的定位关乎到你需要吸收什么样的知识和技能,决定在技术世界里你对什么需要格外敏感。如果你认为前端仅仅停留在切页面,实现交互和视觉的要求,那你对前端的认识还停留在初级阶段。阿里终面的时候我问了考官这么个问题:前端技术日新月异,范围越扩越宽,标准越来越丰富,似乎任何一个触角都能伸出很远。怎么给前端一个合适的定位?考官给我分析了半天,然后总结成一句话,就是用户和网站的联结者,用户体验的创造者(原话不是这样,但大体是这个意思)。也就是说前端的终极目标其实就是创造用户体验,提升用户体验,**以用户体验为中心**。不管你是从交互设计上下手,还是从性能优化出发,或者改进工作流提升工作流效率,最终都是为了创造和提升用户体验,最终都要体现到用户体验这一点上来。我认为这个总结非常有道理(当然“用户体验”这个词太宽泛了,并且不仅仅是前端工程师的范畴,比如开发后台的时候对一个数据处理过程进行优化,提升了整体性能,这也是对用户体验的一个提升)。 现在的前端工程师做到一定阶段不可避免会接触到很多比切页面、实现视觉要求、实现交互等更深入的问题,比如前端自动化、图像编程、性能优化等等,再往后推一点就是PHP/JSP/ASP/nodeJs,过去后端模板一般属于后端的范畴,现在随着[前端架构的演进][6],可能会让你去写后端模板的代码,需要用到后端语言(PHP/Java/C#等),这就是所谓**大前端**(然而这与前端的定位并不是相背离的,大前端处理的依然是与用户接触的部分,仍然是对用户体验的优化)。可能最常见或者被谈论最多的就是node,其实这几种技术选型都可以,bat三家据说百度用PHP比较多,阿里用node比较多。 玉伯在[他的博客][7]里提过所谓全端是横向的,全栈是纵向的。**全端**即所有的终端说白了都是前端,因为都关乎到用户体验,直接和用户接触。适应多终端的开发,要求你在web前端的基础上,可能还要去扩展android开发和ios开发的知识,好在由于hybrid开发方式的流行,对使用native语言开发的技能会要求的不那么深入。 **全栈**可以说是最适合初创公司的一种发展类型,广义上认为是从前端干到后端,从开发干到运维,这种就不说了,一般人应该不会想要去往这个方向发展,想要成为这种意义上的full-stack dev的,可能用不着来看我这篇文章了;而狭义上的全栈特指使用js语言从前端写到架设在nodeJs上的后端,前后端统一语言,统一编程模型,甚至公用同一套代码。更多了解全栈开发可以看看玉伯这篇[说说全栈工程师][8]。 以上是我对前端以及衍生出来的技术路线的一些浅薄理解,学习一个领域掌握它的整体上的走向和趋势还是挺重要的。另外如果想要对前端学习方向、职业成长路径有一个整体的认识,推荐看看拔赤总结的这篇[前端开发十日谈][9]。 #### 最后 贡献几个对前端学习、面试有帮助的链接: * [前端面试问题合集(Front-end-Developer-Interview-Questions)][10] * [前端技能汇总(JacksonTian)][11] * [另一张前端技能汇总图][12] * [前端那点事儿(书列)][13] [1]: https://github.com/qiu-deqing [2]: https://github.com/qiu-deqing/FE-learning [3]: http://book.douban.com/subject/2228378/ [4]: http://mindhacks.cn/topics/learning-method/ [5]: http://blog.youxu.info/2009/08/06/my-undergraduate-year/ [6]: https://github.com/lifesinger/lifesinger.github.com/issues/184 [7]: https://github.com/lifesinger/lifesinger.github.com/labels/blog [8]: https://github.com/lifesinger/lifesinger.github.com/issues/185 [9]: https://github.com/jayli/jayli.github.com/issues/1 [10]: https://github.com/darcyclarke/Front-end-Developer-Interview-Questions [11]: https://github.com/JacksonTian/fks [12]: http://www.f2er.info/ [13]: http://book.douban.com/doulist/13701898/ ### byr论坛yiyizym的建议 与grunt相比,学习gulp会比较简单 做SPA的话,推荐backbone.js和 backbone.marionette.js 翻墙不用折腾,花十块钱买一个月的 红杏。 把基础打扎实了再学这些都没问题。 html 没什么好说的,有空学学html5。 css 尽量看[文档](http://www.w3.org/TR/CSS21/cover.html#minitoc) ,因为很多中文资料都各执一辞,看多了反而会糊涂。 有个[网站](http://caniuse.com/)可以查找html/css标签、属性在各个浏览器中的支持情况,挺好用的。 javascript 就看 javascript高级程序设计 。不过这么厚的书看过就会忘。对javascript核心概念的讲解:对象/原型链/ 构造函数/执行上下文/作用域链/闭包/this,[这里](http://weizhifeng.net/javascript-the-core.html)有篇不错的文章。 有闲情可以看看 ecmascript 6,计划明年6月就发布啦。[阮一峰的网站](http://es6.ruanyifeng.com/)有入门资料。 jquery 有很多 API,[这个网站](http://www.css88.com/jqapi-1.9/)可以方便查到。有时间弄清楚jquery deferred 的用法。 多给 sublimetext 装插件,比如说检查代码错误的,新建目录文件的,整理代码的。 ================================================ FILE: 02-fedHandlebook-master/README.md ================================================ ###前端开发者手册 这是任何人都可以用来学习前端的实践手册, 它概述并讨论了前端工程的实践: 该如何学习以及实践时该使用什么工具. 撰写该手册的目的有两个: 一是为潜在以及正在实践的前端开发人员提供一个包括学习资料和开发工具的专业资源; 二是该手册可以被管理者, CTO, 讲师和猎头用来作为洞察前端开发的实践. 该手册的内容支持Web技术(HTML, CSS, DOM, 和 JavaScript), 并且手册提供的解决方案都直接建立在这些开放的技术之上. 手册中所引用的素材和讨论都是最好的或者当前前端开发者们需要面对的问题. 该手册不应该被视为一个前端开发者对所有可用资源的综合大纲, 其价值在于简洁, 专注和及时管理足够的分类信息, 不致于任何人沉浸在任何一个特定的主题. 该手册会每年发布一个更新内容. 该手册分为三个部分: ####第一部分:前端开发实践 第一部分会大致描述前端工程的实践 ####第二部分:学习前端开发 第二部分为成为一个前端开发人员确定了自主学习的直接资源 ####第三部分:前端开发工具 第三部分会简单地讨论一些前端开发工具的使用 --- 在线阅读: [前端开发者手册](https://dwqs.gitbooks.io/frontenddevhandbook/content/) 资源下载: pdf  mobi Issues/Suggestions/Fixes: [Front-end Developer Handbook](https://github.com/dwqs/fedHandlebook) **说明**: 该手册参考 [Front-end Developer Handbook](http://www.frontendhandbook.com/index.html) 电子书所译, 不当之处, 欢迎 [pr](https://github.com/dwqs/fedHandlebook) 或提出 [issue](https://github.com/dwqs/fedHandlebook). 联系译者: * 博客: Blog * 微博: 会飞的Pikachu * QQ交流群: [![259280570](http://pub.idqqimg.com/wpa/images/group.png)](http://shang.qq.com/wpa/qunwpa?idkey=457dead858ccbda1b670f91229e334695619cb9f891b433301ac3dd780d1ecaa) Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License ================================================ FILE: 02-fedHandlebook-master/SUMMARY.md ================================================ # Summary * [前端开发者手册](README.md) * [什么是前端开发者?](what-is-a-fd.md) * [第一部分: 前端开发实践](practice.md) * [前端的工作职称](practice/front-end-jobs-titles.md) * [常用的网络技术](practice/tech-employed-by-fd.md) * [前端开发技术栈](practice/front-end-skills.md) * [前端开发做什么](practice/fd-dev-for.md) * [团队中的前端](practice/front-end-team-role.md) * [全才神话](practice/full-stack.md) * [前端的面试问题](practice/front-end-interview.md) * [前端工作版块](practice/front-end-jobs.md) * [前端薪资](practice/salaries.md) * [如何成为前端开发者?](practice/making-fd.md) * [第二部分: 前端开发学习](learning.md) * [自主学习](learning/self-direct-learning.md) * [Internet/Web](learning/internet.md) * [Web浏览器](learning/browsers.md) * [DNS](learning/dns.md) * [HTTP/网络](learning/networks.md) * [Web 主机](learning/web-hosting.md) * [前端开发综合学习](learning/general-front-end.md) * [用户界面和交互设计](learning/design.md) * [HTML & CSS](learning/html-css.md) * [SEO](learning/seo.md) * [JavaScript](learning/js.md) * [Web 动画](learning/animation.md) * [DOM, BOM & JQuery](learning/dom.md) * [Web 字体](learning/fonts.md) * [无障碍设计](learning/accessibility.md) * [Web/浏览器 API](learning/api.md) * [JSON](learning/json.md) * [静态网页生成器](learning/static.md) * [前端应用架构设计](learning/front-end-apps.md) * [接口/API 设计](learning/interface.md) * [Web 开发者工具](learning/dev-tools.md) * [命令行](learning/cli.md) * [Node.js](learning/node.md) * [React.js](learning/react.md) * [模块加载器](learning/module.md) * [包管理器](learning/package.md) * [版本控制](learning/version.md) * [构建 & 任务自动化](learning/build.md) * [网站性能优化](learning/optimizing.md) * [JS 测试](learning/test.md) * [无壳浏览器](learning/headless-browsers.md) * [离线开发](learning/offline.md) * [安全](learning/security.md) * [多平台开发](learning/multi-things-dev.md) * [指导学习](learning/direct-learning.md) * [前端指导学习](learning/courses.md) * [前端开发者从哪里学](learning/learn-from.md) * [前端简报, 资讯 & 博客](learning/news-podcasts.md) * [第三部分: 前端开发工具](tools.md) * [常用前端开发工具](tools/general-tools.md) * [DOC/API 浏览](tools/browserdocs.md) * [SEO](tools/seo.md) * [原型和框架](tools/proto.md) * [图表](tools/diagram.md) * [HTTP/网络](tools/http.md) * [代码编辑](tools/code-editor.md) * [浏览器](tools/browser.md) * [HTML](tools/html.md) * [CSS](tools/css.md) * [DOM](tools/dom.md) * [JavaScript](tools/js.md) * [静态网页生成器](tools/static.md) * [APP(桌面, 移动, 平板等) 管理](tools/app.md) * [脚手架](tools/scaffolding.md) * [模板](tools/templates.md) * [UI 部件 & 组件](tools/ui.md) * [数据可视化](tools/charts.md) * [图形](tools/graphics.md) * [动画](tools/animation.md) * [JSON](tools/json.md) * [测试框架](tools/test.md) * [数据存储](tools/data.md) * [模块/包加载](tools/loader.md) * [模块/包仓库](tools/repo.md) * [Web/云/静态主机托管](tools/hosting.md) * [项目管理 & 代码托管](tools/project.md) * [合作 & 交流](tools/coll.md) * [CMS 托管/API](tools/cms.md) * [BASS](tools/bass.md) * [离线](tools/offline.md) * [安全](tools/security.md) * [任务管理](tools/task.md) * [部署](tools/deploy.md) * [网站/APP 监控](tools/monitor.md) * [JS 错误监控](tools/error.md) * [性能](tools/perf.md) * [SVG](tools/svg.md) ================================================ FILE: 02-fedHandlebook-master/learning/accessibility.md ================================================ ###无障碍设计 >无障碍设计是指产品, 设备, 服务, 或者环境是为残疾人设计的. 无障碍设计的概念意味着与一个人的辅助技术(例如, 电脑屏幕阅读器)相兼容, 确保直接访问(即独立)和"间接访问". >无障碍设计可以理解为 "能够访问", 并对一个系统或实体是有利的, 其侧重于使身体残障, 或有特殊需要, 或要依赖辅助技术的人群能够访问 Web. 然后, 研究和开发无障碍设计对每个人都带来了好处. >无障碍设计不应该和可用性混淆. 大多数情况下, 可用性是指产品(如: 设备, 服务, 或者环境)能在特定的环境下被特定的用户使用, 来高效地实现制定目标. >无障碍设计和通用性设计是息息相关的. 通用型设计是指产品的创造过程中, 产品对人们是可用的, 并尽可能最大范围覆盖各能力范围内的人群和各种情形下的操作, 即对所有人是可访问的(无论他们访问 Web 是否有障碍). - wikipedia **综合学习:** * [Web应用程序通用设计](http://www.amazon.cn/Web%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E9%80%9A%E7%94%A8%E8%AE%BE%E8%AE%A1-Wensy-Cbisbolm/dp/B002IIDICE/ref=sr_1_2?ie=UTF8&qid=1446345863&sr=8-2) [read][RMB] * [Web 无障碍设计入门](http://www.pluralsight.com/courses/web-accessibility-getting-started) [watch][$] * [Web 无障碍设计介绍](https://www.w3.org/WAI/intro/accessibility.php) [read] * [兼济天下·用户无障碍体验的可访问性设计](http://www.amazon.cn/UI-UE%E7%B3%BB%E5%88%97%E4%B8%9B%E4%B9%A6-%E5%85%BC%E6%B5%8E%E5%A4%A9%E4%B8%8B%C2%B7%E7%94%A8%E6%88%B7%E6%97%A0%E9%9A%9C%E7%A2%8D%E4%BD%93%E9%AA%8C%E7%9A%84%E5%8F%AF%E8%AE%BF%E9%97%AE%E6%80%A7%E8%AE%BE%E8%AE%A1-%E9%9C%8D%E5%B0%94%E9%A1%BF/dp/B00UT03YEU/ref=sr_1_1?ie=UTF8&qid=1446346063&sr=8-1) [read][RMB] * [UX 基础: 无障碍设计](http://www.lynda.com/Accessibility-tutorials/Foundations-UX-Accessibility/435008-2.html) [watch][$] * [Web 无障碍设计介绍](https://webaccessibility.withgoogle.com/course) [watch] **标准/规范:** * [无障碍设计网络倡议 (WAI)](http://www.w3.org/WAI/) * [网页内容无障碍设计指南 (WCAG) 的目前状态](http://www.w3.org/standards/techs/wcag#w3c_all) * [富 Internet 应用程序的无障碍设计 (WAI-ARIA) 的目前状态](http://www.w3.org/standards/techs/aria#w3c_all) ================================================ FILE: 02-fedHandlebook-master/learning/animation.md ================================================ ###Web 动画 **综合学习:** * [Web 动画的历史](https://www.codeschool.com/courses/adventures-in-web-animations) [watch][$] * [Snap.svg 动画](https://webdesign.tutsplus.com/courses/animating-with-snapsvg) [watch][$] * [CSS3 和 HTML5 动画](https://frontendmasters.com/courses/animation-storytelling-html5-css3/) [watch][$] * [真实世界中的 CSS 动画](https://webdesign.tutsplus.com/courses/css-animation-in-the-real-world) [watch][$] * [HTML5+JavaScript 动画基础](http://www.amazon.cn/HTML5-JavaScript%E5%8A%A8%E7%94%BB%E5%9F%BA%E7%A1%80-%E5%85%B0%E8%B4%9D%E5%A1%94/dp/B00D69IJKA/ref=sr_1_2?ie=UTF8&qid=1446346650&sr=8-2) [read][RMB] * [Web Animation using JavaScript: Develop & Design (Develop and Design)](http://www.amazon.com/Web-Animation-using-JavaScript-Develop-ebook/dp/B00UNKXVDU/ref=sr_1_1) [read][$] * [2014: 动画发展现状](http://www.smashingmagazine.com/2014/11/the-state-of-animation-2014/) [read] * [学会用 CSS 创建动画](http://www.kirupa.com/css_animations/index.htm) [read & watch] * [学会用 JavaScript 创建动画](http://www.kirupa.com/javascript_animations/index.htm) [read & watch] **标准/规范** * [Web 动画](https://w3c.github.io/web-animations/) **译者补充:** * [Pro CSS3 Animation](http://apress.jensimmons.com/v5/pro-css3-animation/ch2.html) ================================================ FILE: 02-fedHandlebook-master/learning/api.md ================================================ ###Web/浏览器 API BOM 和 DOM 并不是唯一的浏览器 API, 在浏览器内部的 Web 平台上, 它们是可用的. DOM 和 BOM 并不是一切, 但是一个用于浏览器编程的接口可以被认识一个 Web 或者 浏览器 API(悲剧的是, 这些 API 曾被称为 HTML 5 API, 这会和 HTML 5 自身的规范/标准混淆, 因为 HTML 5 规范特指 HTML 5 标记语言). Web 或浏览器 API 也会包括访问设备的 API(如: [`Navigator.getBattery()`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery)), 通过平板和手机设备上的浏览器可以利用这些 API. ![api](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/web-api.png) 在适当情况下, 你应该了解和学习 Web/浏览器 API. 为了熟悉这些 API, 应该去研究[HTML5test.com](https://html5test.com/compare/browser/chrome-44/firefox-40/ie-11/safari-9.0.html) 对与 5 款常用浏览器对 API 新特性的支持结果, 这是一个很不错的方式. **学习文档:** * [DIVE INTO HTML5](http://diveintohtml5.info/) [read] * [HTML5 专业开发](http://apress.jensimmons.com/v5/pro-html5-programming/info.html) [read] * [HTML5 Canvas](http://chimera.labs.oreilly.com/books/1234000001654/index.html) [read] **学习视频:** * [Web Audio 的乐趣](https://code.tutsplus.com/courses/fun-with-web-audio/) [watch] * [用 Web Audio 给网站添加声音](https://code.tutsplus.com/courses/add-sound-to-your-site-with-web-audio) [watch] **其它学习资源:** * [Web Audio API](http://chimera.labs.oreilly.com/books/1234000001552/index.html) [read] 此外, MDN 有很多关于 web/browser API 的信息: * [MDN Web API 参考](https://developer.mozilla.org/en-US/docs/Web/Reference/API) * [MDN WebAPI - 设备访问 API 和其它有用的 API](https://developer.mozilla.org/en-US/docs/WebAPI) * [MDN Web APIs 接口参考](https://developer.mozilla.org/en-US/docs/Web/API) 时刻记住, 并不是每一个 API 都在 W3C 或 WHATWG 的规范之内. 除了 MDN, 还列出了一些有用的资源: * [HTMl5-overview](https://github.com/dret/HTML5-overview) * [platform.html5.org](https://platform.html5.org/) * [HTML5 和 JavaScript API 索引](http://html5index.org/) ================================================ FILE: 02-fedHandlebook-master/learning/browsers.md ================================================ ###Web浏览器 >web 浏览器(通常被称为浏览器)是一个用于检索、展示和遍历在万维网上的信息资源的软件应用程序. 信息资源被定义成统一资源定位符(URI/URL). 它可能是网页, 图片, 视频或者一个内容片断. 超链接的出现使用户能轻松的将浏览器导航到相关的资源, 尽管浏览器主要是为了使用万维网, 但它们还可以用来访问 Web服务器在私人网络所提供的信息或文件在文件系统. - Wikipedia [主流的浏览器](http://www.sitepoint.com/browser-trends-april-2015-statcounter-vs-netmarketshare/) 如下: 1. [Chrome](http://www.google.com/chrome/) (引擎: [Blink](https://en.wikipedia.org/wiki/Blink_%28layout_engine%29) + [V8](https://en.wikipedia.org/wiki/V8_%28JavaScript_engine%29)) 2. [Firefox](https://www.mozilla.org/en-US/firefox/new/) (引擎: [Gecko](https://en.wikipedia.org/wiki/Gecko_%28software%29) + [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey_%28software%29)) 3. [Internet Exploere](http://windows.microsoft.com/en-us/internet-explorer/download-ie) (引擎: [Trident](https://en.wikipedia.org/wiki/Trident_%28layout_engine%29) + [Chakra](https://en.wikipedia.org/wiki/Chakra_%28JScript_engine%29)) 4. [Safari](https://www.apple.com/safari/) (引擎: [Webkit](https://en.wikipedia.org/wiki/WebKit) + [SquirrelFish](https://trac.webkit.org/wiki/SquirrelFish)) ![statcounter](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/statcounter.png) **浏览器和web技术的演变(API 等):** * [www.evolutionoftheweb.com](http://www.evolutionoftheweb.com/) [read] * [Timeline of web browsers](https://en.wikipedia.org/wiki/Timeline_of_web_browsers) [read] **最常用的无壳浏览器:** * [PhantomJS](http://phantomjs.org/) (引擎: [Webkit](https://en.wikipedia.org/wiki/WebKit) + [SquirrelFish](https://trac.webkit.org/wiki/SquirrelFish)) * [slimerjs](http://slimerjs.org/) (引擎: [Gecko](https://en.wikipedia.org/wiki/Gecko_%28software%29) + [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey_%28software%29)) * [TrifleJS](http://triflejs.org/) (引擎: [Trident](https://en.wikipedia.org/wiki/Trident_%28layout_engine%29) + [Chakra](https://en.wikipedia.org/wiki/Chakra_%28JScript_engine%29)) **浏览器怎么工作的** * [我所知道的关于浏览器和Web的20件事](http://www.20thingsilearned.com/en-US/foreword/1) [read] * [浏览器如何工作的: 现代浏览器背后的秘密](http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/) [read] * [快速 CSS: 浏览器是怎么组织网页的](http://dbaron.org/talks/2012-03-11-sxsw/master.xhtml) [read] * [浏览器是如何渲染一个网站的?](https://www.youtube.com/watch?v=SmE4OwHztCc) [watch] ![browser-work](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/browsers-work.png) **浏览器优化** * [网站性能优化](https://www.udacity.com/course/website-performance-optimization--ud884) [watch] * [浏览器渲染优化](https://www.udacity.com/course/browser-rendering-optimization--ud860) [watch] **浏览器安全** * [浏览器安全手册](https://code.google.com/p/browsersec/wiki/Main) [read] * [HTML5 安全参考手册](https://html5sec.org/#javascript) [read] * [前端安全](https://mikewest.org/2013/09/frontend-security-frontendconf-2013) [watch] * [Web 安全: JavaScript, HTML, CSS 的使用](http://www.amazon.com/Security-Web-Developers-Using-JavaScript/dp/1491928646/) [read][$] * [网络混战: 现代 Web 应用安全指南](http://lcamtuf.coredump.cx/tangled/) [read] **浏览器比较** * [Web 浏览器的比较](https://en.wikipedia.org/wiki/Comparison_of_web_browsers) [read] **浏览器的发展** 在过去, 前端开发者会花费大量的时间让代码在不同的浏览器中正常工作. 除非你必须写出兼容老版本浏览器的代码(如: IE6/IE7), 否则跟现在比起来, 这(浏览器兼容)在以前是一个很大的问题. 虽说浏览器兼容问题现在仍然存在, 但前端开发者并不用花费很多时间就能处理这类问题. 而实际上, 现代抽离出的框架(如: JQuery, pre-processors, transpilers)已经废除了很多浏览器不一致问题. **绿色浏览器** 浏览器的最新版本被认为是绿色浏览器, 也就是说, 浏览器会自动更新而不用去提示用户更新. 浏览器的自动更新摒弃了老版本浏览器进程缓慢的问题, 因为对于老版本浏览器和现代浏览器之间的共性的差异化开发是很复杂的(如: 新规范和更新速度). **浏览器选择** 现在大多数前端开发者使用 Chrome, "Chrome 开发工具"对开发者很有用, 然而, 所有浏览器都提供了开发者工具, 所以选择一个开发用的浏览器是一个主观的问题. 更重要的问题是要了解需要支持哪些浏览器, 当你在开发的时候, 要在每个浏览器中做测试, 但无论选择哪一款浏览器都能完成开发任务, 我建议使用 Chrome 是因为 Chrome 开发工具一直在改进, 并且包含了更健全地特性. **浏览器 Hacks** * [browserhacks.com](http://browserhacks.com/) [read] ================================================ FILE: 02-fedHandlebook-master/learning/build.md ================================================ ###构建和任务自动化 >构建自动化是软件构建和相关流程的自动化过程, 包括: 将计算机源码编译成二进制代码, 打包二进制代码和运行自动化测试. - wikipedia **综合学习:** * [用 Gulp.js 进行 JavaScript 自动构建: ](http://www.pluralsight.com/courses/javascript-build-automation-gulpjs) [watch][$] * [Gulp 入门](https://www.packtpub.com/web-development/getting-started-gulp) [read][$] * [Gulp 快速入门](https://www.packtpub.com/web-development/rapid-gulp-video) [watch][$] * [学习 Gulp - 前端工厂入门](http://hmphry.com/gulp) [read] * [Gulp 基础](http://teamtreehouse.com/library/gulp-basics) [watch][$] * [使用 npm 作为任务运行器](http://teamtreehouse.com/library/using-npm-as-a-task-runner) [watch][$] **参考/文档:** * [gulp](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md) Gulp 是非常棒的构建工具, 然后, 你可能仅仅只需要 `npm run`. 在你的应用程序栈变得负责之前, 问问你自己, `npm run`是否能完成自动化构建. 如果你需要更多, 可以同时使用 `npm run`和 Gulp. 推荐阅读: * [NPM 使用指南](http://www.sitepoint.com/guide-to-npm-as-a-build-tool/) * [NPM 的任务自动化管理](http://substack.net/task_automation_with_npm_run) * [构建工具 VS NPM 脚本: 为何不使用两者](http://engineering.hobsons.com/2015/06/26/build-tools-vs-npm-scripts-why-not-both/) * [使用 NPM 作为下一个项目的构建系统](https://drublic.de/blog/npm-builds) * [怎么将 NPM 作为构建工具使用](http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/) **译者补充:** * [Gulp不完全入门教程](http://www.ido321.com/1622.html) ================================================ FILE: 02-fedHandlebook-master/learning/cli.md ================================================ ###命令行 >命令行接口或命令语言解译器(CLI), 也称命令行用户界面, 控制台用户界面和字符用户界面(CUI), 是一种用户以连续的文本(命令行)的形式向程序提出需求, 与计算机程序交互的方式. - wikipedia **入门学习:** * [codecademy: Learn the Command Line](https://www.codecademy.com/courses/learn-the-command-line) [watch] * [The Command Line Crash Course](http://cli.learncodethehardway.org/book/) [read] * [Meet the Command Line](http://www.pluralsight.com/courses/meet-command-line) [read][$] * [Learn Enough Command Line to Be Dangerous](http://www.learnenough.com/command-line-tutorial) [read][free tp $] * [Command Line Power User](http://commandlinepoweruser.com/) [watch] **进阶学习:** * [Advanced Command Line Techniques](https://code.tutsplus.com/courses/advanced-command-line-techniques) [watch][$] ================================================ FILE: 02-fedHandlebook-master/learning/courses.md ================================================ ###Directed learning The table below contains instructor led, paid, front-end courses, programs, schools, and bootcamps. If you can't afford a directed education, a self directed education using screencasts, books, and articles is a viable alternative to learn front-end development for the self-driven individual.
company course price on site remote
Iron Yard Front End Engineering 12,000 multiple locations
Udacity Front-End Web Developer Nanodegree 200 monthly multiple locations yes
The New York Code + Design Academy Front End 101 2,000 New York, NY
Portland Code School Advanced Front-end Developer Tools 2,000 Portland, OR
BLOC Become a Frontend Developer 4,999 yes
Thinkful Frontend Web Development 300 per month yes
General Assembly Frontend Web Development 3,500 multiple locations
Hackbright Academy Front-End Web Development 3,000 San Francisco, CA
HackerYou Front-end Web Development Immersive 7,000 - 7,910 Toronto, Canada
The Flatiron School Introduction to Front-End Web Development 3,500 New York, NY
Austin Coding Academy The Front End Track 1,490 per class Austin, TX
Codeup Night Front-End Bootcamp 8,500 San Antonio, Texas
Betamore Front-end Web Development 8,500 Baltimore, MD
Codify Academy Front-end Web Development 4,000 multiple locations
DecodeMTL Learn Front-end Web Development 2,500 Montreal, QC
gr8code Front-End Bootcamp 10,000 Tampa, FL
LearningFuze Part-Time Front-End Development Course 2,500 Irvine, CA
================================================ FILE: 02-fedHandlebook-master/learning/design.md ================================================ ###用户界面和交互设计 >**用户界面设计:** 用户界面设计(UI)或用户界面工程师是为机器或者软件做用户界面设计的, 如: 计算机, 家用器具, 移动设备和其它电子设备, 专注于最大限度地提高用户体验. 用户界面设计的目标是尽可能是使用户交互变得简单有效, 实现用户的操作目标(设计是以用户为中心的). - wikipedia >**交互设计模式:** 设计模式是一种记录解决常见设计问题解决方案的形式化方式. 建筑师 Christopher Alexander 在城市规划和建设体系结构中已经介绍了这种方式, 并已用于其他学科, 包括教学, 教育学和软件架构和设计. - wikipedia >**用户体验设计:** 用户体验设计(又称 UXD 或 UED 或 XD), 是通过提高可用性、可访问性以及在用户跟产品交互时的愉悦来提高用户体验的过程. 用户体验设计从完成传统的人机交互(HCI), 已经扩展到要处理产品或服务中能被用户感知的所有方面. - wikipedia >**人机交互:** 人机交互(HCI)不仅特别关注人和计算之间的界面, 也会研究设计和使用 Web 技术. 人机交互领域的研究人员都会去关注当前人类与计算机交互的方式和为人类与计算机提供新的交互方式的设计技术. - wikipedia 若你想要建立适当的用户界面, 我会建议你阅读以下关于设计方面的书籍: * [Designing Interfaces](http://www.amazon.com/Designing-Interfaces-Jenifer-Tidwell/dp/1449379702/ref=sr_1_1) [read][$] * [About Face: The Essentials of Interaction Design](http://www.amazon.com/About-Face-Essentials-Interaction-Design/dp/1118766571/ref=pd_sim_14_3) [read][$] * [Don't Make Me Think, Revisited: A Common Sense Approach to Web Usability](http://www.amazon.com/Dont-Make-Think-Revisited-Usability/dp/0321965515/ref=pd_sim_14_2) [read][$] **译者补充:** * [简约至上:交互式设计四策略](http://www.amazon.cn/%E7%AE%80%E7%BA%A6%E8%87%B3%E4%B8%8A-%E4%BA%A4%E4%BA%92%E5%BC%8F%E8%AE%BE%E8%AE%A1%E5%9B%9B%E7%AD%96%E7%95%A5-%E7%A7%91%E5%B0%94%E4%BC%AF%E6%81%A9/dp/B004I91HCY/ref=sr_1_1?ie=UTF8&qid=1445948451&sr=8-1) [read][RMB] * [交互设计沉思录](http://www.amazon.cn/%E4%BA%A4%E4%BA%92%E8%AE%BE%E8%AE%A1%E6%B2%89%E6%80%9D%E5%BD%95-%E7%A7%91%E5%B0%94%E7%A7%91/dp/B009A7DAXI/ref=sr_1_3?ie=UTF8&qid=1445948451&sr=8-3) [read][RMB] ================================================ FILE: 02-fedHandlebook-master/learning/dev-tools.md ================================================ ###Web 开发者工具 >Web 开发者工具允许开发者测试和调试代码, 它们不同于网站生成器和 IDE, 因为 Web 开发者工具不直接参与网页的创建, 而是用于测试网站或 Web 应用的用户界面接口的工具. >Web 开发者工具是浏览器的加载项或内置功能. 当今最流行的web浏览器, Google Chrome, Firefox, Opera, Internet Explorer, 和 Safari 都内置工具用于帮助开发者, 并且在各自的插件下载中心, 还提供很多额外的加载项. >Web 开发者工具允许开发者使用各种各样的 Web 技术, 包括 HTML, CSS, DOM, JavaScript 和 其它浏览器能够处理的组件. 由于日益增长的需求, 更多流行的 Web 浏览器包括了更多面向开发人员的功能. 尽管大多数浏览器都配备了开发者工具, 但是 [谷歌开发者工具](https://developers.google.com/web/tools/chrome-devtools/) 是目前谈论最多, 应用最广泛的开发者工具. 我建议学习和使用 [谷歌开发者工具](https://developers.google.com/web/tools/chrome-devtools/), 因为谷歌开发者工具周围有学习 Web 开发者工具最好的资源. **学习使用谷歌开发者工具:** * [Explore and Master Chrome DevTools](http://discover-devtools.codeschool.com/) * [Chrome 开发者工具](https://code.tutsplus.com/courses/chrome-developer-tools) [watch][$] * [使用 Chrome 开发者工具](http://www.pluralsight.com/courses/chrome-developer-tools) [watch][$] **谷歌开发者工具文档:** * [Per-Panel 文档](https://developers.google.com/web/tools/chrome-devtools/#docs) * [命令行接口参考](https://developers.google.com/web/tools/javascript/command-line/command-line-reference?hl=en) * [键盘 & UI 快捷键参考](https://developers.google.com/web/tools/iterate/inspect-styles/shortcuts) * [设置](https://developer.chrome.com/devtools/docs/settings) **新闻/简报/博客/建议:** * [Dev Tips](https://umaar.com/dev-tips/) **译者补充:** * [Chrome Devtools Tips & Tricks](http://mo.github.io/2015/10/19/chrome-devtools.html) * [Chrome开发者工具不完全指南](http://www.92fenxiang.com/121.html) * [Chrome 开发工具指南](http://wiki.jikexueyuan.com/project/chrome-devtools/) ================================================ FILE: 02-fedHandlebook-master/learning/direct-learning.md ================================================ ###Directed learning This section focuses on directed learning via schools, courses, programs and bootcamps. ================================================ FILE: 02-fedHandlebook-master/learning/dns.md ================================================ ###域名系统(又称 DNS) >对于个人电脑、服务器或连接到互联网任何资源, 或专用网络而言, 域名系统(DNS)是一个分层分布式命名系统, 用给每个参与的实体分配域名的方式将各种信息联系起来, 更重要的是, 为能访问全球的计算机服务和设备, DNS 将所需的数字 IP 地址转变为人类容易记住的域名. DNS 是大多数互联网服务的必要功能, 因为这是主要的 IP 地址服务. - wikipedia ![dns](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/dns.jpg) * [什么是 DNS](https://www.youtube.com/watch?v=72snZctFFtA) [watch] * [DNS 是怎么工作的?](https://howdns.works/ep1/) [read] ================================================ FILE: 02-fedHandlebook-master/learning/dom.md ================================================ ###DOM, BOM & JQuery >**DOM** - 文档对象模型(DOM)用于代表和对象交互的HTML, XHTML 和 XML 文档, 是一种跨平台和语言无关性的约定. 每一份文档的所有节点被组织成一种树结构, 称为 DOM 树. DOM 对象通过使用对象上的方法被处理和操作, 一个 DOM 对象的公共接口被指定为它的应用程序编程接口(API). - wikipedia.org >**BOM** - 浏览器对象模型(BOM)是一种浏览器规范, 代指 Web 浏览器暴露出的对象. 与文档对象模型不同的是, 目前并没有关于浏览器对象模型的标准和严格定义, 因而浏览器厂商可以按照他们的意愿, 采取任何方式来实现 BOM. - wikipedia.org >**JQuery** - JQuery 是一个跨平台的 JavaScript 库, 其设计目的是简化客户端的 HTML 脚本操作. JQuery 也是目前最流行的 JavaScript 库, 在目前排名前1000万的网站中, 65% 的网站安装了 JQuery. JQuery 是免费的, 基于 MIT 协议的开源软件. - wikipedia.org 最理想但又难度最大的学习路径就是首先学习 JavaScript, 然后学习 DOM, 之后再学习 JQuery. 然后, 完全可以按照你所想的学习路径来安排学习的顺序. 大多数的前端开发者在了解 JavaScript 和 DOM 之后, 才会接触 JQuery. 无论采取什么学习路径, 都要确保 JavaScript, DOM 或者 JQuery 不要成为"黑盒子". **入门学习:** * [DOM](http://eloquentjavascript.net/13_dom.html) [read] * [codecademy.com jQuery](https://www.codecademy.com/tracks/jquery) [watch] * [jQuery 启蒙](http://jqueryenlightenment.com/) [read] * [JQuery 入门](http://www.pluralsight.com/courses/jquery-getting-started) [watch][$] **进阶学习:** * [DOM 启蒙](http://www.amazon.cn/DOM%E5%90%AF%E8%92%99-%E6%9E%97%E5%BE%B7%E5%88%A9/dp/B00JWXDB52/ref=sr_1_2?ie=UTF8&qid=1446477828&sr=8-2) [read][RMB] 或 [免费在线阅读](http://domenlightenment.com/) * [JS & DOM 进阶](https://frontendmasters.com/courses/javascript-jquery-dom/) [watch][$] * [DOM 进阶: 动态网页设计技术](http://www.amazon.com/gp/product/1590598563/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=1590598563&linkCode=as2&tag=fronenddevejo-20&linkId=VQZU5EQIQQXCF56Y) [read][$] * [常见 JQuery bugs 修复](http://www.pluralsight.com/courses/fixing-common-jquery-bugs) [watch][$] * [JQuery 技巧](http://www.pluralsight.com/courses/jquery-tips-and-tricks) [watch][$] * [jQuery-free JavaScript](http://www.pluralsight.com/courses/jquery-free-javascript) [watch][$] * [Douglas Crockford: DOM 理论--不可忽视的 API](https://www.youtube.com/watch?v=Y2Y0U-2qJMs&list=PL5586336C26BDB324&index=2) [watch] **参考/文档:** * [MDN: 文档对象模型](https://developer.mozilla.org/zh/docs/Web/API/Document_Object_Model) * [MDN: 事件参考](https://developer.mozilla.org/zh/docs/Web/Events) * [JQuery 文档](http://api.jquery.com/) * [MDN: 浏览器对象模型](https://developer.mozilla.org/en-US/docs/Web/API/Window) * [MSDN: 文档对象模型](https://msdn.microsoft.com/en-us/library/hh772384%28v=vs.85%29.aspx) **标准和规范:** * [W3C DOM4](http://www.w3.org/TR/2014/WD-dom-20140204/) * [DOM 在线标准](https://dom.spec.whatwg.org/) * [DOM 3 事件规范](http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/) * [DOM 技术报告](http://www.w3.org/DOM/DOMTR) **译者补充:** * [You Might Not Need JQuery(英)](http://youmightnotneedjquery.com/) [中文版](http://www.webhek.com/you-do-not-need-jquery) ================================================ FILE: 02-fedHandlebook-master/learning/fonts.md ================================================ ###Web 字体 & 图标 >Web 字体是指在 WWW 上使用的字体. 当 HTML 文档第一次被创建时, 字体风格和样式会被每个 Web 浏览器的设置所控制, 因为直到 1995 年网景介绍了``标记之前, 个人网页没有控制字体显示的方式, 而``标记在 HTML 3.2 规范中被标准化. 然而, 被标记指定的字体必须安装在用户的电脑上, 或者是一种可以依赖的字体, 如: 浏览器默认的 sans-serif 字体或 monospace 字体. 在1996 年 发布的 CSS 1.0 规范也提供指定使用字体的功能. > 1998 年, CSS 2.0 规范发布, 意图通过字体匹配, 合成和下载技术, 改善字体的选择过程, 但这些技术并没有得到使用, 并在 CSS2.1 规范中被移除了. 然而, 在 1997 年发布的 IE 4.0 增加了对字体下载的支持, 随后, CSS 3 的字体模块变包含了字体下载, 并且 Safari 3.1, Opera 10 和 Mozilla Firefox 3.5 实现了这一功能, 随后便增加了 Web 字体和所使用字体的下载. - wikipedia **综合学习:** * [Web 字体排版](http://www.pluralsight.com/courses/typography-for-web-1790) [watch][$] * [@font-face 快速学习指南](http://www.html5rocks.com/en/tutorials/webfonts/quick/) [read] * [响应式排版](https://frontendmasters.com/courses/responsive-typography/) [watch][$] * [用最好的字体来美化网页](http://hellohappy.org/beautiful-web-type/) [read] ================================================ FILE: 02-fedHandlebook-master/learning/front-end-apps.md ================================================ ###前端应用架构设计 * [JavaScript Web 应用开发](http://www.amazon.cn/JavaScript-Web%E5%BA%94%E7%94%A8%E5%BC%80%E5%8F%91-%E9%98%BF%E6%A0%B9%E5%BB%B7-%E6%AF%94%E7%93%A6%E5%8D%A1/dp/B016I9T8QI/ref=sr_1_2) [read][RMB] * [用 React & Ampersand 构建 APP](http://learn.humanjavascript.com/react-ampersand) [watch][$] * [Human JavaScript](http://read.humanjavascript.com/) [read] * [JavaScript 应用程序编程](http://chimera.labs.oreilly.com/books/1234000000262/index.html) [read] * [构建现代单页应用](https://frontendmasters.com/workshops/web-apps/) [watch][$] * [JavaScript 函数式编程](https://frontendmasters.com/courses/organizing-javascript/) [watch][$] * [JavaScript: 模块](http://eloquentjavascript.net/10_modules.html) [read] * [Web UI 架构设计](https://frontendmasters.com/courses/web-ui-architecture/) [watch][$] * [Web 应用设计指南](http://www.html5rocks.com/webappfieldguide/toc/index/) [read] * [UI 架构设计](http://www.pluralsight.com/courses/web-ui-architecture) [watch][$] * [Terrific](http://terrifically.org/) [read] * [Nicholas Zakas: 可扩展的 JavaScript 应用架构](https://www.youtube.com/watch?v=vXjVFPosQHw) [watch] * [大型 JavaScript 应用架构设计模式](http://addyosmani.com/largescalejavascript/) [read] * [静态应用指南](http://www.staticapps.org/) [read] ================================================ FILE: 02-fedHandlebook-master/learning/general-front-end.md ================================================ ###前端开发综合学习 **入门学习:** * [前端参考指南](https://github.com/bendc/frontend-guidelines) [read] * [Web 开发者](http://www.yellowshoe.com.au/standards) [read] * [前端代码标准](http://isobar-idev.github.io/code-standards/) [read] * [Web 基本原理](https://developers.google.com/web/fundamentals) [read] * [前端课程](https://gist.github.com/stevekinney/03027e71aac341af14a2) [read] * [FreeCodeCamp](http://freecodecamp.com/) [interact] * [开发前端 JS 应用](https://www.youtube.com/watch?v=q4zEGkjTBFA) [watch] * [前端工程师](https://www.youtube.com/watch?v=Lsg84NtJbmI) [watch] * [Web 前端开发的工作内容](http://www.pluralsight.com/courses/front-end-web-development-career-kickstart) [watch][$] * [Web 前端开发入门](http://www.pluralsight.com/courses/front-end-web-development-get-started) [watch][$] * [用 HTML5, CSS, 和 JavaScript 快速进行 Web 前端开发](http://www.pluralsight.com/courses/front-end-web-app-html5-javascript-css) [watch][$] * [Web 开发介绍](https://frontendmasters.com/courses/web-development/) [watch][$] * [Web 前端开发基础](https://www.udemy.com/foundations-of-front-end-development/) [watch][$] * [精益前端工程](https://frontendmasters.com/courses/lean-front-end-engineering/) [watch][$] * [前端(JS)开发基线: 2015](http://rmurphey.com/blog/2015/03/23/a-baseline-for-front-end-developers-2015/) [read] * [Web 前端开发入门学习](https://teamtreehouse.com/tracks/front-end-web-development) [watch][$] * [Web 前端开发进阶学习](https://mijingo.com/products/bundles/front-end-dev-mastery/) [watch][$] * [没有学位的 Web 前端开发者](https://www.udacity.com/course/front-end-web-developer-nanodegree--nd001) [watch][$ **前端简报, 资讯和博客:** * [shoptalkshow.com](http://shoptalkshow.com/) * [frontendfront.com](http://frontendfront.com/) * [webtoolsweekly.com](http://webtoolsweekly.com/) * [O'Reilly Web Platform Radar](http://radar.oreilly.com/web-platform) * [The Web Platform Podcast](http://thewebplatform.libsyn.com/) * [The Web Ahead](http://thewebahead.net/) * [The Big Web Show](http://5by5.tv/bigwebshow) * [Fresh Brewed Frontend](https://freshbrewed.co/frontend/) * [Mobile Web Weekly](http://mobilewebweekly.co/) * [Open Web Platform Daily Digest](http://webplatformdaily.org/) * [FRONT-END DEV weekly](http://frontenddevweekly.com/) **译者补充:** * [前端工程系列文章](https://github.com/fouber/blog/issues/10) * [前端知识体系](http://qianduanfan.com/index.php/topic/show/231) * [前端资源大总结](http://qianduanfan.com/index.php/topic/show/234) * [前端入门](https://github.com/qiu-deqing/FE-learning) * [WebPlatForm](http://www.webplatform.org/) * [前端开发规范手册](http://zhibimo.com/read/Ashu/front-end-style-guide/) * [Web 开发电子书下载(英)](http://www.allitebooks.com/web-development/) * 对前端开发者有用的文档和指南: [1](http://www.sitepoint.com/20-docs-guides-front-end-developers/) [2](http://www.sitepoint.com/20-more-docs-guides-front-end-developers/) [3](http://www.sitepoint.com/another-20-docs-guides-front-end-developers/) [4](http://www.sitepoint.com/20-docs-guides-front-end-developers-4/) [5](http://www.sitepoint.com/20-docs-guides-front-end-developers-5/) [6](http://www.sitepoint.com/20-docs-guides-front-end-developers-6/) ================================================ FILE: 02-fedHandlebook-master/learning/headless-browsers.md ================================================ ###无壳浏览器 >无壳浏览器是指没有图形用户界面的 Web 浏览器. >无壳浏览器拥有一个和受欢迎的 Web 浏览器相似的环境, 并提供了网页的自动化控制, 但要通过命令行接口或使用网络通信工具执行. 对于测试网页, 无壳浏览器是非常有用的, 因为和普通浏览器一样, 它们能渲染和理解 HTML, 包括样式元素, 如: 页面布局, 颜色, 字体选择, JavaScript 的执行和 AJAX, 但是当使用其它方法时, Ajax 通常就不可用了. 在 2009 年, 谷歌声称使用无壳浏览器有助于搜索引擎使用 Ajax 从其它网站索引内容. - wikipedia * [PhantomJS Cookbook](http://www.amazon.com/PhantomJS-Cookbook-Rob-Friesel/dp/178398192X) [read][$] * [Getting Started with PhantomJS](http://www.amazon.com/Getting-Started-PhantomJS-Aries-Beltran/dp/1782164227) [read][$] * [Rapid PhantomJS](https://www.packtpub.com/web-development/rapid-phantomjs-video) [watch][$] * [PhantomJS for Web Automation](https://www.youtube.com/watch?v=OqEcn_6GBDI) [watch] ================================================ FILE: 02-fedHandlebook-master/learning/html-css.md ================================================ ###HTML & CSS >**HTML** - 超文本标记语言, 通常被称为 HTML, 是被用于创建网页的标准标记语言. Web浏览器能将 HTML 文件渲染成可见的或者可听到的. HTML 随着线索提示, 语义化地描述了网站的结构, 使它成为一种标记语言, 而不是编程语言. - wikipedia.org >**CSS** - 层叠式样式表(CSS)是用于描述外观和格式化标记语言编写的文档的样式表语言. 尽管经常被用来改变用 HTML 和 XHTML 编写的网页和用户界面的样式, 但也可用于任何 XML 文档, 包括纯 XML, SVG 和 XUL. 跟 JavaScript 和 HTML 一样, CSS是被大多数网站用于为Web应用程序创建富有吸引力的网页, 用户界面的一种基础技术, 也为许多移动应用程序创建用户界面. - wikipedia.org 就像建造房子, 你可以认为 HTML 是骨架, 而 CSS 是油漆和装饰. **入门学习:** * [codecademy.com HTML & CSS](https://www.codecademy.com/tracks/web) [interact] * [HTML/CSS 介绍: 网页制作](https://www.khanacademy.org/computing/computer-programming/html-css) [watch] * [HTML & CSS 编码学习](http://learn.shayhowe.com/html-css/) [read] * [CSS 布局](http://learnlayout.com/) [read] * [Web 前端开发入门](http://www.pluralsight.com/courses/front-end-web-development-get-started) [watch][$] * [HTML & CSS 设计与构建网站](http://www.amazon.cn/HTML-CSS%E8%AE%BE%E8%AE%A1%E4%B8%8E%E6%9E%84%E5%BB%BA%E7%BD%91%E7%AB%99-%E8%BE%BE%E7%A7%91%E7%89%B9/dp/B00BMK4GKW/ref=sr_1_1?ie=UTF8&qid=1446191225&sr=8-1) [read][RMB] * [快速使用 HTML5, CSS 3 & JavaScript 进行 Web 前端开发](http://www.pluralsight.com/courses/front-end-web-app-html5-javascript-css) [watch][$] * [深入理解 HTML: 语义, 标准与样式](http://www.amazon.com/gp/product/1590597656/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=1590597656&linkCode=as2&tag=fronenddevejo-20&linkId=VFZVICLZO6GUZQI2) [read][$] * [CSS 定位](http://www.pluralsight.com/courses/css-positioning-1834) [watch][$] * [HTML 文档流](http://www.pluralsight.com/courses/html-document-flow-1837) [watch][$] * [HTML5 & CSS3 介绍](https://frontendmasters.com/courses/introduction-html5-css3/) [watch][$] * [CSS 中的绝对居中](http://codepen.io/shshaw/full/gEiDt) [read] * [CSS 盒模型](https://webdesign.tutsplus.com/courses/understanding-the-css-box-model) [watch] * [HTML 表单结构](https://webdesign.tutsplus.com/courses/solid-html-form-structure) [watch] * [HTML 语义化: 如何构建 Web 页面](https://webdesign.tutsplus.com/courses/semantic-html-how-to-structure-web-pages) [watch] **进阶学习:** * [从 CSS 1 到 CSS 4 的选择器参考](http://css4-selectors.com/selectors/) [read] * [CSS Diner](http://flukeout.github.io/) [interact] * [深入理解 CSS3](https://frontendmasters.com/courses/css3-in-depth/) [watch][$] * [atozcss.com/](http://www.atozcss.com/) [watch] * [Flexbox 完全指南](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) [read] **参考/文档:** * [htmlelement.info](http://htmlelement.info/) * [MDN CSS reference](https://developer.mozilla.org/zh/docs/Web/CSS/Reference) * [MDN HTML element reference](https://developer.mozilla.org/zh/docs/Web/HTML/Element) * [cssvalues.com/](http://cssvalues.com/) * [CSS TRIGGERS...A GAME OF LAYOUT, PAINT, AND COMPOSITE](http://csstriggers.com/) * [HTML attributes reference](https://developer.mozilla.org/zh/docs/Web/HTML/Attributes) **术语表:** * [HTML 元素编程术语参考](https://www.codecademy.com/articles/glossary-html) * [CSS 属性和选择器编程术语参考](https://www.codecademy.com/articles/glossary-css) **标准/规范:** * [W3C HTML5 标准](http://www.w3.org/TR/html5/): WWW 核心语言的第五个主要版本 * [HTML 元素在线标准](https://html.spec.whatwg.org/multipage/semantics.html#semantics) * [HTML 语法](https://html.spec.whatwg.org/multipage/syntax.html#syntax) * [W3C HTML 规范](http://www.w3.org/standards/techs/html#w3c_all) * [全局属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes) * [CSS 2.2 规范](https://drafts.csswg.org/css2/) * [CSS 3 选择器](http://www.w3.org/TR/css3-selectors/) * [W3C CSS 规范](http://www.w3.org/Style/CSS/current-work#roadmap) **CSS 架构设计:** * [OOCSS](http://oocss.org/) [read] * [SMACSS](https://smacss.com/) [read][$] * [SMACSS](https://frontendmasters.com/courses/smacss/) [watch][$] * [Atomic Design](http://atomicdesign.bradfrost.com/) [read] **编写规范:** * [CSS 通用编写规范](https://github.com/necolas/idiomatic-css) [read] * [开发灵活的, 持久的和可持续的 HTML 和 CSS 的标准](http://mdo.github.io/code-guide/) [read] * [cssguidelin.es](http://cssguidelin.es/) [read] * [谷歌的 HTML/CSS 编程风格指南](http://google-styleguide.googlecode.com/svn/trunk/htmlcssguide.xml#General_Formatting) **HTML/CSS 简报:** * [HTML 5 Weekly](http://html5weekly.com/) * [CSS Weekly](http://css-weekly.com/archives/) **译者补充:** * [CSS 3 教程](https://waylau.gitbooks.io/css3-tutorial/content/docs/Introduction.html) * [HTML & CSS 测试](https://sitthetest.com/tests) * [Pro HTML5 Programming](http://apress.jensimmons.com/v5/pro-html5-programming/ch0.html) * [Pro CSS3 Animation](http://apress.jensimmons.com/v5/pro-css3-animation/ch2.html) * [CSS Protips](https://github.com/AllThingsSmitty/css-protips) * [精通CSS: 高级Web标准解决方案](http://www.amazon.cn/%E7%B2%BE%E9%80%9ACSS-%E9%AB%98%E7%BA%A7Web%E6%A0%87%E5%87%86%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88-%E5%B7%B4%E5%BE%B7/dp/B003IURKAM/ref=sr_1_1?s=books&ie=UTF8&qid=1446191443&sr=1-1) * [高流量网站CSS开发技术](http://www.amazon.cn/%E9%AB%98%E6%B5%81%E9%87%8F%E7%BD%91%E7%AB%99CSS%E5%BC%80%E5%8F%91%E6%8A%80%E6%9C%AF-%E8%82%AF%E5%B0%BC%E8%BF%AA/dp/B00FIIM9JO/ref=sr_1_2?s=books&ie=UTF8&qid=1446191443&sr=1-2) * [深入理解HTML5: 语义, 标准与样式](http://www.amazon.cn/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3HTML5-%E8%AF%AD%E4%B9%89-%E6%A0%87%E5%87%86%E4%B8%8E%E6%A0%B7%E5%BC%8F-%E5%B8%83%E6%8B%89%E5%BE%B7%E7%A6%8F/dp/B00DNDR5HM/ref=sr_1_1?ie=UTF8&qid=1446191387&sr=8-1) * [HTML 5与CSS 3权威指南](http://www.amazon.cn/HTML-5%E4%B8%8ECSS-3%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E9%99%86%E5%87%8C%E7%89%9B/dp/B00B03VVRW/ref=sr_1_4?ie=UTF8&qid=1446191598&sr=8-4) ================================================ FILE: 02-fedHandlebook-master/learning/interface.md ================================================ ###接口/API 设计 **数据(类似 JSON) API:** * [Build APIs You Won't Hate](http://apisyouwonthate.com/) [$][read] * [JSON API](http://jsonapi.org/) [read] **JavaScript API** * [Writing JavaScript APIs](http://blog.wolksoftware.com/writing-javascript-apis) [read] * [Designing Better JavaScript APIs](http://www.smashingmagazine.com/2012/10/designing-javascript-apis-usability/) [read] **译者补充** * [HTTP API 设计指南](https://github.com/cocoajin/http-api-design-ZH_CN) * [用 JSON 构建 API 的标准指南](http://wiki.jikexueyuan.com/project/json-api/) ================================================ FILE: 02-fedHandlebook-master/learning/internet.md ================================================ ###Internet/web >互联网使用网络协议套件(TCP / IP)链接全球数十亿的设备, 是一个从区域到全球, 由数以百万计的私人, 公共, 学术, 商业, 和政府网络组成的全球性网络系统, 并通过一系列广泛的电子, 无线, 光纤网络技术相互链接. 互联网提供了广泛的信息资源和服务, 如早期的超文本文档和应用万维网(WWW), 电子邮件, 电话和点对点文件共享网络. - wikipedia * [WHAT IS THE INTERNET? or, "You Say Tomato, I Say TCP/IP"](http://www.20thingsilearned.com/en-US/what-is-the-internet/1) [read] * [How does the Internet work](http://www.w3.org/wiki/How_does_the_Internet_work) [read] * [How Does the Internet Work?"](http://www.theshulers.com/whitepapers/internet_whitepaper/) [read] * [How Does the Internet Work?"](http://www.theshulers.com/whitepapers/internet_whitepaper/) [read] * [How does the Internet Work?](http://web.stanford.edu/class/msande91si/www-spr04/readings/week1/InternetWhitepaper.htm) [read] * [How the Internet Works in 5 Minutes](https://www.youtube.com/watch?v=7_LPdttKXPc) [watch] * [How The Web Works](https://www.eventedmind.com/classes/how-the-web-works-7f40254c) [watch][$] * [How the Internet Works](https://www.khanacademy.org/partner-content/code-org/internet-works) [watch] ================================================ FILE: 02-fedHandlebook-master/learning/js.md ================================================ ###JavaScript >JavaScript 是一种高级的, 动态的, 无类型的和解释型的编程语言, 它已经在 ECMAScript 语言规范中被标准化. 跟 HTML 和 CSS 一样, JavaScript 是 WWW 内容生成的第三种必不可少的技术; 大多数的网会使用Javascript, 并且 Javascript 被所有现在Web浏览器支持. JavaScript 基于原型和函数优先的特点, 使它成为多范型的语言, 支持面向对象的, 命令式的, 和函数式编程风格. JavaScript 能提供 API 来处理文本, 数组, 日期和正则表达式, 但不包括任何 I/O, 如网络, 存储或图形工具, 对这些的依赖取决于宿主环境中嵌入了什么. - wikipedia.org **入门学习:** * [https://www.codecademy.com/en/tracks/javascript](https://www.codecademy.com/en/tracks/javascript) [interact] * [JavaScript 高级程序设计](http://www.amazon.cn/JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E6%B3%BD%E5%8D%A1%E6%96%AF/dp/B007OQQVMY/ref=sr_1_1?ie=UTF8&qid=1446188503&sr=8-1) [read][RMB] * [JavaScript Enlightenment](http://www.javascriptenlightenment.com/) [read] * [JavaScript面向对象精要](http://www.amazon.cn/JavaScript%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%B2%BE%E8%A6%81-%E7%BE%8E-%E5%B0%BC%E5%8F%A4%E6%8B%89%E6%96%AF/dp/B00VDSW6X2/ref=sr_1_1?ie=UTF8&qid=1446188606&sr=8-1) [read][RMB] * [Speaking JavaScript](http://speakingjs.com/es5/index.html) [read] * [你不知道的 JavaScript](https://github.com/getify/You-Dont-Know-JS/blob/master/up%20&%20going/README.md#you-dont-know-js-up--going) [read] * [理解 ECMAScript 6](https://github.com/nzakas/understandinges6) [read] * [JavaScript 模式](http://www.amazon.cn/JavaScript%E6%A8%A1%E5%BC%8F-%E6%96%AF%E7%89%B9%E5%87%A1%E6%B4%9B%E5%A4%AB/dp/B008QTG1HS/ref=sr_1_1?ie=UTF8&qid=1446188801&sr=8-1) [read][RMB] * [JS.Next: ES6](https://frontendmasters.com/courses/jsnext-es6/) [watch][$] * [Crockford on JavaScript - Volume 1: The Early Years](https://www.youtube.com/watch?v=JxAXlJEmNMg) [watch] * [Crockford on JavaScript - Chapter 2: And Then There Was JavaScript](https://www.youtube.com/watch?v=RO1Wnu-xKoY) [watch] * [Crockford on JavaScript - Act III: Function the Ultimate](https://www.youtube.com/watch?v=ya4UHuXNygM) [watch] * [Crockford on JavaScript - Episode IV: The Metamorphosis of Ajax](https://www.youtube.com/watch?v=Fv9qT9joc0M) [watch] * [Crockford on JavaScript - Part 5: The End of All Things](https://www.youtube.com/watch?v=47Ceot8yqeI) [watch] * [Crockford on JavaScript - Scene 6: Loopage](https://www.youtube.com/watch?v=QgwSUtYSUqA) [watch] * [JavaScript 模块](http://jsmodules.io/cjs.html) [read] **进阶学习:** * [JavaScript 函数式编程](http://www.amazon.cn/JavaScript%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B-%E4%BD%9B%E6%A0%BC%E6%96%AF/dp/B01264FOY4/ref=sr_1_1?ie=UTF8&qid=1446189590&sr=8-1) [read][RMB] * [ECMA-262 by Dmitry Soshnikov](http://dmitrysoshnikov.com/) [read] * [JavaScript 进阶](https://frontendmasters.com/courses/advanced-javascript/) [watch][$] * [JavaScript 语言精粹](http://www.amazon.cn/JavaScript%E8%AF%AD%E8%A8%80%E7%B2%BE%E7%B2%B9-%E9%81%93%E6%A0%BC%E6%8B%89%E6%96%AF%E2%80%A2%E5%85%8B%E7%BD%97%E5%85%8B%E7%A6%8F%E5%BE%B7/dp/B0097CON2S/ref=sr_1_1?ie=UTF8&qid=1446189734&sr=8-1) [read][RMB] * [你不知道的 JS: 作用域 & 闭包](https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/README.md#you-dont-know-js-scope--closures) [read] * [你不知道的 JS: this & 原型](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes) [read] * [你不知道的 JS: 数据类型 & 语法](https://github.com/getify/You-Dont-Know-JS/blob/master/types%20&%20grammar/README.md#you-dont-know-js-types--grammar) [read] * [你不知道的 JS: 异步 & 性能](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20&%20performance/README.md#you-dont-know-js-async--performance) [read] * [你不知道的 JS: ES6 & Beyond](https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20&%20beyond/README.md#you-dont-know-js-es6--beyond) [read] * [Eloquent JavaScript](http://eloquentjavascript.net/) [read] * [测试驱动的 JavaScript 开发](http://www.amazon.cn/%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E7%9A%84JavaScript%E5%BC%80%E5%8F%91-%E7%BA%A6%E7%BF%B0%E6%A3%AE/dp/B0077KA3J4/ref=sr_1_1?ie=UTF8&qid=1446190077&sr=8-1) [read][RMB] * [JavaScript Allonge](https://leanpub.com/javascriptallongesix) [read][$] * [JavaScript With Promises](http://www.amazon.com/JavaScript-Promises-Daniel-Parker/dp/1449373216/ref=pd_sim_sbs_14_5) [read][$] * [高性能 JavaScript](http://www.amazon.cn/%E9%AB%98%E6%80%A7%E8%83%BDJavaScript-%E5%B0%BC%E5%8F%A4%E6%8B%89%E6%96%AF-%E6%B3%BD%E5%8D%A1%E6%96%AF/dp/B013SGB2AO/ref=sr_1_1?ie=UTF8&qid=1446190545&sr=8-1) [read][$] * [JavaScript 正则表达式入门](http://codylindley.com/techpro/2013_05_14__javascript-regular-expression-/) [read] * [正则表达式使用](http://www.lynda.com/Regular-Expressions-tutorials/Using-Regular-Expressions/85870-2.html) [watch][$] **参考/文档:** * [MDN JavaScript reference](https://developer.mozilla.org/zh/docs/Web/JavaScript/Reference) **术语表/百科全书:** * [JavaScript 术语表](https://www.codecademy.com/articles/glossary-javascript) * [JavaScript 百科全书](http://www.crockford.com/javascript/encyclopedia/) **标准/规范:** * [ECMAScript® 2015 语言规范](http://www.ecma-international.org/ecma-262/6.0/) * [ECMA262 的状态, 进度和文档](https://github.com/tc39/ecma262) **编程规范:** * [Node.js 风格指南](https://github.com/felixge/node-style-guide) * [JavaScript 开发原则](https://github.com/rwaldron/idiomatic.js) * [Airbnb JavaScript 风格指南](http://airbnb.io/javascript/) **JavaScript 简报, 新闻和播客:** * [JavaScript Jabber](https://devchat.tv/js-jabber/) * [JavaScript Weekly](http://javascriptweekly.com/) * [Echo JS](http://www.echojs.com/) * [JavaScript Kicks](http://javascriptkicks.com/) * [javascript.com](https://www.javascript.com/news) * [FiveJS](https://fivejs.codeschool.com/) * [JavaScript Live](https://jslive.com/) **译者补充:** * [重新认识 JavaScript](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript) * [JavaScript 标准参考教程](http://javascript.ruanyifeng.com/) * [JavaScript 秘密花园](http://bonsaiden.github.io/JavaScript-Garden/zh/) * [浅谈JavaScript系列](http://my.oschina.net/os2015/blog/484017) * [深入理解JavaScript系列](http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html) * [JavaScript Promise 迷你书](http://liubin.github.io/promises-book/) * [学用 JavaScript 设计模式](http://www.oschina.net/translate/learning-javascript-design-patterns) * [ES 6 入门(中文)](http://es6.ruanyifeng.com/) * [ES 6 入门(英文)](https://leanpub.com/understandinges6/read) * [ES 6 In Depth 系列](http://www.infoq.com/cn/es6-in-depth/) * [ES6 Overview in 350 Bullet Points](https://ponyfoo.com/articles/es6) * [JS The Right Way](http://jstherightway.org/) * [JavaScript 新手教程](http://jaskokoyn.com/javascript-tutorial-series/) * [JavaScript 进阶教程](http://jaskokoyn.com/advanced-javascript-tutorial-series/) * [国外优秀 JavaScript 资源推荐](http://www.ido321.com/302.html) * [awesome-javascript1](https://github.com/wwsun/awesome-javascript) [awesome-javascript2](https://github.com/sorrycc/awesome-javascript) * [JavaScript 免费编程电子书索引(中文)](https://github.com/justjavac/free-programming-books-zh_CN#javascript) * [JavaScript:40+基本的 Web 开发工具](http://www.ido321.com/1543.html) * [JavaScript Puzzlers](http://javascript-puzzlers.herokuapp.com/) * [JavaScript Test](https://sitthetest.com/tests) * [Awesome JavaScript-CN](https://github.com/jobbole/awesome-javascript-cn) ================================================ FILE: 02-fedHandlebook-master/learning/json.md ================================================ ###JSON(JavaScript Object Notation) >JSON, 有时也称 JavaScript 对象表示, 是一种使用人类可读的文本传输由键值对组成的数据对象的开放格式. 对于异步浏览器/服务器通信(AJAJ), JSON 是主要的数据格式, 很大程度上代替了 XML(AJAX). >尽管最初是从 JavaScript 脚本语言衍生而来, 但是 JSON 是语言无关性的数据格式, 在许多编程语言中, 代码解析和生成 JSON 是很容易的. >JSON 的格式最初是由 Douglas Crockford 指定的, 但目前却被描述成两种标准: RFC 7159 和 ECMA-404. ECMA 标准只允许被合法的语法语句描述, 而 RFC 则提供了一些语义化描述和安全考虑. JSON 的官方网络媒体类型 application / JSON, 扩展名是 .json. - wikipedia.org **综合学习:** * [json.com](https://www.json.com/) [read] * [什么是 JSON](https://mijingo.com/lessons/what-is-json/) [watch] * [JSON 权威指南](http://www.amazon.com/Introduction-JavaScript-Object-Notation-Point/dp/1491929480/ref=pd_sim_sbs_14_1) [read][$] **参考/文档** * [json.org/](http://json.org/) [read] **标准/规范:** * [ECMA-404 JSON 数据交互格式](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) * [RFC 7159 JSON 数据交互格式](https://tools.ietf.org/html/rfc7159) **架构设计:** * [JSON API](http://jsonapi.org/) **译者补充:** * [JSON 系列文章](http://jaskokoyn.com/json-tutorial-series/) ================================================ FILE: 02-fedHandlebook-master/learning/learn-from.md ================================================ ###前端开发者从哪里学 关于前端开发, 独立学习会慢慢地变得没有意义. 前端开发的高级从业人员已经产出足够多的内容, 而你只要通过关注前端"资讯"(简报, 资讯 & 博客), 跟着社区学习就行. ================================================ FILE: 02-fedHandlebook-master/learning/module.md ================================================ ###模块加载和依赖管理 **综合学习:** * [用 Browserify 创建 JavaScript 模块](http://www.pluralsight.com/courses/creating-javascript-modules-browserify) [watch][$] * [Webpack 基本原理](http://www.pluralsight.com/courses/webpack-fundamentals) [watch] * [browserify-handbook](https://github.com/substack/browserify-handbook) [read] * [ES6 模块](http://developer.telerik.com/featured/choose-es6-modules-today/) [read] **参考/文档:** * [browserify](http://browserify.org/) * [system.js](https://github.com/systemjs/systemjs) * [webpack](http://webpack.github.io/) **译者补充:** * [React Webpack cookbook](https://fakefish.github.io/react-webpack-cookbook/) * [详解前端模块化工具-Webpack](http://www.ido321.com/1646.html) ================================================ FILE: 02-fedHandlebook-master/learning/multi-things-dev.md ================================================ ###多平台开发 ![multi-things-dev](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/things.jpg) 一个网站或应用不仅能运行在各种台式机, 笔记本电脑, 平板和手机, 还能运行于少部分其它设备(手表, 温控器, 电冰箱等等). 你将怎么决定支持哪些平台和为支持这些平台, 怎么去开发, 这被称为多平台开发策略. 接下来, 我会列出常见的多平台开发策略: * 创建 [响应式 Web 设计](https://en.wikipedia.org/wiki/Responsive_web_design) (RWD) 网站/APP * 创建 [RESS](http://www.lukew.com/ff/entry.asp?1392) (基于服务端组件的响应式 Web 设计) 网站/APP * 创建 [自适应/渐进增强地](https://en.wikipedia.org/wiki/Adaptive_web_design) 网站/APP * 对每一个或每一组平台建立一套网站, Web 应用, 本地应用或混合应用 * 尝试修改你用策略1, 策略2或策略创建的应用. 这可能和点缀与屏幕大小无关的部分 UI 一样简单, 也可以试图完全支持其他平台与整个 UI. **入门学习:** * [自适应网页设计](http://adaptivewebdesign.info/) [read][$] * [设计与渐进增强](https://www.filamentgroup.com/dwpe/) [read] * [响应式排版](https://www.pluralsight.com/courses/responsive-typography) [watch][$] * [响应式 Web 设计](https://frontendmasters.com/courses/responsive-web-design/) [watch][$] * [响应式 HTML 邮件设计](https://frontendmasters.com/courses/responsive-email/) [watch][$] * [Designing Multi-Device Experiences: An Ecosystem Approach to User Experiences across Devices](http://www.amazon.com/Designing-Multi-Device-Experiences-Ecosystem-Approach/dp/1449340385/ref=pd_sim_14_8) [read][$] * [响应式 Web 设计原理](https://www.udacity.com/courses/web-development) [watch] * [响应式图片](https://www.udacity.com/course/responsive-images--ud882) [watch] * [移动 Web 开发](https://www.udacity.com/course/mobile-web-development--cs256) [watch] **译者补充:** * [移动 Web 调试工具: weinre](https://people.apache.org/~pmuellr/weinre-docs/latest/) * [Responsive demos & tutorials](http://navnav.co/) * [打造最舒适的 Webview 调试环境](https://github.com/riskers/blog/issues/11) ================================================ FILE: 02-fedHandlebook-master/learning/networks.md ================================================ ###HTTP/网络(包括 CORS 和 WebSockets) >**HTTP**, The Hypertext Transfer Protocol, 即超文本传输协议, 是一个用于分布式, 协作和超媒体信息系统的应用协议, 是 WWW 数据通信的基础. - Wikipedia >**CORS**, Cross-origin resource sharing, 即跨域资源共享, 是一种允许网页上受限制的资源(如: 字体)可以从该资源所在域之外的另一个域被请求. - Wikipedia >**WebSockets**, WebSocket 是一种协议, 提供了在一个 TCP 连接上进行全双工通信的渠道. 在 2011 年, IETF 将 WebSocket 协议标准化为 RFC 6455, 并且 W3C 正在标准化 Web IDL 的WebSocket API. - Wikipedia **HTTP** * [HTTP: 每个 Web 开发者必须知道的协议(一)](http://code.tutsplus.com/tutorials/http-the-protocol-every-web-developer-must-know-part-1--net-31177) [read] * [HTTP: 每个 Web 开发者必须知道的协议(二)](http://code.tutsplus.com/tutorials/http-the-protocol-every-web-developer-must-know-part-2--net-31155) [read] * [HTTP 基本原理](http://www.pluralsight.com/courses/xhttp-fund) [watch][$] * [HTTP 浅谈](http://code.tutsplus.com/series/http-succinctly--net-33683) [read] * [高性能网络浏览: 每个 Web 开发人员都应该了解网络和网络性能](http://chimera.labs.oreilly.com/books/1230000000545/index.html) [read] **CORS** * [60秒内的 HTTP 状态码变化](http://webdesign.tutsplus.com/tutorials/http-status-codes-in-60-seconds--cms-24317) [watch] * [HTTP 访问控制 (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) [read] * [CORS 实战](https://www.manning.com/books/cors-in-action) [read][$] **WebSockets** * [WebSocket: 轻量级的 C/S 通信](http://www.amazon.com/WebSocket-Client-Server-Communications-Andrew-Lombardi/dp/1449369278/ref=sr_1_1) [read][$] * [WebSocket 协议](https://tools.ietf.org/html/rfc6455) [read] * [用 WebSockets 连接 Web](https://code.tutsplus.com/courses/connect-the-web-with-websockets) [watch] 译者补充: * [HTTP/2 资料汇总](https://imququ.com/post/http2-resource.html) * [HTTP 权威指南](http://www.amazon.cn/gp/product/B008XFDQ14?keywords=http&qid=1445943752&ref_=sr_1_1&sr=8-1) [read][RMB] * [图解 HTTP](http://www.amazon.cn/gp/product/B00JTQK1L4?keywords=http&qid=1445943752&ref_=sr_1_2&sr=8-2) [read][RMB] * [图解 TCP/IP](http://www.amazon.cn/gp/product/B00DMS9990?keywords=http&qid=1445943752&ref_=sr_1_3&sr=8-3) [read][RMB] ================================================ FILE: 02-fedHandlebook-master/learning/news-podcasts.md ================================================ ###前端简报, 资讯网站 & 博客 **综合的前端简报, 资讯 & 博客** * [shoptalkshow.com](http://shoptalkshow.com/) * [frontendfront.com](http://frontendfront.com/) * [webtoolsweekly.com](http://webtoolsweekly.com/) * [O'Reilly Web Platform Radar](http://radar.oreilly.com/web-platform) * [The Web Platform Podcast](http://radar.oreilly.com/web-platform) * [The Web Platform Podcast](http://thewebplatform.libsyn.com/) * [The Web Ahead](http://thewebahead.net/) * [The Big Web Show](http://5by5.tv/bigwebshow) * [Fresh Brewed Frontend](https://freshbrewed.co/frontend/) * [Mobile Web Weekly](http://mobilewebweekly.co/) * [Open Web Platform Daily Digest](http://webplatformdaily.org/) * [FRONT-END DEV weekly](http://frontenddevweekly.com/) * [Web Design Weekly](https://web-design-weekly.com/) * [Front-end News in 5 Minutes](https://frontendfive.codeschool.com/) * [TTL](http://ttlpodcast.com/) * [Viewsources](https://viewsourc.es/) * [Web Components Weekly](http://webcomponentsweekly.me/) **HTML/CSS 简报:** * [HTML 5 Weekly](http://html5weekly.com/) * [CSS Weekly](http://css-weekly.com/archives/) **JavaScript 简报, 资讯 & 博客:** * [Javascript Jabber](https://devchat.tv/js-jabber/) * [JavaScript Weekly](http://javascriptweekly.com/) * [Echo JS](http://www.echojs.com/) * [JavaScript Kicks](http://javascriptkicks.com/) * [javascript.com](https://www.javascript.com/news) * [FiveJS](https://fivejs.codeschool.com/) * [JavaScript Live](https://jslive.com/) **图形和动画简报 & 博客:** * [Motion and Meaning](http://motionandmeaning.io/) * [SVG Immersion Podcast](http://svgimmersion.com/) * [Web Animation Weekly](http://rachelnabors.us1.list-manage.com/subscribe?u=0a8f219cf8284562f91a26ee9&id=d60f6683d2) * [Responsive Images Community Group Newsletter](https://responsiveimages.org/#newsletter) * **译者补充:** * [奇舞团](http://www.75team.com/weekly/) * [FEX](http://fex.baidu.com/weekly/) * [开发者头条](http://toutiao.io/) * [码农周刊](http://weekly.manong.io/issues/) * [OurJS](http://ourjs.com/) * [编程狂人](http://www.tuicool.com/mags) * [一周拾遗](http://www.tuicool.com/articles/weekly) * [设计匠艺](http://www.tuicool.com/mags/design) ================================================ FILE: 02-fedHandlebook-master/learning/node.md ================================================ ###Node.js >Node.js 用于开发服务端 Web 应用, 是一个开源的, 跨平台的运行时环境. Node.js 应用由 JavaScript 编写, 可以在 OS X, Microsoft Windows, Linux, FreeBSD, NonStop, IBM AIX, IBM System z and IBM i 上的 Node.js 运行时环境运行. Node.js 的开发和维护有 Node.js 基金会提供支持, 这是 Linux 基金会的一个合作项目. >Node.js 提供一个事件驱动的体系架构和非阻塞的 I/O 设计来优化应用程序的吞吐量和实时web应用程序的可伸缩性, 它使用谷歌的 V8 JavaScript 引擎来执行代码, 并有大量的由 JavaScript 编写的基础模块. Node.js 包含内置的模块, 允许应用程序作为一个web服务器而不依赖类似 Apache HTTP Server, Nginx 或 IIS 的软件. - wikipedia **入门学习:** * [从事件角度介绍 Node.js](https://www.eventedmind.com/classes/introduction-to-node-js-4c0326de) [watch][$] * [Node 的艺术](https://github.com/maxogden/art-of-node#the-art-of-node) [read] * [Node.js 基础](http://teamtreehouse.com/library/nodejs-basics) [watch][$] * [io.js 和 Node.js 入门](http://www.pluralsight.com/courses/running-node-applications-io-js) [watch][$] * [全面学习 Nodes](https://learnallthenodes.com/episodes/1-what-is-nodejs) [watch] * [Node 初学者书籍](https://leanpub.com/nodebeginner) [read][$] * [Node.js 介绍](http://www.pluralsight.com/courses/node-intro) [watch][$] * [Node.js 实战](https://www.manning.com/books/node-js-in-practice#downloads) [read][$] * [Nodeschool.io](http://nodeschool.io/) [code] **译者补充:** * [Node.js 经典入门教程(中文版)](http://nodebeginner.org/index-zh-cn.html) * [从零开始学 Node.js 系列文章](http://blog.fens.me/series-nodejs/) * [Node.js 入门](https://cnodejs.org/getstart) * Node.js 中文文档: [1](https://davidcai1993.gitbooks.io/nodejs-api-doc-in-chinese/content/) [2](http://wiki.jikexueyuan.com/project/nodejs/) [3](http://www.nodeapp.cn/) * [Node.js 风格指南](https://github.com/wwsun/node-style-guide) * Express 中文文档: [1](http://www.expressjs.com.cn/) [2](http://expressjs.jser.us/) * [Express 指南](http://wiki.jikexueyuan.com/project/express-guide/) * Koa中文文档: [1](http://koa.rednode.cn/) [2](http://koa.bootcss.com/) [3](https://github.com/guo-yu/koa-guide) [4](http://koajs.cn/) * [NodeCloud](http://www.nodecloud.org/) * [Node面试题](https://github.com/jimuyouyou/node-interview-questions) * [Node.js 中文资料导航](https://github.com/youyudehexie/node123) * [Node.js 开发常用资源(awesome-nodejs)1](https://github.com/sindresorhus/awesome-nodejs) [开发资源2](https://github.com/lyfeyaj/awesome-resources#nodejs) [开发资源3](https://github.com/vndmtrx/awesome-nodejs) * [Node-books](https://github.com/pana/node-books) ================================================ FILE: 02-fedHandlebook-master/learning/offline.md ================================================ ###离线开发 离线开发(又称离线优先)是一个领域常识和围绕设备并不总是连接到互联网或电源的开发实践的讨论. **综合学习:** * [offlinefirst.org](http://offlinefirst.org) [read] * [HTML5 离线 Web 应用](http://apress.jensimmons.com/v5/pro-html5-programming/ch12.html) [read] * [离线优先](http://www.webdirections.org/offlineworkshop/ibooksDraft.pdf) [read] * [创建离线应用你需要知道的一切](https://github.com/pazguille/offline-first) [read] ================================================ FILE: 02-fedHandlebook-master/learning/optimizing.md ================================================ ###网站性能优化 >Web 性能优化, WPO, 或网站优化是提高用户浏览器的网站加载和显示速度的知识领域. 由于网速整体提高了, 很适合网站的管理者和维护者去考虑网站呈现给访问者所花费的时间了. - wikipedia **综合学习:** * [网站性能](https://frontendmasters.com/courses/website-performance/) [watch][$] * [高性能网站建设指南:前端工程师技能精髓](http://www.amazon.cn/%E9%AB%98%E6%80%A7%E8%83%BD%E7%BD%91%E7%AB%99%E5%BB%BA%E8%AE%BE%E6%8C%87%E5%8D%97-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%88%E6%8A%80%E8%83%BD%E7%B2%BE%E9%AB%93-%E5%8F%B2%E8%92%82%E5%A4%AB%C2%B7%E6%A1%91%E5%BE%B7%E6%96%AF/dp/B00XI979P4/ref=sr_1_1) [read][RMB] * [高性能网站建设进阶指南:Web开发者性能优化最佳实践](http://www.amazon.cn/%E9%AB%98%E6%80%A7%E8%83%BD%E7%BD%91%E7%AB%99%E5%BB%BA%E8%AE%BE%E8%BF%9B%E9%98%B6%E6%8C%87%E5%8D%97-Web%E5%BC%80%E5%8F%91%E8%80%85%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5-%E6%A1%91%E5%BE%B7%E6%96%AF%E3%80%80-%E5%8F%A3%E7%A2%91%E7%BD%91%E5%89%8D%E7%AB%AF%E5%9B%A2%E9%98%9F/dp/B0129346KK/ref=sr_1_1) [read][RMB] * [Web 性能实践日志](http://www.amazon.cn/Web%E6%80%A7%E8%83%BD%E5%AE%9E%E8%B7%B5%E6%97%A5%E5%BF%97-%E6%96%AF%E6%89%98%E6%89%AC/dp/B00K4RUL94/ref=sr_1_1) [read][RMB] * [JavaScript 性能宝石](http://javascriptrocks.com/) [read] * [性能日历](http://calendar.perfplanet.com/2014/) [read] * [页面速度见解规则](https://developers.google.com/speed/docs/insights/rules) [read] * [网站性能优化](https://www.udacity.com/course/website-performance-optimization--ud884) [watch] * [浏览器渲染优化](https://www.udacity.com/course/browser-rendering-optimization--ud860) [watch] * [Using WebPageTest](http://www.amazon.com/Using-WebPageTest-Rick-Viscomi/dp/1491902590/ref=sr_1_1) [read][$] * [Web 性能权威指南](http://www.amazon.cn/Web%E6%80%A7%E8%83%BD%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E6%A0%BC%E9%87%8C%E9%AB%98%E5%88%A9%E5%85%8B/dp/B00JMKWHFU/ref=sr_1_1) [read][RMB] * [perf.rocks](http://perf.rocks) **译者补充:** * [谷歌的性能优化指南](https://developers.google.com/web/fundamentals/performance/?hl=zh-cn) * [前端性能优化指南](https://github.com/kahn1990/web_performance_optimization) * [AlloyTeam: Web 性能优化专栏](http://www.alloyteam.com/2015/11/alloyteam-present-dry-event-countdown-2-days-performance-optimization-blog/) ================================================ FILE: 02-fedHandlebook-master/learning/package.md ================================================ ###包管理器 >包管理器或包管理系统是一系列软件工具的集合, 这些软件工具用和电脑操作系统一致的方式, 使应用的安装, 升级, 配置和删除软件包的过程自动化, 它通常维护一个数据库软件的依赖和版本信息, 防止软件不匹配和无法跟踪. - wikipedia **综合学习:** * [Bower 基本原理](http://www.pluralsight.com/courses/bower-fundamentals) [watch][$] * [包管理器: 前端开发人员入门指南](http://codylindley.com/techpro/2013_04_12__package-managers-an-introducto/) [read] * [NPM: 包的上传和运行](http://www.lynda.com/Developer-Web-Development-tutorials/Up-Running-NPM-Node-Package-Manager/409274-2.html) [watch][$] * [NPM 基础](http://teamtreehouse.com/library/npm-basics) [watch][$] * [NPM 书籍](ttps://leanpub.com/npm) [read] * [NPM & Bower: 依赖版本管理](http://developer.telerik.com/featured/mystical-magical-semver-ranges-used-npm-bower/) [read] **参考/文档:** * [Bower](http://bower.io/) * [jspm.io](http://jspm.io/) * [NPM](https://www.npmjs.com/) ================================================ FILE: 02-fedHandlebook-master/learning/react.md ================================================ ###React >**React** - 用于构建用户界面的JAVASCRIPT库 >**仅仅是UI** - 许多人使用React作为MVC架构的V层。 尽管React并没有假设过你的其余技术栈, 但它仍可以作为一个小特征轻易地在已有项目中使用 >**虚拟DOM** - React为了更高超的性能而使用虚拟DOM作为其不同的实现。 它同时也可以由服务端Node.js渲染 - 而不需要过重的浏览器DOM支持 >**数据流** - React实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。 **入门教程:** * [React 入门实例教程](http://www.ruanyifeng.com/blog/2015/03/react.html) * [React 介绍及实践教程](http://www.ibm.com/developerworks/cn/web/1509_dongyue_react/index.html) 此文中的demo实现: [demo](https://github.com/dwqs/react_practice/tree/master/react-started) * [React 入门教程](https://hulufei.gitbooks.io/react-tutorial/content/webpack.html) * [Learning React](https://github.com/yiminghe/learning-react) * [SurviveJS - Table of Contents](http://survivejs.com/webpack_react/) **进阶学习:** * [深入浅出 React 系列](http://www.infoq.com/cn/articles/react-art-of-simplity) * [React.js 生态系统概览](http://www.inkpaper.io/blog/post/2015/10/18/navigating-the-react-ecosystem.html) * [详解React Flux架构工作方式](http://www.csdn.net/article/2015-08-31/2825587-react-flux) * [Awesome React](https://github.com/enaqx/awesome-react) * [AlloyTeam: React 专栏](http://www.alloyteam.com/2015/11/alloyteam-event-countdown-3-days-dry-presenting-react-technology-blog/) **手册参考:** * [React 速查手册](http://ricostacruz.com/cheatsheets/react.html) * [React Cheat Sheet](http://reactcheatsheet.com/) * [React Style Guide](https://github.com/dwqs/react-style-guide) **工具:** * [Awesome Redux](https://github.com/xgrommx/awesome-redux) * [Webpack 中文文档](https://wohugb.gitbooks.io/webpack/content/) * [webpack学习笔记](http://blog.csdn.net/zhbhun/article/details/47208885) * [Webpack傻瓜式指南](https://github.com/vikingmute/webpack-for-fools) * [React Webpack Cookbook](https://fakefish.github.io/react-webpack-cookbook/) * [Webpack](https://github.com/ruanyf/webpack-demos) * [React Toolbox](http://react-toolbox.com/) **React Native:** * [React Native Lession](https://github.com/vczero/react-native-lession) * [Awesome React Native](https://github.com/jondot/awesome-react-native) * [React Native 探索系列](http://www.infoq.com/cn/articles/react-native-overview) * [React Native专题](http://www.jianshu.com/p/96febc4fec45) * [React Native 学习指南](https://github.com/ele828/react-native-guide) * [React Native 中文版](http://wiki.jikexueyuan.com/project/react-native/) ================================================ FILE: 02-fedHandlebook-master/learning/security.md ================================================ ###安全 * [浏览器安全手册](https://code.google.com/p/browsersec/wiki/Main) [read] * [HTML5 安全手册](https://html5sec.org/#javascript) [read] * [前端安全](https://mikewest.org/2013/09/frontend-security-frontendconf-2013) [watch] * [Security for Web Developers: Using JavaScript, HTML, and CSS](http://www.amazon.com/Security-Web-Developers-Using-JavaScript/dp/1491928646/ref=sr_1_11) [read][$] * [现代 Web 应用安全指南](http://lcamtuf.coredump.cx/tangled/) [read][$] * [Web 安全基础](https://github.com/vasanthk/web-security-basics) [read] ================================================ FILE: 02-fedHandlebook-master/learning/self-direct-learning.md ================================================ ###自主学习 这个部分集中于个人能用来指导自己作为前端开发者的学习进度的免费和付费资源(视频训练, 书籍等等). 这些资源包括免费的和付费的, 付费的资源是以美元为单位结算的. 作者认为, 任何有着正确的决心和奉献精神的人都能教自己如何成为一个前端开发者, 除了一台能连接到Web的电脑和用于付费视频训练, 书籍的现金, 其它都不需要. 下面是一些我通常推荐的视频学习资料(专注技术): * [Frontend Masters](https://frontendmasters.com/) * [pluralsight.com](http://www.pluralsight.com/) * [tutsplus.com](https://tutsplus.com/courses) * [lynda.com](http://www.lynda.com/) * [treehouse](https://teamtreehouse.com/) * [mijingo](https://mijingo.com/) * [codeschool.com](https://www.codeschool.com/) * [laracasts](https://laracasts.com/) * [eventedmind.com](https://www.eventedmind.com/) * [egghead.io](https://egghead.io/) * [codecademy.com](https://codecademy.com/) * [Khan Academy](https://www.khanacademy.org/computing/computer-programming) * [Tagtree](http://tagtree.tv/library) * [Udacity](https://www.udacity.com/courses/web-development) [careful, quality varies] **译者补充:** * [号称最全前端开发学习资源~~学下来能上天了](https://github.com/AutumnsWind/Front-end-tutorial) * [国外有哪些高质量js技术博客?](http://segmentfault.com/q/1010000002773179) * [你经常访问的技术社区或者技术博客(IT类)有哪些?](http://segmentfault.com/q/1010000000094981) * [国外的前端开发社区有哪些](http://segmentfault.com/q/1010000002899648) * [前端开发初学 书籍推荐](http://segmentfault.com/q/1010000002927558) * [MDN Web 技术文档](https://developer.mozilla.org/zh-CN/docs/Web) * [MSDN Web](https://msdn.microsoft.com/web-app-development-msdn) * [SegmentFault](http://segmentfault.com/) * [前端范](http://qianduanfan.com/) * [w3cplus](http://www.w3cplus.com/) * [w3ctech](http://www.w3ctech.com/) * [w3cfuns](http://www.w3cfuns.com/) * [w3help](http://www.w3help.org/zh-cn/kb/) * [jobbole](http://web.jobbole.com/) * [div.io](http://div.io/#/welcome) * [v2ex](https://www.v2ex.com/) * [CSDN](http://blog.csdn.net/web/newest.html) * [cnblogs](http://www.cnblogs.com/cate/108703/) * [博客园知识库](http://kb.cnblogs.com/list/1002/) * [前端乱炖](http://www.html-js.com/) * [慕课网](http://www.imooc.com/) * [极客学院](http://www.jikexueyuan.com/) * [网易云课堂](http://study.163.com/) * [The State of Web Type](http://stateofwebtype.com/) * [Frontend Dev Bookmarks](https://github.com/dypsilon/frontend-dev-bookmarks) ================================================ FILE: 02-fedHandlebook-master/learning/seo.md ================================================ ###SEO: 搜索引擎优化 >搜索引擎优化(SEO)是在自然的搜索结果中, 影响一个网站或者网页可见性的过程. 一般来说, 越靠前出现(或在搜索结果页中权重高的网页)的网页, 和频繁出现在搜索结果列表中的网站, 就能获取更多来自搜索引擎的用户. SEO 会定位于不同类型的搜索, 包括图片搜索, 本地搜索, 视频搜索, 学术搜索, 新闻搜索和特定行业的垂直搜索引擎. - wikipedia **综合学习:** * [David Booth: SEO 基本原理](http://www.lynda.com/Analytics-tutorials/SEO-Fundamentals/187858-2.html) [watch][$] * [Paul Wilson: SEO 基本原理](http://www.pluralsight.com/courses/seo-fundamentals) [watch][$] * [SEO: Web 设计者](https://webdesign.tutsplus.com/courses/seo-for-web-designers) [watch][$] * [谷歌搜索引擎优化指南](http://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf) [read] * [SEO 优化指导](http://www.hobo-web.co.uk/seo-tutorial/) [read] ================================================ FILE: 02-fedHandlebook-master/learning/static.md ================================================ ###静态网页生成器 静态网页生成器, 是使用服务器端代码编写(如: ruby, php, python, nodeJS 等...), 用静态文本数据 + 模板, 生成从服务器发送到客户端的静态 HTML 文件. **综合学习:** * [静态网页生成器](http://www.oreilly.com/web-platform/free/static-site-generators.csp) [read] ================================================ FILE: 02-fedHandlebook-master/learning/test.md ================================================ ###JS 测试 >**单元测试** -  在计算机程序中, 单元测试是一种软件测试方法, 通过独立的代码单元, 一个或多个计算机程序模块的集合, 和相关联的控制数据, 使用程序和操作过程进行测试, 以确定它们是否适合使用. 直观地说, 可以将一个单元视为应用程序最小的, 可测试的一部分. - Wikipedia >**功能测试** - 功能测试是一个质量保证(QA)的过程, 也是一种基于在软件组件测试规范之下的测试案例的黑盒子测试. 功能测试会检查程序的输入和输出, 但很少去考虑内部地程序结构(跟白盒子测试不同). 功能测试通常描述了系统"做什么". - Wikipedia >**集成测试** - 在软件测试中, 集成测试(也称集成和测试, 缩写为 I&T)是各个软件模块结合起来, 作为一个整体进行测试的阶段, 之后便是单元测试和验证测试. 集成测试把已经通过的单元测试作为输入模块, 将它们组织成更大的聚集, 在集成测试中应用计划用于这些聚集而被定义的测试, 并提供为集成测试做好准备的集成系统作为其输出. - Wikipedia **综合学习:** * [测试驱动的 JavaScript 开发](http://www.amazon.cn/%E6%B5%8B%E8%AF%95%E9%A9%B1%E5%8A%A8%E7%9A%84JavaScript%E5%BC%80%E5%8F%91-%E7%BA%A6%E7%BF%B0%E6%A3%AE/dp/B0077KA3J4/ref=sr_1_1) [read][RMB] * [编写可测试的 JavaScript 代码](http://www.amazon.cn/%E7%BC%96%E5%86%99%E5%8F%AF%E6%B5%8B%E8%AF%95%E7%9A%84JavaScript%E4%BB%A3%E7%A0%81-%E7%BE%8E-Mark-Ethan-Trostler-%E6%89%98%E6%96%AF%E5%8B%92/dp/B00SNCI018/ref=sr_1_1) [read][RMB] * [JavaScript Testing Recipes](http://jstesting.jcoglan.com/) [read][$] * [前端优先: JavaScript 的测试和原型设计](http://www.pluralsight.com/courses/testing-and-prototyping-javascript-apps) [watch][$] * [测试驱动的 JavaScript](http://www.letscodejavascript.com/) [watch][$] ================================================ FILE: 02-fedHandlebook-master/learning/version.md ================================================ ###版本控制 >软件配置管理, 版本控制的一个组成部分, 也称为校正控制或源码控制, 用于管理文档, 计算机程序, 大型网站和其它信息集合的变化. 变化通常被定义为一串数字或字母代码, 被称为 "版本编号", "版本标识", 或简称"版本". 举个例子, 初始的文件集合是"版本1", 当第一个改变文件时, 就变成了"版本2"等等. 每一个版本都和一个时间戳和做出改变的人联系在一起. 版本可以被比较, 恢复和跟其它文件合并. - wikipedia **入门学习:** * [codeschool.com](https://try.github.io/levels/1/challenges/1) [interact] * [Git 基本原理](http://www.pluralsight.com/courses/git-fundamentals) [watch][$] * [正确使用 Git](https://www.atlassian.com/git/) [read] * [Git 介绍](http://rypress.com/tutorials/git/introduction) [read] * [Git 权威指南](http://git-scm.com/book/en/v2) [read] **进阶学习:** * [Git 进阶](https://www.atlassian.com/git/tutorials/advanced-overview/) [read] * [Git 权威指南](http://git-scm.com/book/en/v2) [read] **参考/文档:** * [https://git-scm.com/doc](https://git-scm.com/doc) 译者补充: * [Pro Git 教程](http://acoder.cc/git.html) * [Git 教程](http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) * [git-scm(英)](https://git-scm.com/) [中文版](https://git-scm.com/book/zh/v2) * [my-git](https://github.com/xirong/my-git) ================================================ FILE: 02-fedHandlebook-master/learning/web-hosting.md ================================================ ###Web 主机 >Web 主机是一种网络托管服务, 允许万维网访问个人和组织他们的网站, 由拥有服务器的企业提供空间, 或者租赁给客户使用, 并提供网络连接. Web 主机也能提供数据中心空间和连接到互联网上位于数据中心的其他服务器, 称为主机托管. - wikipedia ![web-hosting](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/web-host.jpg) **通用知识** * [Web Hosting Beginner Guide](http://www.webhostingsecretrevealed.net/web-hosting-beginner-guide/) [read] * [Web Hosting For Dummies](http://www.dummies.com/store/product/Web-Hosting-For-Dummies.productCd-1118540573.html) [read][$] * [Ultimate Guide to Web Hosting](http://www.whoishostingthis.com/resources/web-hosting/) [read] ================================================ FILE: 02-fedHandlebook-master/learning.md ================================================ ###第二部分: 学习 第二部分为成为一个前端开发者提供自主学习和指导学习的资源. 注意, 仅需要学习被列举出的资源, 或者一个类别的学习记录, 因为我不建议一个前端开发人员学习所有东西, 这是非常荒谬的. 选择自己行业内的专业知识, 我会尽可能让你掌握它. **译者补充:** * [前端开发笔记本](https://li-xinyang.gitbooks.io/frontend-notebook/content/) * [前端开发规范](http://zhibimo.com/read/Ashu/front-end-style-guide/) * [适用于小团队的前端规范](https://github.com/hzlzh/Front-End-Standards) * [无线 Web 开发浅谈](http://am-team.github.io/amg/dev-exp-doc.html#无线web开发简介) * [如何跟上前端开发的最新前沿](https://uptodate.frontendrescue.org/zh/) * [Engineering Blogs](https://github.com/kilimchoi/engineering-blogs) * [大前端工具集](https://github.com/nieweidong/fetool) * [前端收集](https://github.com/foru17/front-end-collect) * [Front-end Dev Bookmarks](https://github.com/dypsilon/frontend-dev-bookmarks) * [Github 资源收集](http://segmentfault.com/a/1190000003510001) * [github上值得关注的前端项目](http://segmentfault.com/a/1190000002804472) * [前端资源库](http://www.awesomes.cn/) * [前端人的俱乐部](http://f2er.club/) ================================================ FILE: 02-fedHandlebook-master/practice/fd-dev-for.md ================================================ ###前端开发做什么 一个前端开发者能在下面的操作系统列(又称: OS)表中之一上手写运行在Web平台(如: 浏览器)之上的 HTML, CSS 和 JS: * Windows * Windows Phone * OSX * iOS * Android * Ubuntu (or Linux) * Chromium 操作系统运行在下面中的一个或者多个设备之上: * Desktop computer * Laptop / Netbook computer * Mobile phone * Tablet * TV * Watch * Things (任何你能想到的, 汽车, 冰箱, 灯光, 温控器等) ![front-end dev for what](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/fd-devs-for.jpeg) 一般来说, 前端技术通过使用下列运行时场景, 能运行在前面提到的操作系统和设备之上: * 运行在 OS 上的Web浏览器(如: [Chrome, IE, Safari, Firefox](http://outdatedbrowser.com/en)) * 运行在 OS 上并由 CLI 驱动的 [headless浏览器](https://en.wikipedia.org/wiki/Headless_browser)(如: [plantomJS](http://phantomjs.org/)) * 一个[Web视图](http://wiki.awesomium.com/general-use/introduction-to-web-views.html)/嵌入本机程序的浏览器Tab(当做 iframe)作为运行时环境, 作为与本机 API 通信的桥梁. 典型的Web视图应用包括一个由Web技术(HTML, CSS, 和 JS)构建的 UI.(如: [Apache Cordova](https://cordova.apache.org/), [NW.js](http://nwjs.io/), [Electron](http://electron.atom.io/)) * 一个由Web技术创建的本机程序会在运行时作为与本机 API 通信的桥梁, 被解释执行, UI 将使用本机的UI部分(如: IOS 本机控制)而不是Web技术控制([NativeScript](https://www.nativescript.org/), [React Native](https://facebook.github.io/react-native/)). ================================================ FILE: 02-fedHandlebook-master/practice/front-end-interview.md ================================================ ###前端面试 你可能被问到的问题: * [前端工作面试问题](http://h5bp.github.io/Front-end-Developer-Interview-Questions/) * [前端开发面试问题](http://thatjsdude.com/interview/index.html) * [每个 JavaScript 开发者应该知道的 10 个面试问题](https://medium.com/javascript-scene/10-interview-questions-every-javascript-developer-should-know-6fa6bdf5ad95) * [前端测验](http://davidshariff.com/quiz/) * [JavaScript 测验](http://davidshariff.com/js-quiz/) 你可以问的问题: * [一个开源的开发人员可以向潜在雇主提问的问题列表](https://github.com/ChiperSoft/InterviewThis) 译者补充: * [前端开发面试题大收集](https://github.com/paddingme/Front-end-Web-Development-Interview-Question) * [前端开发面试问题及答案整理](https://github.com/hawx1993/Front-end-Interview-questions) * [收集的前端面试题和答案](https://github.com/qiu-deqing/FE-interview) * [写给前端面试者](http://www.w3cplus.com/css/write-to-front-end-developer-interview.html) * [Awesome Interviews](https://github.com/MaximAbramchuck/awesome-interviews) ================================================ FILE: 02-fedHandlebook-master/practice/front-end-jobs-titles.md ================================================ ###前端的工作职称 下面是一个前端开发者在职业发展中各种职称的描述列表. 对于前端开发者最普遍的职称是 "前端开发者" 或者 "前端工程师", 可以根据任何包含 "前端", "客户端", "web UI", "CSS", "HTML" 和 "JavaScript" 的职称推断一个人对 HTML, CSS 和 JavaScript 的了解程度. **** **前端开发者/工程师** (又称作 Web前端开发者/工程师, 客户端开发者/工程师, 前端软件开发者/工程师 或 UI 工程师) 这是通用的职称, 用于描述一个开发者对 HTML, CSS, JavaScript 有很熟练的掌握, 并能在Web平台上应用这些技术. **** **CSS/HTML开发者** 这个职称用于描述一个开发者精通于 CSS 和 HTML, 但是对 JavaScript 和应用不熟悉. **** **前端JavaScript(可选, 应用程序)开发人员** 当职称中包含 "JavaScript应用程序" 时, 这就表示此开发人员是一个拥有高级编程, 软件开发和应用程序开发技能(如: 有构建前端应用程序的实践经验)的高级 JavaScript 开发者. **** **前端Web设计师** 当职称中包含 "设计师" 时, 这就表示此设计师不仅拥有前端技能(如: HTML & CSS), 还拥有专业的设计技能(视觉设计和交互设计). **** **Web/前端用户界面(又称 UI)开发者/工程师** 当职称中包含 "界面" 或者 "UI" 时, 这就表示此开发人员除了掌握前端技能, 还拥有交互设计技能. **** **移动/平板前端开发者** 当职称中包含 "移动" 或者 "平板" 时, 这就表示此开发人员在开发运行在移动或平板设备上的前端应用(或本机程序, 或运行在Web平台, 例如浏览器)很有经验. **** **前端 SEO 专家** 当职称中包含 "SEO" 时, 这就表示此开发者对一个 SEO 策略定制前端技术有着丰富的经验. **** **前端可访问性专家** 当职称中包含 "可访问性" 时, 这就表示此开发者对定制支持可访问性要求和标准的前端技术有着丰富的经验. **** **前端开发运维** 当职称中包含 "开发运维" 时, 这就表示此开发者对软件开发实践与合作, 集成, 部署, 自动化和测量有着丰富的经验. **** **前端测试/QA** 当职称中包含 "测试" 或 "QA" 时, 这就表示此开发者对测试和软件管理, 包括单元测试, 功能测试, 用户测试和 A/B 测试有着丰富的经验. **** 注意, 若你在职称中碰到 "全栈" 或 类似于 "Web开发者" 一样的职位, 这些可能是雇主用来描述这个角色负责 Web/应用程序 开发的各个方面, 即包括前端和后端(还可能包括设计). 译者补充: * [谈前端工程师的职业规划](http://www.alloyteam.com/2015/04/talk-about-the-front-end-engineering-career-planning/) ================================================ FILE: 02-fedHandlebook-master/practice/front-end-jobs.md ================================================ ###前端工作版块 可以列出很多寻找技术工作的方法. 下面的有限列表是和寻找一份具体的前端工作最相关的资源: * [frontenddeveloperjob.com](http://frontenddeveloperjob.com/) * [authenticjobs.com](https://authenticjobs.com/#category=4) * [weworkremotely.com](https://weworkremotely.com/) * [jobs.github.com](https://jobs.github.com/) * [careers.stackoverflow.com](http://careers.stackoverflow.com/jobs?searchTerm=front-end) * [angularjobs.com](http://angularjobs.com/) * [jobs.emberjs.com](http://jobs.emberjs.com/) * [jobs.jsninja.com](http://jobs.jsninja.com/) * [css-tricks.com/jobs](https://css-tricks.com/jobs/) * [glassdoor.com](http://www.glassdoor.com/Job/front-end-developer-jobs-SRCH_KO0,19.htm?jobType=all) 译者补充: * [w3ctech](http://www.w3ctech.com/job) * [w3cfuns](http://www.w3cfuns.com/job.php) * [内推网](http://www.neitui.me/) * [拉勾网](http://www.lagou.com/) * [前端网](http://qianduan.cc/) * [CSDN](http://job.csdn.net/) * [竞鹿网](https://www.nextoffer.com/) * [100Offer](http://100offer.com/) * [SegmentFault](http://segmentfault.com/jobs) * [周伯通](http://www.jobtong.com/) * [内聘网](http://www.neipin.com/) * [博客园](http://job.cnblogs.com/) * [V2EX](http://www.v2ex.com/) * [简历模板](https://github.com/geekcompany/ResumeSample/blob/master/web.md) * [面试问题1](https://github.com/Enolak/Front-end-questions-to-the-interview-stage) [面试问题2](https://github.com/qiu-deqing/FE-interview) ================================================ FILE: 02-fedHandlebook-master/practice/front-end-skills.md ================================================ ###前端开发的技术栈 ![front-end dev skills](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/front-end-skills.png) 对于任何类型的前端开发人员, HTML, CSS, DOM, JavaScript, HTTP/URL 和浏览器利用是基本的技术要求. 对于HTML, CSS, DOM, JavaScript, HTTP/URL 和浏览器开发之外的, 一个前端开发者还应该掌握下面技术列表中的一个或多个: * Content Management System (内容管理系统, 又称 CMS) * Node.js * Cross-browser testing (跨浏览器测试) * Cross-platform testing (跨平台测试) * Unit Testing (单元测试) * Cross-device testing (跨设备测试) * Accessibility / WAI-ARIA (无障碍访问/无障碍富Internet应用程序) * Search Engine Optimization (搜索引擎优化, 又称 SEO) * Interaction or User Interface design (交互或用户设计) * User Experience (用户体验) * Usability (可用性/易用性) * E-commerce Systems (电子商务系统) * Portal Systems (门户系统) * Wireframing (框架) * CSS layout / Grids (CSS 布局/栅格系统) * DOM manipulation (e.g. jQuery) (DOM 操作) * Mobile Web Performance (移动Web性能) * Load Testing (载荷测试) * Performance Testing (性能测试) * Progressive Enhancement / Graceful Degradation (渐进增强/优雅降级) * Version Control (e.g. GIT) (版本控制) * MVC / MVVM / MV* (MV* 框架) * Functional Programming (函数式编程) * Data Formats (e.g. JSON, XML) (数据格式) * Data API's (e.g Restful API) (数据API) * Web Font Embedding (Web字体嵌入) * Scalable Vector Graphics (可伸缩向量图形, 又称 SVG) * Regular Expressions (正则表达式) * Content Strategy (内容策略) * Microdata / Microformats (微数据/微格式) * Task Runners, Build Tools, Process Automation Tools (任务管道, 构建工具, 过程自动化工具) * Responsive Web Design (响应式设计) * Object Oriented Programming (面向对象编程) * Application Architecture (应用架构) * Modules (模块) * Dependency Managers (依赖关系管理) * Package Managers (包管理) * JavaScript Animation (JavaScript 动画) * CSS Animation (CSS 动画) * Charts / Graphs (图表/图形) * UI widgets (UI工具集) * Code Quality Testing (代码质量测试) * Code Coverage Testing (代码覆盖测试) * Code Complexity Analysis (代码复杂度测试) * Integration Testing (集成测试) * Command Line / CLI (命令行/命令行界面) * Templating Strategies (模板策略) * Templating Engines (模板引擎) * Single Page Applications (单页应用) * XHR Requests (aka AJAX) (XHR 请求, 又称 AJAX) * Web/Browser Security (Web/浏览器安全) * HTML Semantics (HTML 语义化) * Browser Developer Tools (浏览器开发者工具) 译者补充: * [前端技能汇总](https://github.com/JacksonTian/fks) * [WebFrontEndStack](https://github.com/unruledboy/WebFrontEndStack) ![front-end-skills1](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/front-end-skill1.jpg) ![front-end-skills2](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/front-end-skill2.png) ================================================ FILE: 02-fedHandlebook-master/practice/front-end-team-role.md ================================================ ###团队中的前端 在一个设计和开发 Web网站, Web应用, 或者基于Web技术的本机应用的团队, 前端开发者是典型的只有一个人. (注意: 构建一切的开发者曾经被称为"Web网管", 但这些罕见的和神秘的开发人员现在被称为"全栈开发者"). 一个构建专业的Web网站或软件的最小化团队也应该包含下列角色: * 视觉设计 (字体, 颜色, 字距, 视觉概念&主题) * UI/交互设计/信息架构师 (制定框架, 指定所有用户交互, UI功能和结构信息) * 前端开发者 (写能够在客户端/设备上运行的代码) * 后端开发者 (写能够在服务端运行的代码) 这些角色是根据技能的覆盖排序的(后面的角色的技能会覆盖前面的). 一个前端开发者很擅长处理 UI/交互设计, 后端开发者也一样. 团队成员承担多个角色是很少见的. 一个大的团队可能包含下列角色, 而不是如上面所展示的: * 视觉设计 * UI/交互设计/信息架构师 * **SEO 策略师** * 前端开发者 * **开发-运维工程师** * 后端开发者 * **API 开发者** * **数据库管理员** * **QA 工程师/测试人员** ================================================ FILE: 02-fedHandlebook-master/practice/full-stack.md ================================================ ###全才神话 ![full-stack](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/full-stack.jpg) 这是个需要设计和开发一个web解决方案的角色, 不仅需要有很深厚的技术栈, 而且需要在视觉设计, UI/交互设计, 前端开发和后端开发有大量的经验. 任何可以以一个专业的水平承担这 4 个角色中的一个或多个的人(又称全才或全栈开发者)是很少的. 务实一点, 你应该寻求, 或者雇佣一个在其中一个角色堪称专家的人. 那些声称在一个或多个角色上是专家的人是非常罕见的, ![change-stack](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/stacks-change.jpg) ================================================ FILE: 02-fedHandlebook-master/practice/making-fd.md ================================================ ###如何成为前端开发者? ![making-fd](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/made-fd.png) 那么, 怎么才能成为一个前端开发者呢? 这个问题很复杂, 因为直到现在, 你也不能去一所大学获得前端工程师的学位, 并且我也很少听说 或者遇见通过编写专业地 HTML, CSS 和 JavaScript 来获得一个无用的计算机科学学位或平面设计学位. 事实上, 现在的大部分前端 开发者都是通过自学成为开发者和没有经过传统训练的程序员. 为什么会是这种情况呢? 前端开发人员不是一个视觉设计师或一个交互设计师, 设计学校不是磨练前端技能的地方; 前端开发者也不是一个受过传统教育地计算机科 学研究生, 传统教育并不专注于让一个人为前端开发做准备. 实际上, 在美国的高等教育系统中(例如大学), 紧跟传统教学方式可能会阻碍 一个人置身实践, 而前端开发最需要实际经验. 在今天, 如果你想成为一个前端开发者, 你可以自学或者参加一些不被认可的项目, 课程, 训练营和班级. 前端工程师会精巧地创建用户界面依赖的骨架. 有时, 他们要足够关注交互设计, 因为他们会编写 UI 交互的底层代码. 因此, 现在的许多 实践是前端工程师使用编程技巧达不到的, 但是, 从另一个方面来说, 和其它类型程序员转前端开发相比, 似乎有更多的设计师转前端开发者. 当然, 由于 JavaScript 已经成熟, 更多的受过传统教育的程序员愿意将他们的知识带到前端实践中. 你可能没有意识到前端开发人员并不 总是被认为是"真正的"程序员, 但是, 时代正待正在改变. 正如所有人说的那样, 我相信作为前端开发人员, 职业生涯道路是一个未知的过程. 我能说的是, 要成为一名前端工程师, 就必须知道和在一 个高层次的水平上使用 HTML, CSS 和 JavaScript, 也不会忽略交互设计或者传统编程所应该知道的技能. 实际上, 从我的经验来看, 最 好的前端开发者通常会掌握交互设计和基于 Web 平台(例如浏览器, HTML, CSS, DOM 和 JavaScript)的编程. 不管出于什么原因, 还有 很多关于前端开发的知识往往没被发现, 也就是说, 前端工程更像一些由自学的人组成的实践, 而不是一个直接对应有组织和认可的高等教育的教 学重心的领域. 如果我从现在开始决定成为一名前端开发人员, 我会努力按照下面所概括的过程进行学习. 学习过程中, 我会假设你是自己最好的老师. 1. 粗略了解 Web 是怎么工作的, 确保你知道域名, DNS, URL, HTTP, 网络, 浏览器, 服务器/服务托管, 数据库, JSON, API, HTML, CSS DOM 和 JavaScript. 了解这些的目的是确保你知道它们如何一起工作以及每部分用于做什么. 专注于高水平的前端架构概述. 从简单的网页 制作开始, 并简单学习一下 [本机 Web 应用(又称 SPA)](https://blog.andyet.com/2015/01/22/native-web-apps). 2. 学习 HTML, CSS, 可访问性和 SEO. 3. 学习 UI 设计模式的基本原理, 交互设计, 用户体验设计和可用性 4. 学习编程的基本原理 5. 学习 JavaScript 6. 学习 JSON 和 API 7. 学习 CLI/命令行 8. 学习软件工程实践(如: 应用设计/架构, 模板, Git, 测试, 监控, 自动化, 代码质量, 开发方法学) 9. 定制自己的工具箱 10. 学习 Node.js 当前端的 HTML/CSS 开发者和前端应用/JavaScript 开发者分离时, 你就快要结束这个学习过程. 关于学习的一个简短建议: 在学习抽象的技术之前, 先学习实际的底层技术. 先学 DOM, 再学 JQuery; 先学 CSS, 再学 SASS; 先学 HTML, 再学 HAML; 先学 JavaScript, 再学 coffeeScript; 先学 ES6 模板字符串, 再学 Handlebars; 先学 UI 模式, 再学 Bootstrap. 当你开始学习时, 你应该会 害怕事情隐藏的复杂性. Abstracts in the wrong hands can give the appearance of advanced skills, while all the time hiding the fact that a developer has an inferior understanding of the basics or underlying concepts. 正如我之前所建议的学习过程, 这本书的剩下部分为读者指明学习资源和工具. 这也假设你不仅要学习, 而且会将你所学到的知识和工具用于实践.一些人认为只 实践, 而其他人则建议只学习, 我建议你结合二者, 找到适合自己的方式, 但是一定要结合学习和实践! 因而不仅要阅读这本书, 而且要实践. 学习, 实践, 学习, 实践. 重复执行是因为事情变化太快, 这就是为什么学习技术的基本原理, 而不是抽象的技术是如此重要. 我在前文已经提到, 现在涌现出很多的非认证的前端编码教育/训练营, 这些成为前端开发者的途径也是由老师在课室(虚拟和实体)指导的课程, 遵循了从官方体系 (如: 教学大纲, 测验, 小测试, 项目, 团队项目, 成绩等)学习的传统风格, 我在这本书的学习指导部分提到了更多关于这方面的东西. 简单地说, Web 上有 一切你需要学习的东西(几乎没有成本), 然后, 如果你需要有人告诉你如何获取真正免费的东西, 并且对你的学习负责, 你可以考虑一个有组织的课程. 关于其他方面, 我不知道其他任何职业可以通过互联网连接和对知识的强烈愿望来免费获取要学习的东西. 如果你要马上开始学习, 我建议你看看下面一些关于前端开发实践的概述: * [前端指南](https://github.com/bendc/frontend-guidelines) [read] * [成为 Web 开发者](http://www.yellowshoe.com.au/standards) [read] * [Isobar 前端代码标准](http://isobar-idev.github.io/code-standards/) [read] * [Web 基本原理](https://developers.google.com/web/fundamentals) [read] * [前端课程](https://gist.github.com/stevekinney/03027e71aac341af14a2) [read] * [freeCodeCamp](http://freecodecamp.com/) [interact] * [Planing a Front-end JS Application](https://www.youtube.com/watch?v=q4zEGkjTBFA) [watch] * [So, You Want to be a Front-end Engineer](https://www.youtube.com/watch?v=Lsg84NtJbmI) [watch] * [Front End Web Development Career Kickstart](http://www.pluralsight.com/courses/front-end-web-development-career-kickstart) [watch][$] * [前端 Web 开发入门](http://www.pluralsight.com/courses/front-end-web-development-get-started) [watch][$] * [Front-End Web Development Quick Start With HTML5, CSS, and JavaScript](http://www.pluralsight.com/courses/front-end-web-app-html5-javascript-css) [watch][$] * [Web 开发介绍](https://frontendmasters.com/courses/web-development/) [watch][$] * [前端 Web 开发基本原理](https://www.udemy.com/foundations-of-front-end-development/) [watch][$] * [Lean Front-End Engineering](https://frontendmasters.com/courses/lean-front-end-engineering/) [watch][$] * [A Baseline for Front-End [JS] Developers: 2015](http://rmurphey.com/blog/2015/03/23/a-baseline-for-front-end-developers-2015/) [read] * [了解前端 Web 开发](https://teamtreehouse.com/tracks/front-end-web-development) [watch][$] * [前端开发精通](https://mijingo.com/products/bundles/front-end-dev-mastery/) [watch][$] * [没有学位的前端 Web 开发者](https://www.udacity.com/course/front-end-web-developer-nanodegree--nd001) [watch][$] **译者补充:** * [成为一名优秀的Web前端开发者](http://www.92fenxiang.com/140.html) * [What makes a good front end engineer?](http://www.92fenxiang.com/112.html) * [How to become a web developer](https://medium.com/@souporserious/how-to-become-a-web-developer-ba26b0d23d81) ================================================ FILE: 02-fedHandlebook-master/practice/salaries.md ================================================ ###前端薪资 在美国, 前端开发者的平均薪资是 [$75K](http://www.glassdoor.com/Salaries/front-end-web-developer-salary-SRCH_KO0,23.htm) 一个有经验的前端开发者可以去任何想去的地方生活(远程工作), 并且一年能赚的钱超过 $150k(访问 [angel.co](https://angel.co/jobs), 注册, 查找超过 $150k 的前端工作). ![front-end-salary](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/front-end-salary.png) ================================================ FILE: 02-fedHandlebook-master/practice/tech-employed-by-fd.md ================================================ ###前端开发者常用的网络技术 ![web-tech-employed](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/web-tech-employed.jpg) 前端开发者常用的网络技术如下: * Hyper Text Markup Language(超文本标记语言, 又称 HTML) * Cascading Style Sheets (层叠式样式表, 又称 CSS) * Document Object Model (文档对象模型, 又称 DOM) * JavaScript Programming Language (JavaScript编程语言, 又称: ECMAScript 6, ES6, JavaScript 2015) * Web API's (Web应用程序接口, 又称 HTML5 API 或浏览器 API) * Hypertext Transfer Protocol (超文本传输协议, 又称 HTTP) * Uniform Resource Locator's (统一资源定位符, 又称 URL) * JavaScript Object Notation (JavaScript对象表示, 又称 JSON) * Web Content Accessibility Guidelines (网页内容无障碍设计指南, 又称 WCAG) & Accessible Rich Internet Applications (富Internet应用程序的无障碍设计, 又称 ARIA) 根据相关的文档和规范, 这些技术定义如下. 作为一个比较, 你可以在 [platform.html5.org](https://platform.html5.org/) 上看到所有与Web相关的规范. **Hyper Text Markup Language(超文本标记语言, 又称 HTML)** >超文本标记语言, 通常被称为 HTML, 是被用于创建网页的标准标记语言. Web浏览器能将 HTML 文件渲染成可见的或者可听到的. HTML 随着线索提示, 语义化地描述了网站的结构, 使它成为一种标记语言, 而不是编程语言. - wikipedia.org 更多相关的文档/规范: * [W3C HTML5 规范](http://www.w3.org/TR/html5/) : HTML5 是WWW核心语言的主要修订 * [HTML 元素在线标准](https://html.spec.whatwg.org/multipage/semantics.html#semantics) * [HTML 在线语法](https://html.spec.whatwg.org/multipage/syntax.html#syntax) * [所有 W3C HTML 规范](http://www.w3.org/standards/techs/html#w3c_all) * [HTML 元素参考](https://developer.mozilla.org/en-US/docs/Web/HTML/Element) * [HTML 属性参考](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes) * [全局属性](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes) **Cascading Style Sheets (层叠式样式表, 又称 CSS)** >层叠式样式表(CSS)是用于描述外观和格式化标记语言编写的文档的样式表语言. 尽管经常被用来改变用 HTML 和 XHTML 编写的网页和用户界面的样式, 但也可用于任何 XML 文档, 包括纯 XML, SVG 和 XUL. 跟 JavaScript 和 HTML 一样, CSS是被大多数网站用于为Web应用程序创建富有吸引力的网页, 用户界面的一种基础技术, 也为许多移动应用程序创建用户界面. - wikipedia.org 更多相关的文档/规范: * [CSS 2.2规范](https://drafts.csswg.org/css2/) * [CSS 3选择器](http://www.w3.org/TR/css3-selectors/) * [所有 W3C CSS 规范](http://www.w3.org/Style/CSS/current-work#roadmap) * [CSS 参考](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) **Document Object Model (文档对象模型, 又称 DOM)** >文档对象模型用于代表和对象交互的HTML, XHTML 和 XML 文档, 是一种跨平台和语言无关性的约定. 每一份文档的所有节点被组织成一种树结构, 称为 DOM 树. DOM 对象通过使用对象上的方法被处理和操作, 一个 DOM 对象的公共接口被指定为它的应用程序编程接口(API). - wikipedia.org 更多相关的文档/规范: * [W3C DOM4](http://www.w3.org/TR/2014/WD-dom-20140204/) * [DOM 在线标准](https://dom.spec.whatwg.org/) * [DOM 3 事件规范](http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/) **JavaScript Programming Language (JavaScript编程语言, 又称: ECMAScript 6, ES6, JavaScript 2015)** >JavaScript 是一种高级的, 动态的, 无类型的和解释型的编程语言, 它已经在 ECMAScript 语言规范中被标准化. 跟 HTML 和 CSS 一样, JavaScript 是 WWW 内容生成的第三种必不可少的技术; 大多数的网会使用Javascript, 并且 Javascript 被所有现在Web浏览器支持. JavaScript 基于原型和函数优先的特点, 使它成为多范型的语言, 支持面向对象的, 命令式的, 和函数式编程风格. JavaScript 能提供 API 来处理文本, 数组, 日期和正则表达式, 但不包括任何 I/O, 如网络, 存储或图形工具, 对这些的依赖取决于宿主环境中嵌入了什么. - wikipedia.org 更多相关的文档/规范: * [ECMAScript 2015 语言规范](http://www.ecma-international.org/ecma-262/6.0/) **Web API's (Web应用程序接口, 又称 HTML5 API)** >当使用 JavaScript 为Web程序写代码时, 有很多不错的 API 是可以利用的. 下面列举了所有在Web APP 或网站开发中可能会用到的接口. - Mozilla 更多相关文档: * [Web API 接口](https://developer.mozilla.org/en-US/docs/Web/API) **Hypertext Transfer Protocol (超文本传输协议, 又称 HTTP)** >超文本传输协议是一个用于分布式, 协作和超媒体信息系统的应用协议, 是 WWW 数据通信的基础. - wikipedia.org 更多相关规范: * [Hypertext Transfer Protocol -- HTTP/1.1](https://tools.ietf.org/html/rfc2616) * [Hypertext Transfer Protocol version 2 draft-ietf-httpbis-http2-16](https://tools.ietf.org/html/draft-ietf-httpbis-http2-16) **Uniform Resource Locator's (统一资源定位符, 又称 URL)** >一个 URL (也称Web地址)是一个资源引用, 指定了资源在计算机网络和检索机制中的位置. 与之类似的概念是 Uniform Resource Identifier(URI), 尽管许多人认为两个术语可以互换使用, 但 URL 是统一资源标识符( URI )[3] 的具体类型. 一个 URL 意味着一种访问资源量的方式, 但这并不适用于 URI. [4][5]URL 不仅常用于引用一个网页(http), 也可用于文件传输(ftp), 邮件(mailto), 数据库访问(JDBC) 和许多其它应用. - wikipedia.org 更多相关规范: * [URL](http://www.w3.org/Addressing/URL/url-spec.txt) * [URL 在线标准](https://url.spec.whatwg.org/) **JavaScript Object Notation (JavaScript对象表示, 又称 JSON)** >JSON, 有时也称 JavaScript 对象表示, 是一种使用人类可读的文本传输由键值对组成的数据对象的开放格式. 对于异步浏览器/服务器通信(AJAJ), JSON 是主要的数据格式, 很大程度上代替了 XML(AJAX). 尽管最初是从 JavaScript 脚本语言衍生而来, 但是 JSON 是语言无关性的数据格式, 在许多编程语言中, 代码解析和生成 JSON 是很容易的. JSON 的格式最初是由 Douglas Crockford 指定的, 但目前却被描述成两种标准: RFC 7159 和 ECMA-404. ECMA 标准只允许被合法的语法语句描述, 而 RFC 则提供了一些语义化描述和安全考虑. JSON 的官方网络媒体类型 application / JSON, 扩展名是 .json. - wikipedia.org 更多相关规范: * [JSON 介绍](http://json.org/) * [JSON 数据交换格式](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) * [JSON API](http://jsonapi.org/) **Web Content Accessibility Guidelines (网页内容无障碍设计指南, 又称 WCAG) & Accessible Rich Internet Applications (富Internet应用程序的无障碍设计, 又称 ARIA)** >无障碍设计是指产品, 设备, 服务, 或者环境是为残疾人设计的. 无障碍设计的概念意味着与一个人的辅助技术(例如, 电脑屏幕阅读器)相兼容, 确保直接访问(即独立)和"间接访问". - wikipedia.org * [无障碍设计网络倡议 (WAI)](http://www.w3.org/WAI/) * [网页内容无障碍设计指南 (WCAG) 的目前状态](http://www.w3.org/standards/techs/wcag#w3c_all) * [富 Internet 应用程序的无障碍设计 (WAI-ARIA) 的目前状态](http://www.w3.org/standards/techs/aria#w3c_all) **译者补充:** * [Web 端开发常用资源](https://github.com/lyfeyaj/awesome-resources#web-%E5%89%8D%E7%AB%AF) * [Web 前端开发资源](http://hao.jobbole.com/?catid=67) ================================================ FILE: 02-fedHandlebook-master/practice.md ================================================ ###第一部分:前端开发实践 第一部分会大致描述前端工程的实践 ================================================ FILE: 02-fedHandlebook-master/styles/ebook.css ================================================ cite{ margin-top: -30px; font-size:10px; text-align: center; color: #666; } ================================================ FILE: 02-fedHandlebook-master/styles/website.css ================================================ cite{ margin-top: -30px; font-size:10px; text-align: center; color: #666; } ================================================ FILE: 02-fedHandlebook-master/tools/animation.md ================================================ ###动画工具 * [Velocity.js](http://julian.com/research/velocity/) * [snabbt.js](http://daniel-lundin.github.io/snabbt.js/) * [TweenJS](https://github.com/CreateJS/TweenJS) * [Dynamics.js](https://github.com/michaelvillar/dynamics.js) * [GreenSock-JS](https://github.com/greensock/GreenSock-JS) **Polyfills/shims:** * [web-animations-js](https://github.com/web-animations/web-animations-js) ================================================ FILE: 02-fedHandlebook-master/tools/app.md ================================================ ###APP(桌面, 移动, 平板等) 工具 **前端 App 框架:** * [AngularJS](https://angularjs.org/) & [Batarang](https://github.com/angular/angularjs-batarang) * [Backbone](http://backbonejs.org/) & [Marionette](http://marionettejs.com/) * [React](http://facebook.github.io/react/) & [React-router](https://github.com/rackt/react-router) & [Flux](http://facebook.github.io/flux/) & [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) * [Vue.js](http://vuejs.org/) & [vue-loader](https://github.com/vuejs/vue-loader) & [vue-router](https://github.com/vuejs/vue-router) * [Ember](http://emberjs.com/) & [Ember Inspector](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi?hl=en) * [Aurelia](http://aurelia.io/) * [Polymer](https://www.polymer-project.org/1.0/) & [Iron Elements](https://elements.polymer-project.org/browse?package=iron-elements) & [Paper Elements](https://elements.polymer-project.org/browse?package=paper-elements) **全栈 JS App 平台:** * [Meteor](https://www.meteor.com/) * [Hood.ie](http://hood.ie/intro/) * [MEAN](http://meanjs.org/) **移动 Web UI/网站/App 框架:** 这些解决方案可以被用到任何地方, 包括 Web 视图(Web 平台和浏览器引擎等) APP. * [Ratchet](http://goratchet.com/getting-started/) * [Kendo UI Mobile](http://demos.telerik.com/kendo-ui/m/index) * [Mobile Angular UI](http://mobileangularui.com/) * [Framework7](http://www.idangero.us/framework7) **本机混合移动 webview 框架:** 典型解决方案是使用 [Cordova](https://cordova.apache.org/), [crosswalk](https://crosswalk-project.org/), 或者自定义 Webview 作为本机 API 的桥梁. * [ionic](http://ionicframework.com/) * [onsen.io](http://onsen.io/) **本机混合移动开发 webview 环境/平台/工具:** 典型解决方案是使用 [Cordova](https://cordova.apache.org/), [crosswalk](https://crosswalk-project.org/), 或者自定义 Webview 作为本机 API 的桥梁. * [AppBuilder](http://www.telerik.com/appbuilder) [$] * [Monaca](https://monaca.io/) [$] * [Adobe PhoneGap](http://phonegap.com/) [$] * [kony](http://www.kony.com/products/mobility-platform) [$] * [ionic hub](http://ionic.io/) [free to $] * [Taco](http://taco.tools/) * [manifoldJS](http://manifoldjs.com/) * [cacoon](https://cocoon.io) [free to $] **本机桌面 webview 应用框架:** * [NW.js](https://github.com/nwjs/nw.js) * [Electron](http://electron.atom.io/) **本机移动应用框架 (又称 JavaScript 本机应用)** 解决方案不使用浏览器引擎或 Webview, 而是利用 JS 引擎作为运行环境去编译 JavaScript, 并能调用本机的 API. UI 则使用本机的 UI 组件进行构造. * [NativeScript](https://www.nativescript.org/) * [React Native](https://facebook.github.io/react-native/) * [tabris.js](https://tabrisjs.com/) [free to $] * [trigger.io](https://trigger.io/how-it-works/) [$] **参考:** * [todomvc.com](http://todomvc.com/) **App seeds/starters/boilerplates:** * [React Starter Kit](http://www.reactstarterkit.com/) * [Ember starter-kit](https://github.com/emberjs/starter-kit) * [NG^-starter](https://github.com/angular-class/NG6-starter) * [Angular 2 Webpack Starter](http://angularclass.com/angular2-webpack-starter/) * [hjs-webpack](https://github.com/henrikjoreteg/hjs-webpack#developing-on-multiple-devices-at-once) ================================================ FILE: 02-fedHandlebook-master/tools/bass.md ================================================ ###前端开发者的后端服务工具(又称 BASS: Back-end as a service) **数据/后端服务管理:** * [Firebase](https://www.firebase.com/index.html) [free to $] * [Parse](https://www.parse.com/) [free to $] * [kinvey](http://www.kinvey.com/) [free to $] * [Back&](https://www.backand.com/) [free to $] * [Pusher](https://pusher.com/) **用户服务管理:** * [UserApp](https://www.userapp.io/) [free to $] * [hull](http://www.hull.io/) [$] * [auth0](https://auth0.com) [$] ================================================ FILE: 02-fedHandlebook-master/tools/browser.md ================================================ ###浏览工具 **JS 浏览工具:** * [URI.js](http://medialize.github.io/URI.js/) * [platform.js](https://github.com/bestiejs/platform.js) * [history.js](https://github.com/browserstate/history.js) * [html2canvas](https://github.com/niklasvh/html2canvas) **参考工具(查看浏览器是否支持某特性)** * [caniusee.com](http://caniuse.com/) * [HTML5 Please](http://html5please.com/) * [HTML5 test](https://html5test.com/) * [Browserscope](http://www.browserscope.org/) * [webbrowsercompatibility.com](http://www.webbrowsercompatibility.com) * [iwanttouse.com/](http://www.iwanttouse.com/) * [Platform status](https://dev.modern.ie/platform/status/) * [Browser support for broken/missing images](http://codepen.io/bartveneman/full/qzCte/) * [Big JS-Compatibility-Table](http://compatibility.shwups-cms.ch/en/home) * [jscc.info](http://jscc.info/) * [What Web Can Do Today](https://whatwebcando.today/) **浏览器开发/调试工具:** * [Opera Dragonfly](http://www.opera.com/dragonfly/) * [Safari Web Inspector](https://developer.apple.com/safari/tools/) * [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools) * [Chrome Developer Tools(aka DevTools)](https://developers.google.com/web/tools/?hl=en) * [Per-Panel Documentation](https://developers.google.com/web/tools/chrome-devtools/#docs) * [Command Line API Reference](https://developers.google.com/web/tools/javascript/command-line/command-line-reference?hl=en) * [Keyboard & UI Shortcuts Reference](https://developers.google.com/web/tools/iterate/inspect-styles/shortcuts) * [Settings](https://developer.chrome.com/devtools/docs/settings) * [IE Developer tools(aka F12)](https://dev.modern.ie/platform/documentation/f12-devtools-guide/) * [vorlon.js](http://vorlonjs.com/) **同步浏览工具:** * [Browsersync](http://www.browsersync.io/) **浏览器编码工具(判断用户的浏览器是否支持某特性):** * [Modernizr](https://modernizr.com/) * [ES Feature Tests](https://featuretests.io/) **浏览器的各种 polyfills/shims:** * [webcomponents.js](https://github.com/WebComponents/webcomponentsjs) * [webshim](https://afarkas.github.io/webshim/demos/) * [HTML5 Cross Browser Polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills) * [console-polyfill](https://github.com/paulmillr/console-polyfill) * [socket.io](http://socket.io/) * [sockjs](https://github.com/sockjs/sockjs-client) **浏览器承载测试/自动化:** * [browserstack](https://www.browserstack.com) [$] * [browserling](https://www.browserling.com/)[$] * [Sauce labs](https://saucelabs.com/) [$] * [Selenium](http://www.seleniumhq.org/) * [CrossBrowserTesting.com](http://crossbrowsertesting.com/) **无壳浏览器:** * [PhantomJS](http://phantomjs.org/) * [slimerjd](http://slimerjs.org/) * [TrifleJS](http://triflejs.org/) **无壳浏览器的自动化工具:** * [nightwatchjs](http://nightwatchjs.org/) * [casperJS](http://casperjs.org/) * [Nightmare](https://github.com/segmentio/nightmare) * [gremlins.js](https://github.com/marmelab/gremlins.js) **浏览器 hacks:** * [browserhacks.com](http://browserhacks.com/) ================================================ FILE: 02-fedHandlebook-master/tools/browserdocs.md ================================================ ###DOC/API 浏览工具 用于浏览开发者文档和 API 文档的工具. * [devdocs.io](http://devdocs.io/) * [Dash](https://kapeli.com/dash) [OS X, iOS][$] * [Velocity](https://velocity.silverlakesoftware.com/) [Windows][$] * [Zeal](https://zealdocs.org/) [Windows, Linux] ================================================ FILE: 02-fedHandlebook-master/tools/charts.md ================================================ ###数据可视化工具 **JS 库:** * [d3](http://d3js.org/) * [sigmajs](http://sigmajs.org/) **部件 & 组件: * [Chart.js](http://www.chartjs.org/) * [C3.js](http://c3js.org/) * [Google Charts](https://developers.google.com/chart/interactive/docs/) * [chartist-jsj](https://github.com/gionkunz/chartist-js) * [amCharts](http://www.amcharts.com/) [$] * [Highcharts](http://www.highcharts.com/) [Non-commercial free to $] * [FusionCharts](http://www.fusioncharts.com/) [$] * [ZingChart](http://www.zingchart.com/) [free to $] * [Epoch](https://github.com/epochjs/epoch) **服务:** * [Datawrapper](https://datawrapper.de/) * [infogr.am](https://infogr.am) [free to $] * [plotly](https://plot.ly/) [free to $] * [ChartBlocks](http://www.chartblocks.com/) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/cms.md ================================================ ###内容管理托管/API工具 **API CMS 工具:** * [prismic.io](https://prismic.io/) [free to $] * [contentful](https://www.contentful.com/) [$] * [Cosmic JS](https://cosmicjs.com/) [free to $] **Hosted CMS tools:** * [LightCMS](https://www.lightcms.com) [$] * [Surreal CMS](http://www.surrealcms.com/) [$] * [Page Lime](http://www.pagelime.com/) [$] * [Cushy CMS](https://www.cushycms.com) [free to $] **Static CMS tools:** * [webhook.com](http://www.webhook.com/) ================================================ FILE: 02-fedHandlebook-master/tools/code-editor.md ================================================ ###了解代码编辑器 >源代码编辑器是一个文本编辑程序, 专门为编辑计算机程序源代码的程序员而设计的, 它可能是一个独立的应用程序或内置在集成开发环境(IDE)或web浏览器中. 源代码编辑器是最基本的编程工具, ,作为程序员的基本工作就是编写和编辑源代码. - Wikipedia 前端代码可以被一个简单的文本编辑应用程序(如: Notepad 或 TextEdit), 但是, 大多数前端人员使用专门为一种编程语言而设计的代码编辑器编辑. 可以这么说, 代码编辑器有各种各样的类型和大小. 选择一个编辑器是主观行为。选择一个, 学习它的使用,然后继续学习 HTML, CSS 和 JavasCript DOM. 但是, 我一直相信, 编辑器应该有如下特点: * 一份不错的关于如何使用编辑器的文档 * 报告 HTML, CSS 和 JavaScript 代码的质量 * 为 HTML, CSS 和 JavaScript 提供语法高亮 * 为 HTML, CSS 和 JavaScript 提供代码自动完成 * 通过插件的方式自定义编辑器架构 * 有大量的第三方仓库/插件社区, 能够用于自定义编辑器 * 轻量, 简单, 不耦合代码(不需要编辑代码等等) 我个人推荐将下列的插件和 [Sublime Text](http://www.sublimetext.com/) 一起使用: * [Package Control](https://packagecontrol.io/packages/Package%20Control) * [AutoFileName](https://packagecontrol.io/packages/AutoFileName) * [SublimeLinter](https://packagecontrol.io/packages/SublimeLinter) * [SublimeLinter-json](https://packagecontrol.io/packages/SublimeLinter-json) * [SublimeLinter-jslint](https://packagecontrol.io/packages/SublimeLinter-jshint) * [SublimeLinter-html-tidy](https://packagecontrol.io/packages/SublimeLinter-html-tidy) * [Side​Bar​Enhancements](https://packagecontrol.io/packages/SideBarEnhancements) * [Terminal](https://packagecontrol.io/packages/Terminal) * [Bracket​Highlighter](https://packagecontrol.io/packages/BracketHighlighter) * [Color Highlighter](https://packagecontrol.io/packages/Color%20Highlighter) * [CSS3](https://packagecontrol.io/packages/CSS3) * [HTMLAttributes](https://packagecontrol.io/packages/HTMLAttributes) * [StringEncode](https://packagecontrol.io/packages/StringEncode) * [HTML-CSS-JS Prettify](https://packagecontrol.io/packages/HTML-CSS-JS%20Prettify) Sublime 的学习资源: * [Sublime Productivity](https://leanpub.com/sublime-productivity) [read][$] * [Sublime Text Power User Book](https://sublimetextbook.com/) [read + watch][$] * [Sublime Text 3 From Scratch](http://www.pluralsight.com/courses/sublime-text-3-from-scratch) [watch][$] * [Perfect Workflow in Sublime Text 2](https://code.tutsplus.com/courses/perfect-workflow-in-sublime-text-2) [watch][requires login, but free] **代码编辑器:** * [Atom](https://atom.io/) * [Brackets](http://brackets.io/) * [Sublime Text](http://www.sublimetext.com/) [$] * [WebStorm](https://www.jetbrains.com/webstorm/whatsnew/) [$] * [Visual Studio Code](https://code.visualstudio.com/) **在线合作的代码编辑器:** * [jsbin.com](http://jsbin.com/) [free to $] * [jsfiddle.net](http://jsfiddle.net/) * [liveweave.com](http://liveweave.com/) * [es6fiddle.net](http://www.es6fiddle.net/) * [codepen.io](http://codepen.io/) [free to $] * [Plunker](http://plnkr.co/) **在线代码编辑器:** * [codeanywhere](https://codeanywhere.com) [free to $] * [Koding](https://koding.com) [free to $] * [Clound9](https://c9.io) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/coll.md ================================================ ###合作 & 交流工具 * [Slack](https://slack.com/) & [screenhero](https://screenhero.com/) [free to $] * [Skype](http://www.skype.com/) [free to $] * [Google Hangouts](https://hangouts.google.com/) **代码/Github 合作 & 交流:** * [Gitter](https://gitter.im) [free to $] **译者补充:** * [Gitup](http://gitup.co/) ================================================ FILE: 02-fedHandlebook-master/tools/css.md ================================================ ###CSS 工具 **桌面 & 移动应用 CSS 框架:** * [Semantic UI](http://semantic-ui.com/) * [Foundation](http://foundation.zurb.com/) * [Bootstrap](http://getbootstrap.com/) * [Metro UI](http://metroui.org.ua/) * [Pure.css](http://purecss.io/) * [Concise](http://concisecss.com/) * [Materialize](http://materializecss.com/) * [Material Design Lite(MDL)](http://www.getmdl.io/index.html) * [Base](http://getbase.org/) **移动应用 CSS 框架:** * [Ratchet](http://goratchet.com/) **CSS 重置:** >CSS 重置(或重置 CSS)是一个很小的, 被压缩的 CSS 规则集合, 用于重置所有 HTML 元素的样式. - [http://cssreset.com/](http://cssreset.com/) * [Eric Meyer’s “Reset CSS” 2.0](http://meyerweb.com/eric/tools/css/reset/) * [Normalize](https://necolas.github.io/normalize.css/) **Transpiling:** * [SASS/SCSS](http://sass-lang.com/) * [stylus](https://github.com/stylus/stylus) * [PostCSS](https://github.com/postcss/postcss) & [cssnext](http://cssnext.io/) * [rework](https://github.com/reworkcss/rework) & [myth](http://www.myth.io/) * [pleeease.io](http://pleeease.io/) **参考文档:** * [css3test.com](http://css3test.com/) * [css4-selectors.com](http://css4-selectors.com/) * [css3clickchart.com](http://css3clickchart.com) * [cssvalues.com](http://cssvalues.com) * [CSS TRIGGERS](http://csstriggers.com/) * [MDN CSS reference](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) * [overapi.com CSS cheatsheet](http://overapi.com/css/) **Linting/hinting:** * [CSS Lint](http://csslint.net/) * [stylelint](http://stylelint.io/) **代码格式化/美化:** * [CSScomb](https://github.com/csscomb/csscomb.js) * [cssfmt](https://github.com/morishitter/cssfmt) **优化:** * [csso](http://css.github.io/csso/) * [clear-css](https://github.com/jakubpawlowicz/clean-css) * [cssnano](http://cssnano.co/) **CSS 在线生成工具:** * [Ultimate CSS Gradient Generator](http://www.colorzilla.com/gradient-editor/) * [Enjoy CSS](http://enjoycss.com/) * [CSS matic](http://www.cssmatic.com/) * [patternify.com](http://patternify.com) * [patternizer.com](http://patternizer.com) * [CSS arrow please](http://cssarrowplease.com/) * [flexplorer](http://bennettfeely.com/flexplorer/) * [Flexbox Playground](https://scotch.io/demos/visual-guide-to-css3-flexbox-flexbox-playground) **CSS 架构:** * [oocss](http://oocss.org/) [read] * [SMACSS](https://smacss.com/) [read][$] * [Atomic Design](http://atomicdesign.bradfrost.com/) [read] **编写规范:** * [idiomatic-css](https://github.com/necolas/idiomatic-css) [read] * [CSS code guide](http://codeguide.co/#css) [read] * [cssguidelin.es](http://cssguidelin.es) [read] * [Google HTML/CSS Style Guide](http://google-styleguide.googlecode.com/svn/trunk/htmlcssguide.xml#General_Formatting) **本月 CSS 仓库在Github的趋势:** * [https://github.com/trending?l=css&since=monthly](https://github.com/trending?l=css&since=monthly) ================================================ FILE: 02-fedHandlebook-master/tools/data.md ================================================ ###前端数据存储工具 * [YDN-DB](http://dev.yathit.com/ydn-db/index.html) * [forerunner](http://forerunnerdb.com/) * [AlaSQL](http://alasql.org/) * [LokiJS](http://lokijs.org/#/) * [lovefiled](https://google.github.io/lovefield) * [Dexie.js](http://www.dexie.org/) * [localForage](http://mozilla.github.io/localForage/) * [pouchdb](http://pouchdb.com/) ================================================ FILE: 02-fedHandlebook-master/tools/deploy.md ================================================ ###部署工具 * [FTPLOY](http://ftploy.com/) [free to $] * [Travis CI](http://docs.travis-ci.com/) [free to $] * [codeship](https://codeship.com/) [free to $] * [Bamboo](https://www.atlassian.com/software/bamboo/) [$] * [Springloops](http://www.springloops.io/) [free to $] * [surge](https://surge.sh/) * [sync ninja](http://www.syncninja.com/) ================================================ FILE: 02-fedHandlebook-master/tools/diagram.md ================================================ ###图表工具 * [Cacoo](https://cacoo.com) [free to $] * [gliffy](https://www.gliffy.com/products/online/) [free to $] * [draw.io](https://www.draw.io) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/dom.md ================================================ ###DOM 工具 **DOM 库/框架:** * [jQuery](http://jquery.com/) * [Zepto.js](http://zeptojs.com) * [keypress](http://dmauro.github.io/Keypress/) * [clipboard.js](http://zenorocha.github.io/clipboard.js/) * [tether.io](http://tether.io/docs/welcome/) **DOM 性能分析:** * [DOMMonster](http://mir.aculo.us/dom-monster/) **参考文档:** * [DOM Browser Support](http://www.webbrowsercompatibility.com/dom/desktop/) * [DOM Events Browser Support](http://www.webbrowsercompatibility.com/dom-events/desktop/) * [HTML Interfaces Browser Support](http://www.webbrowsercompatibility.com/html-interfaces/desktop/) * [Events](https://html.spec.whatwg.org/#events-2) * [MDN Document Object Model (DOM)](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model) **DOM polyfills/shims:** * [dom-shims](https://github.com/necolas/dom-shims) * [Pointer Events Polyfill: a unified event system for the web platform](https://github.com/jquery/PEP) **虚拟 DOM:** * [jsdom](https://github.com/tmpvar/jsdom) * [virtual-dom](https://github.com/Matt-Esch/virtual-dom) ================================================ FILE: 02-fedHandlebook-master/tools/error.md ================================================ ###JavaScript 错误监控工具 * [Raygun](https://raygun.io) [$] * [errorception](https://errorception.com/) [$] * [sentry](https://getsentry.com/welcome/) [free to $] * [{track:js}](https://trackjs.com/) [$] ================================================ FILE: 02-fedHandlebook-master/tools/general-tools.md ================================================ ###常用前端开发工具 **开发工具:** * [screensiz.es](http://screensiz.es/) * [placehold.it](http://placehold.it/) * [codeKit](http://incident57.com/codekit/) * [prepros](https://prepros.io/) * [Browsersync](http://www.browsersync.io/) * [ish. 2.0.](https://github.com/bradfrost/ish.) * [Wraith](http://bbc-news.github.io/wraith/index.html) **在线代码编辑:** * [jsbin.com](http://jsbin.com/) * [jsfiddle.net](http://jsfiddle.net/) * [liveweave.com](http://liveweave.com/) * [es6fiddle.net](http://www.es6fiddle.net/) * [codepen.io](http://codepen.io/) * [Plunker](http://plnkr.co/) **查找工具:** * [stackshare.io](http://stackshare.io/) * [javascripting.com](http://www.javascripting.com/) * [built with](http://builtwith.com/) * [microjs.com](http://microjs.com/) * [The Tool Box](http://thetoolbox.cc/) * [unheap.com](http://www.unheap.com/) * [stylesheets.co](https://stylesheets.co/) ================================================ FILE: 02-fedHandlebook-master/tools/graphics.md ================================================ ###图形工具 **常见图形工具:** * [Two.js](http://jonobr1.github.io/two.js/#introduction) * [Fabric.js](http://fabricjs.com/) **画布:** * [Paper.js](http://paperjs.org/) * [EaselJS](https://github.com/CreateJS/EaselJS) **SVG:** * [svg.js](http://svgjs.com/) * [Snap.svg](http://snapsvg.io/) * [Raphaël](http://raphaeljs.com/) * [d3](http://d3js.org/) **Webgl:** * [three.js](http://threejs.org/) * [pixi.js](https://github.com/pixijs/pixi.js) ================================================ FILE: 02-fedHandlebook-master/tools/hosting.md ================================================ ###Web/云/静态主机托管工具 * [AWS](https://aws.amazon.com/websites/) [$] * [Heroku](https://heroku.com) [free to $] * [DigitalOcean](https://digitalocean.com) [$] * [Modulus](https://modulus.io/) [$] * [DIVSHOT](https://divshot.com) [free to $] * [netlify](https://www.netlify.com) [free to $] * [surge](https://surge.sh/) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/html.md ================================================ ###HTML 工具 **HTML 模板:** * [HTML5 Boilerplate](https://html5boilerplate.com/) * [Mobile boilerplate](https://html5boilerplate.com/mobile/) * [Web Starter Kit Boilerplate & Tooling for Multi-Device Development](https://developers.google.com/web/tools/starter-kit) * [dCodes](http://www.dcodes.net/2/docs/index.html) * [HTML5 Bones](http://html5bones.com/) * [Email-Boilerplate](https://github.com/seanpowell/Email-Boilerplate) * [Pears](http://pea.rs/) **HTML polyfill:** * [html5shiv](https://github.com/aFarkas/html5shiv) **Transpiling:** * [jade](http://jade-lang.com/) * [HAML](http://haml.info/) * [Markdown](http://daringfireball.net/projects/markdown/) **文档参考:** * [HTML Interfaces Browser Support](http://www.webbrowsercompatibility.com/html-interfaces/desktop/) * [HTML Entity Lookup](http://entity-lookup.leftlogic.com/) * [HTMLElement.info](http://htmlelement.info/) * [ELements](https://html.spec.whatwg.org/multipage/indices.html#elements-3) * [Elelemt Attributes](https://html.spec.whatwg.org/multipage/indices.html#attributes-3) * [HTML Arrows](http://htmlarrows.com/) **Linting/hinting:** * [html5-lint](https://github.com/mozilla/html5-lint) * [HTMLHint](http://htmlhint.com/) * [html-inspector](https://github.com/philipwalton/html-inspector) **HTML 优化:** * [HTML Minifier](http://kangax.github.io/html-minifier/) **HTML 在线生成工具:** * [tablesgenerator.com](http://www.tablesgenerator.com) **编写规范:** * [Principles of writing consistent, idiomatic HTML](https://github.com/necolas/idiomatic-html) * [HTML code guide](http://codeguide.co/#html) **Workflow:** * [Emmet](http://emmet.io/) **本月 HTML 仓库在Github的趋势:** * [https://github.com/trending?l=html&since=monthly](https://github.com/trending?l=html&since=monthly) ================================================ FILE: 02-fedHandlebook-master/tools/http.md ================================================ ###HTTP/网络工具 * [Charles](http://www.charlesproxy.com/) [$] * [Fiddler](http://www.telerik.com/fiddler) * [Postman](https://www.getpostman.com/) * [Chrome DevTools Network Panel](https://developers.google.com/web/tools/chrome-devtools/profile/network-performance/resource-loading) ================================================ FILE: 02-fedHandlebook-master/tools/js.md ================================================ ###JavaScript 工具 **JS 库:** * [lodash](http://lodash.com) * [underscore.js](http://underscorejs.org) * [Moment.js](http://momentjs.com/) * [string.js](http://stringjs.com/) * [Numeral.js](http://numeraljs.com/) * [accounting.js](http://openexchangerates.github.io/accounting.js/) * [xregexp.com](http://xregexp.com/) * [Math.js](http://mathjs.org/) * [wait](https://github.com/elving/wait) * [async](https://github.com/caolan/async) * [format.js](http://formatjs.io/) * [Chance](http://chancejs.com/) **编译转换 (ESX to ESX):** * [Babel](https://babeljs.io/) **JavaScript 兼容性检查:** * [jscc.info](http://jscc.info/) **代码检查:** * [jshint](http://jshint.com/) * [eshint](http://eslint.org/) * [JSlint](http://www.jslint.com/) * [jslinterrors.com](http://jslinterrors.com/) **单元测试:** * [Mocha](http://mochajs.org/) * [QUnit](http://qunitjs.com/) * [Jasmine](http://jasmine.github.io/) * [Jest](http://facebook.github.io/jest/) **单元测试的断言库:** * [should.js](http://shouldjs.github.io/) * [Chai](http://chaijs.com/) * [expect.js](https://github.com/Automattic/expect.js) **单元测试监控, 存根和模拟:** * [sinon.js](http://sinonjs.org/) **编码规范检查:** * [JSCS](http://jscs.info/) **代码格式化/美化:** * [jsfmt](http://rdio.github.io/jsfmt/) * [esformatter](https://github.com/millermedeiros/esformatter#esformatterformatstr-optsstring) * [js-beautify](http://jsbeautifier.org/) **性能测试:** * [jsperf](http://jsperf.com/) * [benchmark.js](http://benchmarkjs.com/) **可视化, 静态分析, 复杂性, 覆盖工具:** * [jscomplexity.org](http://jscomplexity.org/) * [istanbul](https://github.com/gotwarlost/istanbul) * [Blanket.js](http://blanketjs.org/) * [Coveralls](https://coveralls.io/) [$] * [Plato](https://github.com/es-analysis/plato) * [escomplex](https://github.com/jared-stilwell/escomplex) * [Esprima](http://esprima.org/) **优化:** * [UglifyJS 2](https://github.com/mishoo/UglifyJS2) **混淆:** * [Javascript 混淆器](http://www.javascriptobfuscator.com/) * [JScrambler](https://jscrambler.com/) [$] **在线代码编辑器:** * [jsbin.com](http://jsbin.com/) * [jsfiddle.net](http://jsfiddle.net/) * [es6fiddle.net](http://www.es6fiddle.net/) * [plnkr.co](http://plnkr.co/) **在线正则表达式编辑器/可视化工具:** * [regex101](https://regex101.com/) * [regexper](http://regexper.com/) * [debuggex](https://www.debuggex.com) * [RegExr](http://regexr.com/) **编码规范:** * [Node.js 规范指南](https://github.com/felixge/node-style-guide) * [JavaScript 编码原则](https://github.com/rwaldron/idiomatic.js) * [JavaScript 规范指南](http://airbnb.io/javascript/) **本月 JS 仓库在 Github 的趋势:** * [https://github.com/trending?l=javascript&since=monthly](https://github.com/trending?l=javascript&since=monthly) **NPM 上被依赖最多的包:** * [https://www.npmjs.com/browse/depended](https://www.npmjs.com/browse/depended) ================================================ FILE: 02-fedHandlebook-master/tools/json.md ================================================ ###JSON 工具 **JSON 在线编辑器:** * [JSONmate](http://jsonmate.com/) **JSON 查询工具:** * [DefiantJS](http://www.defiantjs.com/) * [ObjectPath](http://objectpath.org/) * [JOSN Mask](https://github.com/nemtsov/json-mask) **生成模拟 JSON 工具:** * [JSON Generator](http://www.json-generator.com/) * [Mockaroo](https://www.mockaroo.com/) **JSON API 在线模拟工具:** * [Mocky](http://www.mocky.io/) * [FillText.com](http://www.filltext.com) * [JSONPlaceholder](http://jsonplaceholder.typicode.com/) * [mackable.io](https://www.mockable.io/) **JSON API 本地模拟工具: * [json-server](https://github.com/typicode/json-server) **JSON 规范/模式:** * [json-schema.org](http://json-schema.org/) & [jsonschema.net](http://jsonschema.net) * [jsonapi](http://jsonapi.org/) ================================================ FILE: 02-fedHandlebook-master/tools/loader.md ================================================ ###模块/包加载工具 * [SystemJS](https://github.com/systemjs/systemjs) * [webpack](https://webpack.github.io/) * [Broeserify](http://browserify.org/) * [rollup.js](http://rollupjs.org/) ================================================ FILE: 02-fedHandlebook-master/tools/monitor.md ================================================ ###网站/APP 监控工具 **Uptime:** * [pingdom](https://www.pingdom.com/) [free to $] * [Uptime Robot](https://uptimerobot.com/) * [Uptrends](https://www.uptrends.com/) [$] **General:** * [New Relic](http://newrelic.com/) ================================================ FILE: 02-fedHandlebook-master/tools/offline.md ================================================ ###离线工具 * [upup](https://www.talater.com/upup/) * [offline.js](http://github.hubspot.com/offline/docs/welcome/) * [pouchdb](http://pouchdb.com/) * [hood.ie](http://hood.ie/) ================================================ FILE: 02-fedHandlebook-master/tools/perf.md ================================================ ###性能工具 **Reporting:** * [WEIGHTOF.IT](http://weightof.it/category/application-frameworks) * [Web Page Test](http://www.webpagetest.org/) * [GTmetrix](https://gtmetrix.com/) * [Speed Curve](https://speedcurve.com/) [$] * [Chrome Devtools Timeline](https://developers.google.com/web/tools/profile-performance/evaluate-performance/timeline-tool?hl=en) * [sitespeed.io](https://www.sitespeed.io) **JS tools:** * [ImageOptim-CLI](http://jamiemason.github.io/ImageOptim-CLI/) * [imagemin](https://github.com/imagemin/imagemin) **Budgeting:** * [performancebudget.io](http://www.performancebudget.io) ================================================ FILE: 02-fedHandlebook-master/tools/project.md ================================================ ###项目管理 & 代码托管 * [Github](https://github.com/) [free to $] * [Codebase](https://www.codebasehq.com/) [$] * [Bitbucket](https://bitbucket.org) [free to $] * [Unfuddle](https://unfuddle.com/) [$] * [Assembla](https://www.assembla.com) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/proto.md ================================================ ###原型和框架工具 **创建原型和框架:** * [Balsamiq Mockups](https://balsamiq.com) [$] * [Justinmind](http://www.justinmind.com) [$] * [UXPin](http://www.uxpin.com/) [free to $] **合作/展示:** * [InVision](http://www.invisionapp.com/) [free to $] * [myBalsamiq](https://www.mybalsamiq.com/) [$] * [conceptboard](https://conceptboard.com/) [free to $] ================================================ FILE: 02-fedHandlebook-master/tools/repo.md ================================================ ###模块/包仓库工具 * [NPM](https://www.npmjs.com/) * [Bower](http://bower.io/) * [jspm.io](http://jspm.io/) * [spmjs](http://spmjs.io) ================================================ FILE: 02-fedHandlebook-master/tools/scaffolding.md ================================================ ###脚手架工具 脚手架是指为整个应用生成一个初始化的模板, 而不是[生成访问数据库的代码](https://en.wikipedia.org/wiki/Scaffold_%28programming%29). * [Yeoman](http://yeoman.io/) * [Slush](http://slushjs.github.io/#/) ================================================ FILE: 02-fedHandlebook-master/tools/security.md ================================================ ###安全工具 **Coding tool:** * [DOMPurity](https://github.com/cure53/DOMPurify) * [XSS](http://jsxss.com/en/index.html) **References:** * [HTML5 Security Cheatsheet](https://html5sec.org/) ================================================ FILE: 02-fedHandlebook-master/tools/seo.md ================================================ ###SEO 工具 * [Google Webmasters Search Console](https://www.google.com/webmasters/) * [Varvy SEO tool](https://varvy.com/tools/) * [Keyword Tool](http://keywordtool.io/) ================================================ FILE: 02-fedHandlebook-master/tools/static.md ================================================ ###静态网页生成器工具 **JS 网页生成器:** * [Metalsmith](http://www.metalsmith.io/) * [harp](http://harpjs.com/) **JS 博客网站生成器:** * [hubpress.io](http://hubpress.io) * [Hexo.io](http://hexo.io) **网站生成器列表:** * [staticsitegenerators.net](https://staticsitegenerators.net) * [www.staticgen.com](https://www.staticgen.com) ================================================ FILE: 02-fedHandlebook-master/tools/svg.md ================================================ ###SVG 工具 **优化:** * [SVGOMG](https://jakearchibald.github.io/svgomg/) * [Peter Collingridge's SVG Optimiser](http://petercollingridge.appspot.com/svg-optimiser) * [SVGO](https://github.com/svg/svgo) * [SVGO-GUI](https://github.com/svg/svgo-gui) * [SVG Cleaner](https://launchpad.net/svg-cleaner) * [Scour SVG Scrubber](http://www.codedread.com/scour/) * [Clean Multiple Gradient Stops](http://codepen.io/jakealbaugh/pen/OVrQXY) **SVG 编辑器:** * [Illustrator](http://www.adobe.com/products/illustrator.html) * [Sketch](https://www.sketchapp.com/) * [Inkscape](https://inkscape.org/en/) * [DrawSVG](http://www.drawsvg.org/) **创建雪碧图:** * [Icomoon](https://icomoon.io/) * [Fontastic](http://fontastic.me/) * [Grunticon](http://www.grunticon.com/) **Bug 追踪:** * [SVG Weirdness](https://github.com/emilbjorklund/svg-weirdness) * [SVG Edit](https://code.google.com/p/svg-edit/wiki/BrowserBugs) ================================================ FILE: 02-fedHandlebook-master/tools/task.md ================================================ ###任务管理(又称 构建)工具 **任务管理/构建 工具:** * [Gulp](http://gulpjs.com/) * [Grunt](http://gruntjs.com/) **Tasking/build and more tools:** * [Brunch](http://brunch.io/) * [Mimosa](http://mimosa.io/) ================================================ FILE: 02-fedHandlebook-master/tools/templates.md ================================================ ###模板工具 **Just Templating:** * [Mustache.js](https://github.com/janl/mustache.js) * [Handlebars](http://handlebarsjs.com/) * [htmlbars](https://github.com/tildeio/htmlbars) * [Nunjuncks](http://mozilla.github.io/nunjucks/) * [Transparency](http://leonidas.github.io/transparency/) * [doT.js](http://olado.github.io/doT/) * [Hogan.js](http://twitter.github.io/hogan.js/) **Templating and reactive data binding:** * [Rivets.js](http://rivetsjs.com) * [paperclip.js](http://paperclipjs.com) * [riot](http://riotjs.com/) * [vue.js](http://vuejs.org) * [ractive.js](http://www.ractivejs.org) * [react.js](https://facebook.github.io/react/index.html) * [RxJS](https://github.com/Reactive-Extensions/RxJS) * [knockout](http://knockoutjs.com/index.html) * [jquerymy.js](http://jquerymy.com/) **Templating to Virtual DOM:** * [JSX](https://facebook.github.io/jsx/) * [t7](http://t7js.com/) **译者补充:** * [Handlebars 入门](http://www.ido321.com/1629.html) * [Handlebars 系列文章](http://jaskokoyn.com/handlebars-js-tutorial-series/) ================================================ FILE: 02-fedHandlebook-master/tools/test.md ================================================ ###测试框架工具 * [Karma](http://karma-runner.github.io/0.8/index.html) * [Intern](https://theintern.github.io/) * [NightWatch.js](http://nightwatchjs.org/) ================================================ FILE: 02-fedHandlebook-master/tools/ui.md ================================================ ###UI 部件 & 组件工具 **桌面 & 移动:** * [Kendo UI](http://www.telerik.com/kendo-ui) [free to $] * [Webix](http://webix.com/) [$] * [Semantic UI](http://semantic-ui.com/) * [Metro UI](http://metroui.org.ua/) * [Bootstrap](http://getbootstrap.com/components/) * [Materialize](http://materializecss.com/) * [Material UI](http://material-ui.com/) * [Polymer Paper Elements](https://elements.polymer-project.org/browse?package=paper-elements) **桌面 (NW.js 和 Electron):** * [photonkit](http://photonkit.com/) * [React UI Components for OS X El Capitan and Windows 10](http://gabrielbull.github.io/react-desktop/) **专注移动:** * [Ratchet](http://goratchet.com/) * [Kendo UI Mobile](http://demos.telerik.com/kendo-ui/m/index) * [Mobile Angular UI](http://mobileangularui.com/) * [Framework7](http://www.idangero.us/framework7) ================================================ FILE: 02-fedHandlebook-master/tools.md ================================================ ###第三部分 第三部分会简单地讨论一些前端开发工具的使用. 为确保你理解一套工具所属的类别, 建议在此之前先研究工具本身. 注意, 仅仅是一个工具列表, 或一个类别的工具记录, 但这并不等于我断言, 前端开发人员应该学习它并使用它. 选择自己的工具箱, 我只是提供常见的工具箱选项. **译者补充:** * [Web 前端开发资源汇总](https://github.com/lyfeyaj/awesome-resources#web-%E5%89%8D%E7%AB%AF) ================================================ FILE: 02-fedHandlebook-master/what-is-a-fd.md ================================================ ###什么是前端开发者? 一个前端开发者, 要会使用Web技术(如:HTML,CSS,DOM和JavaScript)设计和开发网站应用. 网站应用, 或运行于 [Web平台](https://en.wikipedia.org/wiki/Open_Web_Platform) 之上, 或用于编译非Web平台环境的输入(如:[NativeScript](https://www.nativescript.org/)). ![what-is-front-end-dev](https://raw.githubusercontent.com/dwqs/fedHandlebook/master/images/what-is-front-end-dev.png) 图片来源:https://www.upwork.com/hiring/development/front-end-developer/ 一般而言, 一个人可以通过学习 HTML,CSS,JavaScript进入前端开发领域, 这些代码运行在 [Web浏览器](https://en.wikipedia.org/wiki/Web_browser), [无壳为浏览器](https://en.wikipedia.org/wiki/Headless_browser), Web视图之中, 或用于编译本地运行环境的输入. 后文将详细介绍这四个运行场景. Web浏览器是用于检索, 呈现和遍历万维网(WWW)信息的软件. 一般而言, 浏览器可以运行在台式机, 笔记本电脑, 平板电脑或手机. 但是近来, 几乎在任何事物上都能够发现浏览器(如: 冰箱上, 汽车里等). 最普遍的Web浏览器如下: * [Chrome](http://www.google.com/chrome/) * [Internet Explorer](http://dev.modern.ie/) * [Firefox](https://www.mozilla.org/firefox/) * [Safari](http://www.apple.com/safari/) 无壳浏览器是指没有图形用户界面的Web浏览器, 可以通过命令行接口控制达到网页自动化的目的(如: 功能测试, 单元测试等). 把无壳浏览器当做可以从命令行运行的浏览器, 它依然可以检索和遍历网页. 最普遍的无壳浏览器如下: * [PhantomJS](http://phantomjs.org/) * [slimerjs](http://slimerjs.org/) * [trifleJS](http://triflejs.org/) [Webviews](http://developer.telerik.com/featured/what-is-a-webview/) 被本地 OS 用来运行网页. 把Web视图当做Web浏览器中的iframe或者单个的Tab, 其嵌入于运行在设备上的本地应用程序中(如:[iOS](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWebView_Class/), [android](http://developer.android.com/reference/android/webkit/WebView.html), [windows](https://msdn.microsoft.com/library/windows/apps/windows.ui.xaml.controls.webview.aspx)). Web视图开发最普遍的解决方案如下: * [Cordova](https://cordova.apache.org/) (用于本地手机/平板应用) * [NW.js](https://github.com/nwjs/nw.js) (即 Node-Webkit, 用于桌面应用) * [Electron](http://electron.atom.io/) (用于桌面应用) 最后, 前端开发者从 Web 浏览器开发环境中学到的东西也可以用于不受浏览器引擎驱动的环境下. 目前, 脱离 Web 引擎,使用 Web 技术(如: CSS 和 JavaScript)去创建真正的本地应用的开发环境正在出现. 此类环境的示例如下: * [NativeScript](https://www.nativescript.org/) * [React Native](https://facebook.github.io/react-native/) **译者补充:** * [达到什么样的标准才能是大公司要的前端](http://qianduanfan.com/index.php/topic/show/217) * [写给初学前端工程师的一封信](http://www.w3ctech.com/topic/983) * [前端路上的旅行](http://www.w3cplus.com/front-end-trip-on-road.html) * [React Native专题](http://www.jianshu.com/p/96febc4fec45) ================================================ FILE: 03-FEND_Note-master/AUTHORS ================================================ Li Xinyang Li Xinyang rwang23 hcy003 Fred.W. tinglin92 Tinglin Sylvia Zhang leikn The Gitter Badger Osub <814566123@qq.com> Chenyu nifanle ================================================ FILE: 03-FEND_Note-master/Booklist.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [书单](#%E4%B9%A6%E5%8D%95) - [HTML](#html) - [CSS](#css) - [JavaScript](#javascript) # 书单 ## HTML N/A ## CSS - CSS Mastery: Advanced Web Standard Solutions ## JavaScript - Professional JavaScript for Web Developers - DOM Scripting: Web Design with JavaScript and the Document Object Model - AdvancED DOM Scripting: Dynamic Web Design Techniques - JavaScript: The Definitive Guide ## Version Control - [Git 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html) - [Git Pro](https://git-scm.com/book/en/v2) ================================================ FILE: 03-FEND_Note-master/README.md ================================================ # 前端开发笔记本 [![Join the chat at https://gitter.im/li-xinyang/FEND_Note](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/li-xinyang/FEND_Note?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 点击[这里](https://www.gitbook.com/read/book/li-xinyang/frontend-notebook)开始在 GitBook 阅读! 点击[这里](http://wiki.jikexueyuan.com/project/fend_note/)开始在极客学院 Wiki 阅读!(中国大陆访问速度较快) 前端开发笔记本的 GitHub 项目地址在[这里](https://github.com/li-xinyang/FEND_Note)。如果你觉得这个项目不错,请点击 **Star** 一下,您的支持是我最大的动力。 Star   Watch   Fork   Download ![笔记路径规划](img/C/career-path.jpg) 《前端笔记本》涵盖了 Web 前端开发所需的全部基本知识以及所对应的学习路径。它并不能当做一本完整的学习材料来进行前端的学习,因为在有限的篇幅中无法深入的展开每一个单独的知识点。它更适合作为一个学习清单或者是查询手册,结合其他在各个方面更专业的图书或者官方文档来进行同步学习。在学习过程中为了能达到最佳的学习效果,也可将每个技术点实现并进行适当的拓展。 ### 写作进程 *第一版草稿* 完成时间 1507252244 | 章节 | 名称 | 进程 | | :--- | :-------------- | :-------------------------------- | | 第一章 | 页面制作 | ![](http://progressed.io/bar/100) | | 第二章 | JavaScript 程序设计 | ![](http://progressed.io/bar/100) | | 第三章 | DOM 编程 | ![](http://progressed.io/bar/100) | | 第四章 | 页面构架 | ![](http://progressed.io/bar/100) | | 第五章 | 前端产品构架 | ![](http://progressed.io/bar/100) | ### 成就 **1508311803** - 100 Stars: [@dszls](https://github.com/dszls), thank you! **1511071358** - 200 Stars: [@cschenchen](https://github.com/cschenchen) **1511222144** - 300 Stars: [@lujun9972](https://github.com/lujun9972) **1512102333** - 400 Stars: [@Niefee](https://github.com/Niefee) **1601231619** - 500 Stars: [@wangdsh](https://github.com/wangdsh) **1603261510** - 600 Stars: [@lty2226262](https://github.com/lty2226262) **1605170450** - 700 Stars: [@eqzcy](https://github.com/eqzcy) **1608010000** - 800 Stars: [@BeOker](https://github.com/BeOker) ### 相关链接 - [NEC](http://nec.netease.com/) {N: nice, E: easy, C: css;} ### 声明 前端笔记本大纲及内容由[网易云课堂前端专业](http://mooc.study.163.com/smartSpec/detail/12001.htm_)改编 Creative Commons License
This work by Li Xinyang is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. ================================================ FILE: 03-FEND_Note-master/SUMMARY.md ================================================ # Summary - [页面制作](chapter1/00_intro.md) - [Photoshop](chapter1/01_photoshop.md) - [工具、面板、视图](chapter1/01_01_tool_panel_view.md) - [测量及取色](chapter1/01_02_measurement_and_color.md) - [切图](chapter1/01_03_slice.md) - [图片保存](chapter1/01_04_save_image.md) - [图片优化与合并](chapter1/01_05_image_optimisation.md) - [开发及调试工具](chapter1/02_dev_tools.md) - [Sublime 编辑器](chapter1/02_01_sublime.md) - [Atom 编辑器](chapter1/02_02_atom.md) - [HTML](chapter1/03_html.md) - [HTML 简介](chapter1/03_01_html_intro.md) - [HTML 语法](chapter1/03_02_html_sytax.md) - [HTML 标签](chapter1/03_05_html_tags.md) - [实体字符](chapter1/03_03_html_ascii_encoding.md) - [浏览器兼容](chapter1/03_04_cross_browser.md) - [CSS](chapter1/04_css_intro.md) - [语法](chapter1/04_01_css_sytax.md) - [选择器](chapter1/04_02_selector.md) - [文本](chapter1/04_03_text.md) - [盒模型](chapter1/04_04_box_model.md) - [背景](chapter1/04_05_background.md) - [布局](chapter1/04_06_layout.md) - [变形](chapter1/04_07_transform.md) - [动画](chapter1/04_08_animation.md) - [常见布局样例](chapter1/04_09_layout_demo.md) - [JavaScript 程序设计](chapter2/00_intro.md) - [JavaScript 介绍](chapter2/01_javascript_intro.md) - [调试器](chapter2/02_dev_tools.md) - [基础语法](chapter2/03_basic_syntax.md) - [类型系统](chapter2/04_data_type.md) - [类型判断](chapter2/11_js_type_determin.md) - [内置对象](chapter2/05_internal_object.md) - [变量作用域](chapter2/06_scope.md) - [表达式与运算符号](chapter2/07_statement_and_operator.md) - [语句](chapter2/08_statement.md) - [闭包](chapter2/09_closure.md) - [面向对象](chapter2/10_object.md) - [正则表达式](chapter2/12_reg_exp.md) - [DOM 编程](chapter3/00_intro.md) - [文档树(DOM Tree)](chapter3/01_dom_tree.md) - [节点操作](chapter3/02_node_manipulation.md) - [操作属性](chapter3/03_attribute.md) - [样式操作](chapter3/04_style_manipulation.md) - [事件](chapter3/05_events.md) - [多媒体(视频与音频)](chapter3/08_multimedia.md) - [Canvas](chapter3/07_canvas.md) - [BOM](chapter3/10_bom.md) - [数据通信](chapter3/09_network.md) - [数据存储](chapter3/11_storage.md) - [动画](chapter3/06_animation.md) - [表单操作](chapter3/12_form_manipulation.md) - [列表操作](chapter3/13_list_manipulation.md) - [页面架构](chapter4/00_intro.md) - [CSS Reset](chapter4/01_CSS_Reset.md) - [布局解决方案](chapter4/02_layout.md) - [响应式布局](chapter4/03_responsive.md) - [页面优化](chapter4/04_page_optimisation.md) - [规范与模块化](chapter4/05_modulation.md) - [产品前端架构](chapter5/00_intro.md) - [协作流程](chapter5/01_collaboration.md) - [接口设计](chapter5/02_design_api.md) - [版本控制](chapter5/03_version_control.md) - [技术选型](chapter5/04_tech_selection.md) - [开发实践](chapter5/05_development.md) - [附录 A:书单](Booklist.md) ================================================ FILE: 03-FEND_Note-master/SampleCode/CSS/Animation.html ================================================ Animation

动画 animation

================================================ FILE: 03-FEND_Note-master/SampleCode/CSS/AnimationPractice.html ================================================ Animation Practice

3D 正方体组合旋转

back
front
right
left
botto
top
================================================ FILE: 03-FEND_Note-master/SampleCode/CSS/Transform2D.html ================================================ Transform 2D

Transform 2D

可忽略所有:after的样式

transform (translate & rotate)

translate(50%) -> rotate(45deg)


rotate(45deg) -> translate(50%)


translate-origin

transform-origin: 100% 100%


scale

scale(0.5)


rotate(45deg) scale(1.5, 2)


skew

skew(-45deg, 45deg)


================================================ FILE: 03-FEND_Note-master/SampleCode/CSS/Transform3D.html ================================================ Transfomr 3D

Transform 3D

可以忽略所有:after的样式

rotate

perspective-origin

top

50% 50%

bottom

translate3d(x, y, z)

(10px, 10px, 200px)

(0, 0, -100px)

(0, 0, 0)

scale3d

改变轴的比例

scale3d(1.2, 1.2, 1)

rotate3d

(0, 0, 1, 45deg)

(1, 1, 1, 45deg)

(-1, -1, -1, 45deg)

transform-style

preserve-3d

flat

backface-visibility

B

visible

B

hidden

================================================ FILE: 03-FEND_Note-master/SampleCode/CSS/Transition.html ================================================ Transition

动画 - Transition

transition

无过度

有过度

transition-property

none

all

left

color

多个动画,多个时间

multipe

================================================ FILE: 03-FEND_Note-master/SampleCode/Layout/00_center_horizontal.html ================================================ 水平居中

inline-block + text-align

table + margin

absolute + transform

flex + justify-content

================================================ FILE: 03-FEND_Note-master/SampleCode/Layout/01_center_vertical.html ================================================ 垂直居中
================================================ FILE: 03-FEND_Note-master/SampleCode/Layout/02multiple_column.html ================================================ 多列布局 - 一列定宽
================================================ FILE: 03-FEND_Note-master/TOC.md ================================================ # Summary - [页面制作](chapter1/00_intro.md) - [Photoshop](chapter1/01_photoshop.md) - [工具、面板、视图](chapter1/01_01_tool_panel_view.md) - [测量及取色](chapter1/01_02_measurement_and_color.md) - [切图](chapter1/01_03_slice.md) - [图片保存](chapter1/01_04_save_image.md) - [图片优化与合并](chapter1/01_05_image_optimisation.md) - [开发及调试工具](chapter1/02_dev_tools.md) - [Sublime 编辑器](chapter1/02_01_sublime.md) - [Atom 编辑器](chapter1/02_02_atom.md) - [HTML](chapter1/03_html.md) - [HTML 简介](chapter1/03_01_html_intro.md) - [HTML 语法](chapter1/03_02_html_sytax.md) - [HTML 标签](chapter1/03_05_html_tags.md) - [实体字符](chapter1/03_03_html_ascii_encoding.md) - [浏览器兼容](chapter1/03_04_cross_browser.md) - [CSS](chapter1/04_css_intro.md) - [语法](chapter1/04_01_css_sytax.md) - [选择器](chapter1/04_02_selector.md) - [文本](chapter1/04_03_text.md) - [盒模型](chapter1/04_04_box_model.md) - [背景](chapter1/04_05_background.md) - [布局](chapter1/04_06_layout.md) - [变形](chapter1/04_07_transform.md) - [动画](chapter1/04_08_animation.md) - [常见布局样例](chapter1/04_09_layout_demo.md) - [JavaScript 程序设计](chapter2/00_intro.md) - [JavaScript 介绍](chapter2/01_javascript_intro.md) - [调试器](chapter2/02_dev_tools.md) - [基础语法](chapter2/03_basic_syntax.md) - [类型系统](chapter2/04_data_type.md) - [类型判断](chapter2/11_js_type_determin.md) - [内置对象](chapter2/05_internal_object.md) - [变量作用域](chapter2/06_scope.md) - [表达式与运算符号](chapter2/07_statement_and_operator.md) - [语句](chapter2/08_statement.md) - [闭包](chapter2/09_closure.md) - [面向对象](chapter2/10_object.md) - [正则表达式](chapter2/12_reg_exp.md) - [DOM 编程](chapter3/00_intro.md) - [文档树(DOM Tree)](chapter3/01_dom_tree.md) - [节点操作](chapter3/02_node_manipulation.md) - [操作属性](chapter3/03_attribute.md) - [样式操作](chapter3/04_style_manipulation.md) - [事件](chapter3/05_events.md) - [多媒体(视频与音频)](chapter3/08_multimedia.md) - [Canvas](chapter3/07_canvas.md) - [BOM](chapter3/10_bom.md) - [数据通信](chapter3/09_network.md) - [数据存储](chapter3/11_storage.md) - [动画](chapter3/06_animation.md) - [表单操作](chapter3/12_form_manipulation.md) - [列表操作](chapter3/13_list_manipulation.md) - [页面架构](chapter4/00_intro.md) - [CSS Reset](chapter4/01_CSS_Reset.md) - [布局解决方案](chapter4/02_layout.md) - [响应式布局](chapter4/03_responsive.md) - [页面优化](chapter4/04_page_optimisation.md) - [规范与模块化](chapter4/05_modulation.md) - [产品前端架构](chapter5/00_intro.md) - [协作流程](chapter5/01_collaboration.md) - [接口设计](chapter5/02_design_api.md) - [版本控制](chapter5/03_version_control.md) - [技术选型](chapter5/04_tech_selection.md) - [开发实践](chapter5/05_development.md) - [附录 A:书单](Booklist.md) ================================================ FILE: 03-FEND_Note-master/assets/Bookcover.sketch ================================================ [File too large to display: 31.8 MB] ================================================ FILE: 03-FEND_Note-master/book.json ================================================ { "gitbook": ">=2.0.0", "title":"Frontend Notebook", "language":"zh-cn", "description":"互联网前端开发过程全记录。从基础开始,从现在开始。" } ================================================ FILE: 03-FEND_Note-master/chapter1/00_intro.md ================================================ ## 前端工程师概述 [TOC] **网页发展史** Web 1.0 -> Web 2.0(基于 Ajax) -> Web 3.0 (基于 HTML5) **网站开发协作流程** ![](../img/D/development-flow.png) **前端职责** 视觉稿(配色图标距离空间) + 交互稿(用户逻辑) = UI(用户界面) 视觉稿 -> 页面制作 交互稿 -> 页面逻辑开发 **所需技能** - DOM (操作 HTML 及 CSS 的接口) - JavaScript (定义页面互动) - CSS (定义页面样式) - HTML (定义页面结果) - Photoshop (取图) ================================================ FILE: 03-FEND_Note-master/chapter1/01_01_tool_panel_view.md ================================================ ### 工具, 面板, 视图 [TOC] 在全局设置下将单位修改为像素,因其在 CSS 中运用最广 (**Preference** -> **Units & Rulers** -> **Units**) 。设置工作区布局为切图及图片编辑做准备(所需窗口为信息窗口,图层窗口以及历史记录窗口)。 ![](../img/P/photoshop-mainWindow.png) 打开『信息窗口』、『图层窗口』、『历史纪录窗口』、『工具面板』、『选项』面板,可以通过 Window -> Workspace -> New WorkSpaces 保存工作区,以便下次打开。 **切图常用工具** | 工具名 | 示意图 | 注释 | | ------ | :----------------------: | -------------------------------------- | | 移动工具 | ![](../img/H/hwa_03.png) | | | 矩形选框工具 | ![](../img/H/hwa_01.png) | 需将自动选择调至选择图层 | | 魔棒工具 | ![](../img/H/hwa_05.png) | (容差 Tolerance 越小选择的范围就越小)消除锯齿可以让选择区域光滑 | | 剪裁工具 | ![](../img/H/hwa_06.png) | | | 切片工具 | ![](../img/H/hwa_07.png) | 在裁剪工具分支下 | | 缩放工具 | ![](../img/H/hwa_34.png) | `Ctrl+加号`与`Ctrl+减号` 或者 `alt+鼠标滑轮` | | 取色器 | ![](../img/H/hwa_31.png) | | 图层(单层元素)与组(类似于文件夹)的区别。 **辅助视图**,在视图菜单下启动 - 对齐,会启动靠近吸附功能 - 标尺,Command + R - 参考线,Command + ; ![](../img/P/photoshop-menu.png) NOTE: 所有工具及快捷键如下。 ![](../img/T/ToolsPanelOverview.png) ### 常用快捷键 **放大缩小画布** - 放大到100%大小:Ctrl+数字1 - 放大:ctrl + 加号 - 缩小:ctrl + 减号 - alt + 滚轮 **合并图层** - 合并图层:ctrl+E - 合并可见图层:ctrl+shift+E ================================================ FILE: 03-FEND_Note-master/chapter1/01_02_measurement_and_color.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [测量及取色](#%E6%B5%8B%E9%87%8F%E5%8F%8A%E5%8F%96%E8%89%B2) ### 测量及取色 所有能接受数字的属性都需要测量并尽可能百分百的还原设计稿。 #### 测量 - 宽度,高度 (width, height) - 内外边距 (padding, margin) - 边框 (border) - 定位 (position) - 文字大小 (font-size) - 行高 (line-height),其为第一行的底端到第二行的底端。 - 背景位置 (background-position) NOTE: 测量时尽可能放大画布以减少误差。量取文字是为了减少误差尽量选取尺寸大的文字进行测量。 ![](../img/P/photoshop-measure&colorSelection.png) **选框工具的多用途**,增(Shift)减(Alt)以及交叉选择(Shift + Alt)。左右(或上下)使用分离选框选择可以得到整两个分离边框的距离总值。 ![](../img/T/rect-selection-tool.gif) #### 取色 所有能接受颜色的属性都需要取色。 - 边框色 - 背景色 - 文字色 NOTE:使用魔棒工具可以迅速识别背景色是否没*线性*渐变的方法。Mac OS X 推荐使用 **Sip** 可在 Mac App Store 免费下载。 NOTE+:可以使用魔棒工具来判断颜色是否为渐变。 ================================================ FILE: 03-FEND_Note-master/chapter1/01_03_slice.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [切图](#%E5%88%87%E5%9B%BE) ### 切图 - **内容性图片** 指的是图片在页面是作为内容存在,如页面中的海报。 - **修饰性图片** 指的是图片在页面中起修饰作用,如页面中的背景和图标。 修饰性图标和内容性图片需要(在 HTML 的 `` 之中,只需站位不需切图)切出。切出的内容性图片应保存为 `*.jpg` 格式,而修饰性图片因保存为 `png24`(IE6 不支持半透明,Alpha 透明) 或 `png8` 它们均支持全透明。 **隐藏文字**,方法一,直接在图层中隐藏文字图层。方法二(两种,分别应对于纯色和有背景需要隐藏文本的情况)如下图所示,使用自由变换。 ![](../img/B/btn-remove-text.gif) **PNG24**切图方法 - 移动工具选中所需图层(Ctrl 多选) - 右键合并图层(Ctrl + E) - 复制到新图层 **PNG8**带背景切图方法 - 合并可见图层(Shift + Ctrl + E) - 矩形选框选择内容 - 魔棒工具去除多余部分(Alt + 选取) **可平铺**背景的切图方法 - 用矩形选择一个区域 - 复制至新图层 NOTE: X 轴平铺需要占满图片的宽,Y 轴平铺需要占满图片的高。 **切片**工具(大图化小的方法,将一大图分为多小图) - 拉参考线 - 选择切片工具 - 点击 “基于参考线的切片” 按钮 - 选择切片选择工具 - 保存于新图层 #### 如何开始切图 ##### 第一步:去掉所有文字,只留背景 打开视觉稿后,用上面说过的方法去掉所有的文字,只留背景和图片(记得备份一下PSD文件)。 ![](../img/P/photoshop-without-image.png) ##### 第二步:保存图片 将去掉文字的图片保存起来。一般情况下内容性图片保存为jpg格式,图标类型的保存为png格式。 ![](../img/P/photoshop-cut-image.png) ##### 第三步:构思页面的具体实现 划分页面的结构,具体的实现方式。 ##### 第四步:一边编写HTML代码,一边测量、取色 根据构思号的页面结构,开始编写HTML代码,并且开始进行测量和取色。 ================================================ FILE: 03-FEND_Note-master/chapter1/01_04_save_image.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [图片保存](#%E5%9B%BE%E7%89%87%E4%BF%9D%E5%AD%98) - [保存格式的选择](#%E4%BF%9D%E5%AD%98%E6%A0%BC%E5%BC%8F%E7%9A%84%E9%80%89%E6%8B%A9) - [图片修改与维护](#%E5%9B%BE%E7%89%87%E4%BF%AE%E6%94%B9%E4%B8%8E%E7%BB%B4%E6%8A%A4) ### 图片保存 将需要的内容保存在独立的文件里便于之后的导出。(存储于 Web 所用格式 Alt + Shift + Ctrl + S) 如需保存**独立图层**则要把需要的图层拖到新建的透明背景的图层,或在图层上右键复制(Duplicate)图层选择地址为新文件即可。 **图片与背景合并**的切图方法如下 ![](../img/S/save-image.gif) #### 保存格式的选择 保存类型一:色彩丰富切无透明要求时保存为 `JPG` 格式并选用时候的品质(通常使用品质 80 )。 保存类型二:图片色彩不丰富,不伦透明与否一律保存为 `PNG8` 格式(256颜色,需特殊设置如下图,需设置`杂边:无` `无仿色`)。 ![](../img/P/photoshop-saveFormat.png) 保存类型三:图片有半透明(Alpha 透明)的要求,保存为 `PNG24` 格式(不对图片进行压缩)。 保存类型四:保留 PSD 源文件,以备不时之需。 ##### 如何保存 一般使用『存储于 Web 所用格式』菜单(Alt + Shift + Ctrl + S)保存 ### 图片修改与维护 维护与修改之一:**更改画布**大小以便增加新素材。 ![](../img/R/resize-canvas.gif) 维护与修改之二:移动图标分两种,独立图层(移动工具拖动),于非独立图层(选取工具选中分离后移动工具拖动)。 ![](../img/M/move-layer.gif) 维护与修改之三:**裁剪画布**的方法有两种,(1)用选取工具选取后图片裁取,(2)直接用裁剪工具裁剪画布。 ![](../img/C/crop-canvas.gif) **注意事项**:为了可维护性的考虑因适当的留出适当的空白区域以便修改所用和提高容错性。`PNG8`需更改图片颜色模式为 RGB 颜色(默认为索引颜色模式,颜色信息会被丢失)。 ![](../img/P/png8-color-mode.gif) ================================================ FILE: 03-FEND_Note-master/chapter1/01_05_image_optimisation.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [图片优化与合并](#%E5%9B%BE%E7%89%87%E4%BC%98%E5%8C%96%E4%B8%8E%E5%90%88%E5%B9%B6) - [图片的兼容](#%E5%9B%BE%E7%89%87%E7%9A%84%E5%85%BC%E5%AE%B9) ### 图片优化与合并 在 HTML 中使用背景图片的方法如下: ```html ``` 图片的**合并**就如同上面提到的切图后保存的过程。拼好的图称之为 **Sprite** 它能减少网络请求次数提高速度。图片压缩工具分为无损(ImageOptim 等工具,也可结合 Grunt 自动化构建工具一同使用)与有损压缩工具(TinyPng)。 #### 图片合并 图片合并建议方案: - 同个模块的图片合并 - 大小相近的图片合并 - 色彩相近的图片合并 - 以上3种合并混合 合并的图片可以以横向或纵向的排列,分类可将同属于一个模块(功能模块),大小相近(充分利用画布空间),颜色相近(减少文件大小)。 #### 图片的兼容 IE6 不支持 PNG24 半透明所以需要保存两份(sprite.png - png24 和 sprite-ie.png - 8)。在使用 CSS3 是让高级浏览器使用 CSS3 低级浏览器则使用切图。优雅降级指的是让低级浏览器不显示高级浏览器中的界面细节。 ================================================ FILE: 03-FEND_Note-master/chapter1/01_photoshop.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Photoshop](#photoshop) ## Photoshop **切图** 从设计稿中切除网页的素材并在代码中引入图片 (复杂的图片或者解决兼容问题) ```html // 设计稿 (*.psd) -> 产出物 (*png, *.jpg) desc ``` ================================================ FILE: 03-FEND_Note-master/chapter1/02_01_sublime.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Sublime 编辑器](#sublime-%E7%BC%96%E8%BE%91%E5%99%A8) - [介绍](#%E4%BB%8B%E7%BB%8D) - [安装](#%E5%AE%89%E8%A3%85) - [Windows/OS X](#windowsos-x) - [Ubuntu](#ubuntu) - [推荐插件](#%E6%8E%A8%E8%8D%90%E6%8F%92%E4%BB%B6) - [1. Package Control](#1-package-control) - [2.Emmet](#2emmet) - [3.JQuery](#3jquery) - [4.FileHeader](#4fileheader) - [5.Pretty Json](#5pretty-json) - [6.CSS Format](#6css-format) - [7.ConvertToUTF8](#7converttoutf8) - [用户自定义代码](#%E7%94%A8%E6%88%B7%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BB%A3%E7%A0%81) ### Sublime 编辑器 #### 介绍 Sublime Text是一款性感的编辑器,具有优雅,快速,插件多等优势,不失为前端开发者的轻量高效编辑器。 #### 安装 ##### Windows/OS X 官方[站点](http://www.sublimetext.com/)下载安装即可。 ##### Ubuntu 可参见 [Ubuntu 下使用 Sublime Text 并解决中文输入问题](http://www.zntec.cn/archives/ubuntu-sublime-text-fcitx.html),用apt-get安装,解决任务栏,中文输入等多个问题。 ##### Sublime 快捷键 command/control + P 进入查找命令(Goto Anything),此时有三种选择: * `:` 输入行数找到对应行 (control + G) * `@` 找到特定函数 (command/control + R) * `#` 找到对应变量与块 #### 推荐插件 ##### 1. Package Control 以后的插件安装基本都靠他了,安装方法可以去[Package Control](https://packagecontrol.io/installation)查看,注意Sublime Text的版本问题。 ##### 2.Emmet 前端神器,相信搞前端的没有不用的 **下面插件在Edit 或者 Tools 里面看到插件功能:** ##### 3.JQuery 写JQuery怎么能不用他来增强你的提示? ##### 4.FileHeader 自动创建文件开头模板,并且会根据最后的保存时间修改更新时间 ##### 5.Pretty Json 写json不格式话怎么行? ##### 6.CSS Format css格式化 ##### 7.ConvertToUTF8 GBK编码兼容 #### 用户自定义代码 Preferences - Settings - User 里面加入,全部的设置均为 `JSON` 文本。 ``` "translate_tabs_to_spaces": true, "tab_size": 2, ``` 把Tab对齐转化为空格对齐,tab_size 控制转化比例。 ``` "trim_trailing_white_space_on_save": true, ``` 自动移除行尾多余空格。 ``` "ensure_newline_at_eof_on_save": true, ``` 自动在文件末尾加入一个空行,git 用户相信知道是干嘛的。 ``` "save_on_focus_lost": true, ``` 窗口失去焦后立即保存文件。 ``` "bold_folder_labels": true, ``` 侧栏文件夹加粗。 ================================================ FILE: 03-FEND_Note-master/chapter1/02_02_atom.md ================================================ ### Atom 文本编辑器 本文即为在 **Atom** 下编写完成,在写作过程中让我对这个崭新的 1.0 版本文本编辑器 有了更多的了解。 在阅读本文时注意快捷键于后面英文单词的对应可帮助记忆,在使用中忘记的快捷键以可以通过使用 **查询面板**(后面会提到)进行查询。 如果你在使用过程中发现了异常和错误可以到 **Atom** 所在的 GitHub 仓库提交问题报告。同一款编辑器一同成长,愿力量与你同在! 下面的快捷键均为 **Mac OS X** 默认设置。如你用的是 Windows 或者是 Linux,可能需要尝试将 所有提到的 cmd 改为 ctrl。 #### 基础中的基础 开始之前先把下面这条快捷键记住。cmd+shift+P 它会打开类似 Alfred 的快捷功能选择窗口, 如果你从来没有听过 Alfred(此为 Mac OS X 特有应用) 那你应该赶紧去所搜引擎中找找了。 **保存时间** |快捷键|描述| |-----|----| |`cmd-shift-S`|可以另存为 "Save As"| |`cmd-alt-S`|可以保存全部的 "Save All".| **打开文件与目录** 如果在命令行环境中可以使用下面的方法一次打开多个目录。 ```bash # 打开目录 atom ./hopes ./dreams # 获得帮助 atom -h ``` |快捷键|描述| |-----|----| |`cmd-O`|打开文件| |`cmd-shift-O`|添加目录至当前编辑器窗口| `cmd-P` 可以打开 Fuzzy Finder 进行模糊搜索,默认可所搜区域为项目内所有文件。 下面的命令可以对模糊所搜做一些限制,`cmd-B` 只所搜已打开的文件(存在与 Buffer 中的文件)。 `cmd-shift-B` 1.0 版本中在编辑器中添加的新文件无法使用 Fuzzy Finder(模糊寻找) 找到,重启后则可以解决。 **边栏(树目录)** |快捷键|描述| |-----|----| |`cmd-\`|显示或隐藏边栏| |`ctrl-0`|聚焦边栏,聚焦后可以操作树目录中的文件| 在聚焦后可以通过 `a` 来增加(add),`m` 来移动(move),`d` 来复制(duplicate)或者 `delete` 来删除(此处为键盘删除键)。 这里的操作并没有自动路径补全功能,之后可能需要插件支持。 #### 开始使用 **Atom** 中几乎所有的功能都是以插件的形式存在的。所有如何安装插件则就是我们第一件要做的事。 除了图形界面安装的方法外,随 **Atom** 还安装了插件管理器叫做 `apm` 。通过它也可以直接安装 和更新插件。简单说主题也是插件,所以安装主题与安装插件的步骤类似。 *下面的操作均需要联网* ``` # 安装插件 apm install # 安装指定版本的插件 apm install @ # 查询插件 apm search # 查询插件详情 apm view ``` ##### 移动光标 **Atom** 的移动快方法同 **Emacs** 一致。在熟悉使用 **Atom** 后也很容易的转移至 **Emacs** 的环境下熟练工作。 单个字符的移动,效果于方向键一致。 |快捷键|描述| |-----|----| |`ctrl-P`|上移(Previous)| |`ctrl-N`|下移(Next)| |`ctrl-B`|后移(Back)| |`ctrl-F`|前移(Forward)| 在单个字符移动基础上,可以延展至更大范围的移动。例如,单词,整行。 |快捷键|描述| |-----|----| |`alt-B`|向后以词为单位移动(英文),中文则以英文标点为间隔| |`alt-F`|向前以词为单位移动(英文),中文则以英文标点为间隔| |`ctrl-E`|移动至行末(End)| |`ctrl-A`|移动至此行首字符(Ahead)| |`ctrl-A`(敲击两次)|移动至此行行首(包括空格)| |`cmd-up`|移动至文件最顶| |`cmd-down`|移动至文件最低| `ctrl-G` 加数字可移动至**目标行**,使用 `row:column` 可以定位行数和列数, 使用这个方法在查找错误时变得十分方便。 `cmd-R` 可以**在当前文件中**(Buffer)按照符号来搜索,符号关键字指的是函数名(代码中) 或标题(文档中)。 ##### 选择 选择是在移动的基础上加入 `shift` 既可完成。特别的几种选择方法如下。 |快捷键|描述| |-----|----| |`cmd-L`|选取整行| |`ctrl-shift-W`|选取当前单词(英文),中文则为整行| ##### 编辑与删除 **Atom** 如同其他的常用的文本编辑器一样可以直接编辑文字,并不存在特殊的模式。但了解下面的 编辑技巧可以让你使用 **Atom** 更得心应手。 **编辑操作** |快捷键|描述| |-----|----| |`ctrl-T`|交换光标两边的字符(Transpose)| |`ctrl-J`|将下一行同当前行合并(Join)| |`ctrl-cmd-up`|向上冒泡当前行| |`ctrl-cmd-down`|向下冒泡当前行| |`cmd-shift-D`|复制当前行(Duplicate)| |`cmd-K, cmd-U`|转换选中字符至全大写| |`cmd-K, cmd-L`|转换选中字符至全小| **删除操作** |快捷键|描述| |-----|----| |`ctrl-shift-K`|删除当前(Cut)| |`cmd-delete`|删除此行光标后全部字符| |`cmd-backspace`|删除至当前行首| |`ctrl-K`|切帖至行末(Cut)| |`alt-H`|删除前一个字符| |`alt-D`|删除后一个字符| **多个光标及选择** 同 **Sublime Text** 相同,**Atom** 也同样有多光标的实现。 按住cmd可以在文本中使用进行区域性选择。 |快捷键|描述| |-----|----| |`cmd-click`|在点击处增加新光标| |`cmd-shift-L`|将选择区域转换为多光标| |`ctrl-shift-up`|在上一行增加新光标| |`ctrl-shift-down`|在下一行增加新光标| |`cmd-D`|选择下一个于当前被选字符相同的字符并添加新光标| |`cmd-ctrl-G`|选择全部于当前选中字符相同的字符并添加光标| ##### 括号 编程中最常打交道和需要跳出的莫属于括号和引号了。**Atom** 对于括号有很好的处理办法, 各种括号在光标内移动都会被自动高亮(引号和 HTML 中的标签也会被高亮和自动补全)。 选中内容后使用括号可以自动将选中内容包含在括号或引号内。 |快捷键|描述| |-----|----| |`ctrl-M`|跳至最近的一个括号的起始位置| |`ctrl-cmd-M`|选中括号内的所有内容| |`alt-cmd-.`|关闭最近的一个 XML/HTML 标签| ##### 搜索与替换 |快捷键|描述| |-----|----| |`cmd-F`|当前文本中搜索| |`cmd-shift-F`|搜索整个项目| |`cmd-G`|找到下一个匹配的搜索结果| |`cmd-G-shift`|找到上一个匹配的搜索结果| 在项目搜索中可以使用 wildcard 和指定目标的搜索路径。 ##### 代码片段(Snippets) 代码片段让你在写代码时有飞一般的感觉,代码片段会将预先设置好的代码片段替换在当前文本中, 并且设置焦点并用 tab 聚焦下一个焦点, 或 shift + tab 聚焦上一个焦点。 所有的代码片都存储在下面的目录中 `~/.atom/snippets.cson`, 你可以通过 Open Your Snippets Menu 打开此文件。 |快捷键|描述| |-----|----| |`alt-shift-S`|显示当前文件类型下的全部代码片段| 当然制作代码片也有一个代码片,它就是 `snip` 。 ###### 制作代码片段 下面是一个简单的代码片样例。 ```javascript '.source.js': 'console.log': 'prefix': 'log' 'body': 'console.log(${1: "crash"});$2' ``` - `.source.js` 为代码片可用的文件类型范围 - `console.log` 为代码片内容描述 - `prefix` 为代码片调用字符 - `body` 代码片主体内容 - `${1:'crash'}` 用于定义焦点,顺序及其默认值 多行代码代码片 ```javascript '.source.js': 'if, else if, else': 'prefix': 'ieie' 'body': """ if (${1:true}) { $2 } else if (${3:false}) { $4 } else { $5 } """ ``` ##### 代码折叠 可以点击代码行号边的箭头折叠当前层级的代码。 |快捷键|描述| |-----|----| |`alt-cmd-[`|折叠当前层级| |`alt-cmd-]`|展开当前层级| |`alt-cmd-shift-{`|折叠全部层级| |`alt-cmd-shift-}`|展开全部层级| |`cmd-K, cmd-N`(层级数)|根据层级级别折叠| ##### 多窗口模式 任意一个窗口都可以无需的四面分割,分割的部分则依然使用标签来表示。 |快捷键|描述| |-----|----| |`cmd-k arrow`|根据方向指定分割窗口| |`cmd-K, cmd-arrow`|聚焦指定方向的窗口| ##### 解码(Encoding) **Atom** 支持多种解码格式(包括中文 GBK 的支持),也可自动识别解码方式 (不能识别时则默认为 UTF-8)。当然你也可以使用这种方法将多种文本在多种解码格式直接转换。 |快捷键|描述| |-----|----| |`ctrl-shift-U`|切换解码方式| ##### 书签 在 **Atom** 添加书签就如同你看书的时添加书签一样,它使你在书写代码时可以自如的跳转到你需要的 位置。 |快捷键|描述| |-----|----| |`F2-cmd`|可以在当前行切换标记书签| |`F2`|跳转至下一个书签| |`F2-shift`|跳转至上一个书签| |`F2-ctrl`|查看全部的书签| |`F2-cmd-shift`|清除全部标签| ##### 扩展插件 下面列出了笔者在日常 Web 开发中所易用的插件,这些插件满足了超过百分之九十笔者的开发需求。下面的这些插件均可以在官方插件管理器中进行下载和安装。 ```javascript [ { "name": "advanced-new-file", "version": "0.4.3" }, { "name": "advanced-open-file", "version": "0.8.2" }, { "name": "atom-beautify", "version": "0.28.8" }, { "name": "AtomicChar", "version": "0.3.8" }, { "name": "autocomplete-paths", "version": "1.0.2" }, { "name": "docblockr", "version": "0.7.3" }, { "name": "ex-mode", "version": "0.7.0" }, { "name": "file-icons", "version": "1.6.2" }, { "name": "language-jade", "version": "0.6.2" }, { "name": "linter", "version": "1.3.0" }, { "name": "linter-jshint", "version": "1.1.4" }, { "name": "markdown-toc", "version": "0.3.0" }, { "name": "merge-conflicts", "version": "1.3.5" }, { "name": "minimap", "version": "4.12.2" }, { "name": "minimap-linter", "version": "1.0.0" }, { "name": "open-in-browser", "version": "0.4.6" }, { "name": "pigments", "version": "0.9.3" }, { "name": "sort-lines", "version": "0.11.0" }, { "name": "sync-settings", "version": "0.6.0" }, { "name": "tab-switcher", "version": "1.2.1" }, { "name": "theme-switcher", "version": "1.1.0" }, { "name": "vim-mode", "version": "0.57.0" }, { "name": "atom-dark-syntax", "version": "0.27.0", "theme": "syntax" }, { "name": "atom-dark-ui", "version": "0.49.0", "theme": "ui" }, { "name": "atom-light-syntax", "version": "0.28.0", "theme": "syntax" }, { "name": "atom-light-ui", "version": "0.41.0", "theme": "ui" }, { "name": "base16-tomorrow-dark-theme", "version": "0.26.0", "theme": "syntax" }, { "name": "base16-tomorrow-light-theme", "version": "0.9.0", "theme": "syntax" }, { "name": "one-dark-ui", "version": "1.0.2", "theme": "ui" }, { "name": "one-dark-syntax", "version": "1.1.0", "theme": "syntax" }, { "name": "one-light-syntax", "version": "1.1.0", "theme": "syntax" }, { "name": "one-light-ui", "version": "1.0.2", "theme": "ui" }, { "name": "solarized-dark-syntax", "version": "0.38.1", "theme": "syntax" }, { "name": "solarized-light-syntax", "version": "0.22.1", "theme": "syntax" }, { "name": "about", "version": "1.0.1" }, { "name": "archive-view", "version": "0.58.0" }, { "name": "autocomplete-atom-api", "version": "0.9.2" }, { "name": "autocomplete-css", "version": "0.9.0" }, { "name": "autocomplete-html", "version": "0.7.2" }, { "name": "autocomplete-plus", "version": "2.19.0" }, { "name": "autocomplete-snippets", "version": "1.7.1" }, { "name": "autoflow", "version": "0.25.0" }, { "name": "autosave", "version": "0.22.0" }, { "name": "background-tips", "version": "0.25.0" }, { "name": "bookmarks", "version": "0.35.0" }, { "name": "bracket-matcher", "version": "0.76.0" }, { "name": "command-palette", "version": "0.36.0" }, { "name": "deprecation-cop", "version": "0.53.1" }, { "name": "dev-live-reload", "version": "0.46.0" }, { "name": "encoding-selector", "version": "0.21.0" }, { "name": "exception-reporting", "version": "0.36.0" }, { "name": "find-and-replace", "version": "0.175.0" }, { "name": "fuzzy-finder", "version": "0.87.0" }, { "name": "git-diff", "version": "0.55.0" }, { "name": "go-to-line", "version": "0.30.0" }, { "name": "grammar-selector", "version": "0.47.0" }, { "name": "image-view", "version": "0.54.0" }, { "name": "incompatible-packages", "version": "0.24.1" }, { "name": "keybinding-resolver", "version": "0.33.0" }, { "name": "link", "version": "0.30.0" }, { "name": "markdown-preview", "version": "0.150.0" }, { "name": "metrics", "version": "0.51.0" }, { "name": "notifications", "version": "0.57.0" }, { "name": "open-on-github", "version": "0.37.0" }, { "name": "package-generator", "version": "0.40.0" }, { "name": "release-notes", "version": "0.53.0" }, { "name": "settings-view", "version": "0.211.2" }, { "name": "snippets", "version": "0.95.0" }, { "name": "spell-check", "version": "0.59.0" }, { "name": "status-bar", "version": "0.75.1" }, { "name": "styleguide", "version": "0.44.0" }, { "name": "symbols-view", "version": "0.100.0" }, { "name": "tabs", "version": "0.82.0" }, { "name": "timecop", "version": "0.31.0" }, { "name": "tree-view", "version": "0.180.0" }, { "name": "update-package-dependencies", "version": "0.10.0" }, { "name": "whitespace", "version": "0.30.0" }, { "name": "wrap-guide", "version": "0.35.0" }, { "name": "language-c", "version": "0.45.0" }, { "name": "language-clojure", "version": "0.16.0" }, { "name": "language-coffee-script", "version": "0.41.0" }, { "name": "language-csharp", "version": "0.6.0" }, { "name": "language-css", "version": "0.32.1" }, { "name": "language-gfm", "version": "0.80.0" }, { "name": "language-git", "version": "0.10.0" }, { "name": "language-go", "version": "0.31.0" }, { "name": "language-html", "version": "0.40.0" }, { "name": "language-hyperlink", "version": "0.14.0" }, { "name": "language-java", "version": "0.15.0" }, { "name": "language-javascript", "version": "0.85.0" }, { "name": "language-json", "version": "0.15.0" }, { "name": "language-less", "version": "0.28.1" }, { "name": "language-make", "version": "0.14.0" }, { "name": "language-mustache", "version": "0.12.0" }, { "name": "language-objective-c", "version": "0.15.0" }, { "name": "language-perl", "version": "0.28.0" }, { "name": "language-php", "version": "0.27.0" }, { "name": "language-property-list", "version": "0.8.0" }, { "name": "language-python", "version": "0.38.0" }, { "name": "language-ruby", "version": "0.57.0" }, { "name": "language-ruby-on-rails", "version": "0.22.0" }, { "name": "language-sass", "version": "0.40.0" }, { "name": "language-shellscript", "version": "0.15.0" }, { "name": "language-source", "version": "0.9.0" }, { "name": "language-sql", "version": "0.17.0" }, { "name": "language-text", "version": "0.7.0" }, { "name": "language-todo", "version": "0.25.0" }, { "name": "language-toml", "version": "0.16.0" }, { "name": "language-xml", "version": "0.30.0" }, { "name": "language-yaml", "version": "0.22.0" } ] ``` ================================================ FILE: 03-FEND_Note-master/chapter1/02_dev_tools.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [开发及调试工具](#%E5%BC%80%E5%8F%91%E5%8F%8A%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7) ## 开发及调试工具 - 文本编辑器或 IDE (集成开发环境) - Google Chrome, Firefox Firebug, Safari Developer Tool NOTE: [Google Chrome DevTools Doc](https://developer.chrome.com/devtools) ================================================ FILE: 03-FEND_Note-master/chapter1/03_01_html_intro.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [HTML 简介](#html-%E7%AE%80%E4%BB%8B) ### HTML 简介 ![](../img/H/html-overview.png) 注意事项: - `` 必须首行定格 - `` 为文档标题 - `<meta charset="utf-8">` 文档解码格式 - `<meta name="keywords" content="...">` 和 `<meta name="description" content="...">` 提供给搜索引擎使用 - `<meta name="viewport" content="width=device-width, initial-scale=1.0">` 移动端浏览器的宽高与缩放 - `<link>` 标签可以引入 favicon 和样式表 CSS 文件 ================================================ FILE: 03-FEND_Note-master/chapter1/03_02_html_sytax.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [HTML 语法](#html-%E8%AF%AD%E6%B3%95) - [全局属性](#%E5%85%A8%E5%B1%80%E5%B1%9E%E6%80%A7) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### HTML 语法 ![](../img/H/html-syntax.png) **书写规范**: - 小写标签和属性 - 属性值双引号 - 代码因嵌套缩进 #### 全局属性 - id, `<div id='unique-element'></div>`,页面中唯一 - class,`<button class='btn'>Click Me</button>`,页面中可重复出现 - style,尽量避免 - title,对于元素的描述类似于 Tooltip 的效果。 ================================================ FILE: 03-FEND_Note-master/chapter1/03_03_html_ascii_encoding.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [实体字符](#%E5%AE%9E%E4%BD%93%E5%AD%97%E7%AC%A6) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 实体字符 实体字符(ASCII Encoding Reference)是用来在代码中以实体代替与HTML语法相同的字符,避免浏览解析错误。它的两种表示方式,第一种为 `&` 外加实体字符名称,例如 ` `,第二种为 `&` 加实体字符序号,例如 ` `。 <table> <caption>常用HTML字符实体(建议使用实体):</caption> <thead> <tr> <th>字符</th> <th>名称</th> <th>实体名</th> <th>实体数</th> </tr> </thead> <tbody> <tr> <td>"</td> <td>双引号</td> <td>&quot;</td> <td>&#34;</td> </tr> <tr> <td>&</td> <td>&符</td> <td>&amp;</td> <td>&#38;</td> </tr> <tr> <td><</td> <td>左尖括号(小于号)</td> <td>&lt;</td> <td>&#60;</td> </tr> <tr> <td>></td> <td>右尖括号(大于号)</td> <td>&gt;</td> <td>&#62;</td> </tr> <tr> <td> </td> <td>空格</td> <td>&nbsp;</td> <td>&#160;</td> </tr> <tr> <td> </td> <td>中文全角空格</td> <td>&amp;</td> <td>&#12288;</td> </tr> </tbody> </table> <table> <caption>常用特殊字符实体(不建议使用实体):</caption> <thead> <tr> <th>字符</th> <th>名称</th> <th>实体名</th> <th>实体数</th> </tr> </thead> <tbody> <tr> <td>¥</td> <td>元</td> <td>&yen;</td> <td>&#165;</td> </tr> <tr> <td>¦</td> <td>断竖线</td> <td>&brvbar;</td> <td>&#166;</td> </tr> <tr> <td>©</td> <td>版权</td> <td>&copy;</td> <td>&#169;</td> </tr> <tr> <td>®</td> <td>注册商标R</td> <td>&reg;</td> <td>&#174;</td> </tr> <tr> <td>™</td> <td>商标TM</td> <td>&trade;</td> <td>&#8482;</td> </tr> <tr> <td>·</td> <td>间隔符</td> <td>&middot;</td> <td>&#183;</td> </tr> <tr> <td>«</td> <td>左双尖括号</td> <td>&laquo;</td> <td>&#171;</td> </tr> <tr> <td>»</td> <td>右双尖括号</td> <td>&raquo;</td> <td>&#187;</td> </tr> <tr> <td>°</td> <td>度</td> <td>&deg;</td> <td>&#176;</td> </tr> <tr> <td>×</td> <td>乘</td> <td>&times;</td> <td>&#215;</td> </tr> <tr> <td>÷</td> <td>除</td> <td>&divide;</td> <td>&#247;</td> </tr> <tr> <td>‰</td> <td>千分比</td> <td>&permil;</td> <td>&#8240;</td> </tr> </tbody> </table> NOTE:具体所需可在使用时查询,无需记忆。 ================================================ FILE: 03-FEND_Note-master/chapter1/03_04_cross_browser.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [浏览器兼容](#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 浏览器兼容 主流浏览器都兼容 HTML5 的新标签,对于 IE8 及以下版本不认识 HTML5的新元素,可以使用 JavaScript 创建一个没用的元素来解决,例如: ```javascript <script> document.createElement("header"); </script> ``` 也可以使用 shiv 来解决兼容性问题,详情可参考 [HTML5 Shiv](https://github.com/afarkas/html5shiv) ================================================ FILE: 03-FEND_Note-master/chapter1/03_05_html_tags.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [HTML 标签](#html-%E6%A0%87%E7%AD%BE) - [文本标签](#%E6%96%87%E6%9C%AC%E6%A0%87%E7%AD%BE) - [组合内容标签](#%E7%BB%84%E5%90%88%E5%86%85%E5%AE%B9%E6%A0%87%E7%AD%BE) - [嵌入](#%E5%B5%8C%E5%85%A5) - [资源标签](#%E8%B5%84%E6%BA%90%E6%A0%87%E7%AD%BE) - [图标签](#%E5%9B%BE%E6%A0%87%E7%AD%BE) - [热点区域标签](#%E7%83%AD%E7%82%B9%E5%8C%BA%E5%9F%9F%E6%A0%87%E7%AD%BE) - [表格](#%E8%A1%A8%E6%A0%BC) - [表单](#%E8%A1%A8%E5%8D%95) - [语义化](#%E8%AF%AD%E4%B9%89%E5%8C%96) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### HTML 标签 **[HTML5 标签集合](http://www.html5star.com/manual/html5label-meaning)** ![](../img/H/html-elements.jpg) #### 文档章节 `<body>` 页面内容 `<header>` 文档头部 `<nav>` 导航 `<aside>` 侧边栏 `<article>` 定义外部内容(如外部引用的文章) `<section>` 一个独立的块 `<footer>` 尾部 **页面通常结构** ![Web Structure](../img/S/structure.gif) #### 文本标签 ```html <!-- 默认超链接 --> <a href="http://sample-link.com" title="Sample Link">Sample</a> <!-- 当前窗口显示 --> <a href="http://sample-link.com" title="Sample Link" target="_self">Sample</a> <!-- 新窗口显示 --> <a href="http://sample-link.com" title="Sample Link" target="_blank">Sample</a> <!-- iframe 中打开链接 --> <a href="http://sample-link.com" title="Sample Link" target="iframe-name">Sample</a> <iframe name="iframe-name" frameborder="0"></iframe> <!-- 页面中的锚点 --> <a href="#achor">Achor Point</a> <section id="achor">Achor Content</section> <!-- 邮箱及电话需系统支持 --> <a href="mailto:sample-address@me.com" title="Email">Contact Us</a> <!-- 多个邮箱地址 --> <a href="mailto:sample-address@me.com, sample-address0@me.com" title="Email">Contact Us</a> <!-- 添加抄送,主题和内容 --> <a href="mailto:sample-address@me.com?cc=admin@me.com&subject=Help&body=sample-body-text" title="Email">Contact Us</a> <!-- 电话示例 --> <a href="tel:99999999" title="Phone">Ring Us</a> ``` #### 组合内容标签 - `<div>` - `<p>` - `<ol>` - `<ul>` - `<dl>` - `<pre>` - `<blockquote>` #### 文档章节 `<body>` 页面内容 `<header>` 文档头部 `<nav>` 导航 `<aside>` 侧边栏 `<article>` 定义外部内容(如外部引用的文章) `<section>` 一个独立的块 `<footer>` 尾部 #### 引用 - `<cite>` 引用作品的名字、作者的名字等 - `<q>` 引用一小段文字(大段文字引用用`<blockquote>`) - `<blockquote>` 引用大块文字 - `<pre>` 保存格式化的内容(其空格、换行等格式不会丢失) ```html <pre> <code> int main(void) { printf('Hello, world!'); return 0; } </code> </pre> ``` #### 代码 `<code>` 引用代码 #### 格式化 `<b>` 加粗 `<i>` 斜体 #### 强调 `<em>` 斜体。着重于强调内容,会改变语义的强调 `<strong>` 粗体。着重于强调内容的重要性 #### 换行 `<br>` 换行 #### 列表 **无序列表** ```html <ul> <li>标题</li> <li>结论</li> </ul> ``` **有序列表** ```html <ol> <li>第一</li> <li>第二</li> </ol> ``` **自定义列表** ```html <dl> <dt>作者</dt> <dd>爱因斯坦</dd> <dt>作品</dt> <dd>《相对论》</dd> <dd>《时间与空间》</dd> </dl> ``` 一个`<dt>`可以对应多个`<dd>` NOTE: `<dl>` 为自定义列表,其中包含一个或多个 `<dt>` 及 一个或多个 `<dd>`,并且`dt` 与 `dl`列表会有缩进的效果。`<pre>` 会保留换行和空格,通常与 `<code>` 一同使用。 ```html <pre> <code> int main(void) { printf('Hello, world!'); return 0; } </code> </pre> ``` `<blockquote>` 拥有 `cite` 属性,它包含引用文本的出处,示例如下所示: ```html <blockquote cite="http://example.com/facts"> <p>Sample Quote...</p> </blockquote> ``` #### 嵌入 ```html <iframe src=""></iframe> 页面操作可以不影响到iframe的内容 <!--object embed通常用来嵌入外部资源 --> <object type="application/x-shockwave-player"> <param name="movie" value="book.pdf"> </object> <!--视频 track可以引入字幕 autoplay可以使视频加载后自动播放,loop可以使其循环播放 --> <video autoplay loop controls="controls" poster="poster.jpg"> <source src="movie.mp4" type="video/mp4"> <source src="movie.webm" type="video/webm"> <source src="movie.ogg" type="video/ogg"> <track kind="subtitles" src="video.vtt" srclang="cn" label="cn"> </video> ``` #### 资源标签 ##### 图标签 `canvas` 基于像素,性能要求比较高,可用于实时数据展示。`svg` 为矢量图形图像。 ##### 热点区域标签 `img`中套用`map`以及`area`可以实现点击某部分图片触发一个链接,点击另一部分触发另一个链接 ```html <img src="mama.jpg" width=100 height=100 usemap="#map" /> <map name="map"> <area shap="rect" coords="0,0,50,50" href="" alt=""> <area shap="circle" coords="75,75,25" href="" alt=""> </map> ``` #### 表格 **表格代码示例** ```html <table> <caption>table title and/or explanatory text</caption> <thead> <tr> <th>header</th> </tr> </thead> <tbody> <tr> <td>data</td> </tr> </tbody> </table> ``` 使用 `colspan=val` 进行跨列,使用 `rowspan=val` 进行跨行。 #### 表单 ```html <form action="WebCreation_submit" method="get" accept-charset="utf-8"> <fieldset> <legend>title or explanatory caption</legend> <!-- 第一种添加标签的方法 --> <label><input type="text/submit/hidden/button/etc" name="" value=""></label> <!-- 第二种添加标签的方法 --> <label for="input-id">Sample Label</label> <input type="text" id="input-id"> </fieldset> <fieldset> <legend>title or explanatory caption</legend> <!-- 只读文本框 --> <input type="text" readonly> <!-- 隐藏文本框,可提交影藏数据 --> <input type="text" name="hidden-info" value="hiden-info-value" hidden> </fieldset> <button type="submit">Submit</button> <button type="reset">Reset</button> </form> ``` 使用`fieldset`可用于对表单进行分区 表单中的其他控件类型: - `textarea` (文本框) - `select` 与 `option` (下拉菜单可多选) <table> <caption>input 类型支持值列表</caption> <tbody> <tr> <th style="width:22%">Value</th> <th>Description</th> </tr> <tr> <td>button</td> <td>Defines a clickable button (mostly used with a JavaScript to activate a script)</td> </tr> <tr> <td>checkbox</td> <td>Defines a checkbox</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">color</td> <td>Defines a color picker</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">date</td> <td>Defines a date control (year, month and day (no time))</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">datetime</td> <td>The input type datetime has been removed from the HTML standard. Use datetime-local instead.</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">datetime-local</td> <td>Defines a date and time control (year, month, day, hour, minute, second, and fraction of a second (no time zone)</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">email</td> <td>Defines a field for an e-mail address</td> </tr> <tr> <td>file</td> <td>Defines a file-select field and a "Browse..." button (for file uploads)</td> </tr> <tr> <td>hidden</td> <td>Defines a hidden input field</td> </tr> <tr> <td>image</td> <td>Defines an image as the submit button</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">month</td> <td>Defines a month and year control (no time zone)</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">number</td> <td>Defines a field for entering a number</td> </tr> <tr> <td>password</td> <td>Defines a password field (characters are masked)</td> </tr> <tr> <td>radio</td> <td>Defines a radio button</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">range</td> <td>Defines a control for entering a number whose exact value is not important (like a slider control)</td> </tr> <tr> <td>reset</td> <td>Defines a reset button (resets all form values to default values)</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">search</td> <td>Defines a text field for entering a search string</td> </tr> <tr> <td>submit</td> <td>Defines a submit button</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">tel</td> <td>Defines a field for entering a telephone number</td> </tr> <tr> <td>text</td> <td>Default. Defines a single-line text field (default width is 20 characters)</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">time</td> <td>Defines a control for entering a time (no time zone)</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">url</td> <td>Defines a field for entering a URL</td> </tr> <tr> <td class="html5badge"><img src="../img/H/html5_badge20.png">week</td> <td>Defines a week and year control (no time zone)</td> </tr> </tbody> </table> #### 语义化 语义化(Semantic Tag)是指用合适的标签标识适当的内容,它可以起到搜索引擎优化(Search Engine Optimization),提高可访问性(例如盲人使用的屏幕阅读器),与此同时还可以提高代码的可读性。简而言之也就是**在正确的地方使用正确的标签** ================================================ FILE: 03-FEND_Note-master/chapter1/03_html.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [HTML](#html) - [HTML 历史](#html-%E5%8E%86%E5%8F%B2) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ## HTML ### HTML 历史 HTML (Hyper Text Markup Language),用于标记页面中的内容。 ![](../img/H/html-history.png) ================================================ FILE: 03-FEND_Note-master/chapter1/04_01_css_sytax.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [语法](#%E8%AF%AD%E6%B3%95) - [浏览器私有属性](#%E6%B5%8F%E8%A7%88%E5%99%A8%E7%A7%81%E6%9C%89%E5%B1%9E%E6%80%A7) - [属性值语法](#%E5%B1%9E%E6%80%A7%E5%80%BC%E8%AF%AD%E6%B3%95) - [基本元素](#%E5%9F%BA%E6%9C%AC%E5%85%83%E7%B4%A0) - [组合符号](#%E7%BB%84%E5%90%88%E7%AC%A6%E5%8F%B7) - [数量符号](#%E6%95%B0%E9%87%8F%E7%AC%A6%E5%8F%B7) - [@规则语法](#@%E8%A7%84%E5%88%99%E8%AF%AD%E6%B3%95) - [@规则](#@%E8%A7%84%E5%88%99) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 语法 ```css /* 选择器 */ .m-userlist { /* 属性声明 */ margin: 0 0 30px; /* 属性名:属性值; */ } .m-userlist .list { position: relative; height: 100px; overflow: hidden; } ``` #### 浏览器私有属性 - Google Chrome, Safari (`-webkit`) - Firefox (`-moz-`) - IE (`-ms-`) - Opera (`-o-`) ```css .pic { -webkit-transform: rotate(-3deg); -ms-transform: rotate(-3deg); transform: rotate(-3deg); } ``` NOTE: 使用 [http://pleeease.io/play/](http://pleeease.io/play/) ,CSS 预处理器(Sass,Less,Stylus)或编辑器插件可自动添加浏览器厂商的私有属性前缀。 #### 属性值语法 ```css margin: [ <length> | <percentage> | auto ]{1,4} /* 基本元素:<length>, <percentage>, auto*/ /* 组合符号:[], | */ /* 数量符号:{1,4} */ ``` ##### 基本元素 **关键字** - auto - solid - bold - ... **类型** - 基本类型 - `<length>` - `<percentage>` - `<color>` - ... - 其他类型 - <'padding-width'> - <'color-stop'> - 符号 - `/` - ',' - inherit, initial ##### 组合符号 - `<'font-size'> <'font-family'>` (` ` 两项必存,顺序毕遵) - 合法:`12px arial` - 不合法:`2em` - 不合法:`arial 14px` - `<length>&&<color>` (`&&` 两项必存,顺序无碍) - 合法:green 2px - 合法:1em orange - 不合法:blue - `underline || overline || line-through || blink` (`||` 至少选一,顺序无碍) - 合法:underline - 合法:overline underline - `<color> | transparent`(`|` 只可选一,不可共存) - 合法:orange - 合法:transparent - 不合法:orange transparent - `bold [thin || <length>]`(`[]` 分组之用,视为整体) - 合法:bold thin - 合法:bold 2em ##### 数量符号 - `<length>`(无则表示仅可出现一次) - 合法:1px - 合法:10em - 不合法:1px 2px - `<color-stop>[, <color-stop>]+` (`+` 可出现一次或多次) - 合法:#fff, red - 合法:blue, green 50%, gray - 不合法:red - `inset?&&<color>` (`?` 表示可选) - 合法:inset orange - 合法:red - `<length>{2,4}` (`{2,4}` 可出现次数和最少最多出现次数) - 合法:1px 2px - 合法:1px 2px 3px - 不合法: 1px - 不合法:1px 2px 3px 4px 5px - `<time>[, <time>]*`(`*` 出现 0 次或多次) - 合法:1s - 合法:1s,4ms - `<time>#`(`#` 出现一次或者多次,用`,`分隔) - 合法:2s, 4s - 不合法:1s 2s **CSS 规则示例** ![](../img/C/css-value-rule.png) #### @规则语法 ``` @import "subs.css"; @charset "utf-8"; @media print { /* property: value */ } @keyframes fadein { /* property: value */ } ``` - `@标示符 内容;` - `@标示符 内容{}` ##### @规则 **常用的规则** - `@media` (用于响应式布局) - `@keyframes` (CSS 动画的中间步骤) - `@font-face` (引入外部字体) **其他规则**(不常用) - `@import` - `@charset` - `@namespace` - `@page` - `@supports` - `@document` ================================================ FILE: 03-FEND_Note-master/chapter1/04_02_selector.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [选择器](#%E9%80%89%E6%8B%A9%E5%99%A8) - [简单选择器](#%E7%AE%80%E5%8D%95%E9%80%89%E6%8B%A9%E5%99%A8) - [标签选择器](#%E6%A0%87%E7%AD%BE%E9%80%89%E6%8B%A9%E5%99%A8) - [类选择器](#%E7%B1%BB%E9%80%89%E6%8B%A9%E5%99%A8) - [id 选择器](#id-%E9%80%89%E6%8B%A9%E5%99%A8) - [通配符选择器](#%E9%80%9A%E9%85%8D%E7%AC%A6%E9%80%89%E6%8B%A9%E5%99%A8) - [属性选择器](#%E5%B1%9E%E6%80%A7%E9%80%89%E6%8B%A9%E5%99%A8) - [伪类选择器](#%E4%BC%AA%E7%B1%BB%E9%80%89%E6%8B%A9%E5%99%A8) - [其他选择器](#%E5%85%B6%E4%BB%96%E9%80%89%E6%8B%A9%E5%99%A8) - [伪元素选择器](#%E4%BC%AA%E5%85%83%E7%B4%A0%E9%80%89%E6%8B%A9%E5%99%A8) - [组合选择器](#%E7%BB%84%E5%90%88%E9%80%89%E6%8B%A9%E5%99%A8) - [选择器分组](#%E9%80%89%E6%8B%A9%E5%99%A8%E5%88%86%E7%BB%84) - [继承、优先、层级](#%E7%BB%A7%E6%89%BF%E3%80%81%E4%BC%98%E5%85%88%E3%80%81%E5%B1%82%E7%BA%A7) - [继承](#%E7%BB%A7%E6%89%BF) - [优先](#%E4%BC%98%E5%85%88) - [改变优先级](#%E6%94%B9%E5%8F%98%E4%BC%98%E5%85%88%E7%BA%A7) - [层叠](#%E5%B1%82%E5%8F%A0) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 选择器 <p data-height="268" data-theme-id="15197" data-slug-hash="mJmwdK" data-default-tab="result" data-user="li-xinyang" class='codepen'>See the Pen <a href='http://codepen.io/li-xinyang/pen/mJmwdK/'>FEND_Selectors</a> by Li Xinyang (<a href='http://codepen.io/li-xinyang'>@li-xinyang</a>) on <a href='http://codepen.io'>CodePen</a>.</p> <script async src="//assets.codepen.io/assets/embed/ei.js"></script> 选择器可被看做表达式,通过它可以选择相应的元素并应用不同的样式。 - 简单选择器 - 元素选择器 - 组合选择器 #### 简单选择器 简单选择器可组合使用。 ##### 标签选择器 ```html <div> <p>Sample Paragraph</p> <p>Sample Paragraph</p> <p>Sample Paragraph</p> </div> <style type="text/css"> p { color: blue; } </style> ``` ##### 类选择器 `.className` 以 `.` 开头,名称可包含字母,数字,`-`,`_`,但必须以字母开头。它区分大小写并可出现多次。 ```html <div> <p>Sample Paragraph</p> <p class="special bold">Sample Paragraph</p> <p>Sample Paragraph</p> </div> <style type="text/css"> p { color: blue } .special { color: orange; } .bold { font-weight: bold; } </style> ``` ##### id 选择器 `#idName` 以 `#` 开头且只可出现**一次**,其命名要求于 `.className` 相同。 ```html <div> <p id="special">Sample Paragraph</p> </div> <style type="text/css"> #special { color: red; } </style> ``` ##### 通配符选择器 ```html <div> <p>Sample Paragraph</p> <p>Sample Paragraph</p> </div> <style type="text/css"> * { color: blue; } </style> ``` ##### 属性选择器 `[attr]` 或 `[attr=val]` 来选择相应的元素。`#nav{...}` 既等同于 `[id=nav]{...}`。IE7+ `[attr~=val]` 可选用与选择包含 `val` 属性值的元素,像`class="title sports"` 与 `class="sports"`。`.sports{...}` 既等同于 `[class~=sports]{...}` IE7+ `[attr|=val]` 可以选择`val`开头及开头紧接`-`的属性值。IE7+ `[attr^=val]` 可选择以`val`开头的属性值对应的元素,如果值为符号或空格则需要使用引号 `""`。IE7+ `[attr$=val]` 可选择以`val`结尾的属性值对应的元素。IE7+ `[attr*=val]` 可选择以包含`val`属性值对应的元素。IE7+ ```html <div> <form action=""> <input type="text" value="Xinyang" disabled> <input type="password" placeholder="Password"> <input type="button" value="Button"> </form> </div> <style type="text/css"> [disabled] { background-color: orange; } [type=button] { color: blue; } </style> ``` ##### 伪类选择器 **常用伪类选择器**: - `:link` IE6+ - `:visited` IE7+ - `:hover` IE6中仅可用于链接 - `:active` IE6/7中仅可用于链接 - `:enabled` IE9+ - `:disabled` IE9+ - `:checked` IE9+ - `:first-child` IE8+ - `:last-child` IE9+ - `:nth-child(even)` 可为 `odd` `even` 或数字 IE9+ - `:nth-last-child(n)` `n`从 0 开始计算 IE9+ - `:only-child` 仅选择唯一的元素 IE9+ - `:only-of-type` IE9+ - `:first-of-type` IE9+ - `:last-of-type` IE9+ - `:nth-of-type(even)` IE9+ - `:nth-last-of-type(2n)` IE9+ **不常用伪类选择器**: - `:empty` 选中页面中无子元素的标签 IE9+ - `:root` 选择 HTML 根标签 IE9+ - `:not()` 参数为一般选择器 IE9+ - `:target` 被锚点选中的目标元素 IE9+ - `:lang()` 选中语言值为某类特殊值的元素 IE7+ NOTE:`element:nth-of-type(n)` 指父元素下第 n 个 `element` 元素,`element:nth-child(n)` 指父元素下第 n 个元素且元素为 `element`,若不是,选择失败。具体细节请在使用时查找文档。 ```html <div> <a href="http://sample-site.com" title="Sample Site">Sample Site</a> </div> <style type="text/css"> /* 伪类属性定义有顺序要求! */ a:link { color: gray; } a:visited { color:red; } a:hover { color: green; /* 鼠标悬停 */ } a:active { color: orange; /* 鼠标点击 */ } </style> ``` #### 其他选择器 ##### 伪元素选择器 注意与伪类学则器的区分。 - `::first-letter` IE6+ - `::first-line` IE6+ - `::before{content: "before"}` 需与 `content` 一同使用 IE8+ - `::after{content: "after"}` 需与 `content` 一同使用 IE8+ - `::selection` 被用户选中的内容(鼠标选择高亮属性)IE9+ Firefox需用 `-moz` 前缀 ##### 组合选择器 - 后代选择器 `.main h2 {...}`,使用` `表示 IE6+ - 子选择器 `.main>h2 {...}`,使用`>`表示 IE7+ - 兄弟选择器 `h2+p {...}`,使用`+`表示 IE7+ - `h2~p {...}`,使用`~`表示(此标签无需紧邻)IE7+ ##### 选择器分组 ```html <style type="text/css"> /* 下面两组样式声明效果一致 */ h1 {color: red;} h2 {color: red;} h3 {color: red;} h1, h2, h3 {color: red;} </style> ``` #### 继承、优先、层级 ##### 继承 子元素继承父元素的样式,但并不是所有属性都是默认继承的。通过文档中的 `inherited: yes` 来判断属性是否可以自动继承。 ![](../img/C/css-inherit-doc.png) 自动继承属性: - color - font - text-align - list-style - ... 非继承属性: - background - border - position - ... ##### 优先 CSS Specificity Calculator 可以在[这里](http://specificity.keegan.st/)找到。更多关于 CSS 优先级别的信息可以在[这里](https://css-tricks.com/specifics-on-css-specificity/)找到(英文)。 计算方法: - a = 行内样式 - b = id 选择器的数量 - c = 类、伪类的属性选择器的数量 - d = 标签选择器和伪元素选择器的数量 NOTE:从上到下优先级一次降低,且优先级高的样式会将优先级低的样式覆盖。大致公式(并不准确)如下。 ``` value = a * 1000 + b * 100 + c * 10 + d ``` ###### 改变优先级 - 改变样式声明先后顺序 - 提升选择器优先级 - `!important`(慎用) ##### 层叠 层叠为相同属性根据优先级覆盖,如优先级相同则后面会覆盖前面的属性,而不同属性则会合并。 ================================================ FILE: 03-FEND_Note-master/chapter1/04_03_text.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [文本](#%E6%96%87%E6%9C%AC) - [字体](#%E5%AD%97%E4%BD%93) - [改变字号](#%E6%94%B9%E5%8F%98%E5%AD%97%E5%8F%B7) - [改变字体](#%E6%94%B9%E5%8F%98%E5%AD%97%E4%BD%93) - [加粗字体](#%E5%8A%A0%E7%B2%97%E5%AD%97%E4%BD%93) - [倾斜字体](#%E5%80%BE%E6%96%9C%E5%AD%97%E4%BD%93) - [更改行距](#%E6%9B%B4%E6%94%B9%E8%A1%8C%E8%B7%9D) - [font shorthand](#font-shorthand) - [改变文字颜色](#%E6%94%B9%E5%8F%98%E6%96%87%E5%AD%97%E9%A2%9C%E8%89%B2) - [对齐方式](#%E5%AF%B9%E9%BD%90%E6%96%B9%E5%BC%8F) - [文字居中](#%E6%96%87%E5%AD%97%E5%B1%85%E4%B8%AD) - [文本垂直对齐](#%E6%96%87%E6%9C%AC%E5%9E%82%E7%9B%B4%E5%AF%B9%E9%BD%90) - [文本缩进](#%E6%96%87%E6%9C%AC%E7%BC%A9%E8%BF%9B) - [格式处理](#%E6%A0%BC%E5%BC%8F%E5%A4%84%E7%90%86) - [保留空格格式](#%E4%BF%9D%E7%95%99%E7%A9%BA%E6%A0%BC%E6%A0%BC%E5%BC%8F) - [文字换行](#%E6%96%87%E5%AD%97%E6%8D%A2%E8%A1%8C) - [文本装饰](#%E6%96%87%E6%9C%AC%E8%A3%85%E9%A5%B0) - [文字阴影](#%E6%96%87%E5%AD%97%E9%98%B4%E5%BD%B1) - [文本装饰(下划线等)](#%E6%96%87%E6%9C%AC%E8%A3%85%E9%A5%B0%EF%BC%88%E4%B8%8B%E5%88%92%E7%BA%BF%E7%AD%89%EF%BC%89) - [高级设置](#%E9%AB%98%E7%BA%A7%E8%AE%BE%E7%BD%AE) - [省略字符](#%E7%9C%81%E7%95%A5%E5%AD%97%E7%AC%A6) - [更换鼠标形状](#%E6%9B%B4%E6%8D%A2%E9%BC%A0%E6%A0%87%E5%BD%A2%E7%8A%B6) - [强制继承](#%E5%BC%BA%E5%88%B6%E7%BB%A7%E6%89%BF) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 文本 <p data-height="268" data-theme-id="15197" data-slug-hash="mJmwej" data-default-tab="result" data-user="li-xinyang" class='codepen'>See the Pen <a href='http://codepen.io/li-xinyang/pen/mJmwej/'>FEND_Fonts</a> by Li Xinyang (<a href='http://codepen.io/li-xinyang'>@li-xinyang</a>) on <a href='http://codepen.io'>CodePen</a>.</p> <script async src="//assets.codepen.io/assets/embed/ei.js"></script> #### 字体 ##### 改变字号 `font-size: <absolute-size> | <relative-size> | <length> | <percentage> | inherit` - `<absolute-size>` 有 small large medium - `<relative-size>` 有 smaller larger ```stylus div font-size 12px p#sample0 font-size 16px p#sample1 font-size 2em p#sample2 font-size 200% ``` NOTE:以上两值在开发中并不常用。`2em` 与 `200%` 都为父元素默认大小的两倍(参照物为父元素的字体大小 `12px`)。 ##### 改变字体 `font-family: [ <family-name> | <generic-family> ]# ` `<generic-family>` 可选选项,但具体使用字体由浏览器决定 - serif - sans-serif - cursive - fantasy - monospace ```css font-family: arial, Verdana, sans-serif; ``` NOTE:优先使用靠前的字体 ##### 加粗字体 `font-weight: normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900` ```css font-weight: normal; font-weight: bold; ``` ##### 倾斜字体 `font-style: normal | italic | oblique | inherit` `italic` 使用字体中的斜体,而 `oblique` 在没有斜体字体时强制倾斜字体。 ##### 更改行距 `line-height: normal | <number> | <length> | <percentage>` `normal` 值为浏览器决定,在1.1至1.2之间(通常设置值为1.14左右) ```css /* length 类型 */ line-height: 40px; line-height: 3em; /* percentage 类型 */ line-height: 300%; /* number 类型 */ line-height: 3; ``` NOTE:当`line-height`为 `number` 类型时,子类直接继承其数值(不计算直接继承)。 而当为 `percentage` 类型时,子类则会先计算再显示(先计算后继承)。 ##### 字间距(字母间距) `letter-spacing: normal | <length>` 其用于设置字间距或者字母间距,此属性适用于中文或西文中的字母。 如果需要设置西文中词与词的间距或标签直接的距离则需要使用 `word-spacing`。 `word-spacing: normal | <length>` ##### font shorthand `font: [ [ <‘font-style’> || <font-variant-css21> || <‘font-weight’> || <‘font-stretch’> ]? <‘font-size’> [ / <‘line-height’> ]? <‘font-family’> ] | caption | icon | menu | message-box | small-caption | status-bar` ```css font: 30px/2 "Consolas", monospace; font: italic bold 20px/1.5 arial, serif; font: 20px arial, serif; ``` NOTE:当其他值为空时,均被设置为默认值。 ##### 改变文字颜色 `color: <color>` ```css element { color: red; } element { color: #f00; } element { color: #ff0000; } element { color: rgb(255,0,0); } element { color: rgb(100%, 0%, 0%); } element { color: hsl(0, 100%, 50%); } /* 50% translucent */ element { color: rgba(255, 0, 0, 0.5); } element { color: hsla(0, 100%, 50%, 0.5); } /* 全透明 */ element { color: transparent' } element { color: rgba(0, 0, 0, 0); } ``` #### 对齐方式 ##### 文字居中 `text-align: start | end | left | right | center | justify | match-parent | start end` NOTE:默认为文本左对齐。 ##### 文本垂直对齐 `vertical-align: baseline | sub | super | text-top | text-bottom | middle | top | bottom | <percentage> | <length>` NOTE:`<percentage>`的参照物为`line-height` ##### 文本缩进 `text-indent: <length> | <percentage> && [ hanging || each-line ]` NOTE:缩进两个字可使用 `text-indent: 2em;` #### 格式处理 ##### 保留空格格式 `white-space: normal | pre | nowrap | pre-wrap | pre-line` `pre` 行为同 `<pre>` 一致。 <table class="standard-table"> <thead> <tr> <th> </th> <th>New lines</th> <th>Spaces and tabs</th> <th>Text wrapping</th> </tr> </thead> <tbody> <tr> <th><code>normal</code></th> <td>Collapse</td> <td>Collapse</td> <td>Wrap</td> </tr> <tr> <th><code>nowrap</code></th> <td>Collapse</td> <td>Collapse</td> <td>No wrap</td> </tr> <tr> <th><code>pre</code></th> <td>Preserve</td> <td>Preserve</td> <td>No wrap</td> </tr> <tr> <th><code>pre-wrap</code></th> <td>Preserve</td> <td>Preserve</td> <td>Wrap</td> </tr> <tr> <th><code>pre-line</code></th> <td>Preserve</td> <td>Collapse</td> <td>Wrap</td> </tr> </tbody> </table> ##### 文字换行 `word-wrap: normal | break-word` NOTE:允许长单词自动换行。 `word-break: normal | break-all | keep-all` NOTE:`break-all` 单词中的任意字母间都可以换行。 #### 文本装饰 ##### 文字阴影 `text-shadow:none | <shadow-t>#` 或 `text-shadow:none | [<length>{2,3}&&<color>?]#` ```css p { text-shadow: 1px 1px 1px #000, 3px 3px 5px blue; } ``` 1. value = The X-coordinate X 轴偏移像素 2. value = The Y-coordinate Y 轴偏移像素 3. value = The blur radius 阴影模糊半径 4. value = The color of the shadow 阴影颜色(默认为文字颜色) ##### 文本装饰(下划线等) `text-decoration: <'text-decoration-line'> || <'text-decoration-style'> || <'text-decoration-color'>` ```css h1.under { text-decoration: underline; } h1.over { text-decoration: overline; } p.line { text-decoration: line-through; } p.blink { text-decoration: blink; } a.none { text-decoration: none; } p.underover { text-decoration: underline overline; } ``` #### 高级设置 ##### 省略字符 `text-overflow: [ clip | ellipsis | <string> ]{1,2}` ```css /* 常用配合 */ text-overflow: ellipsis; overflow: hidden; /* 溢出截取 */ white-space: nowrap; /* 禁止换行 */ ``` ##### 更换鼠标形状 `cursor: [[<funciri>,]* [ auto | crosshair | default | pointer | move | e-resize | ne-resize | nw-resize | n-resize | se-resize | sw-resize | s-resize | w-resize| text | wait | help ]] | inherit` **常用属性** `cursor: [<uri>,]*[auto | default | none | help | pointer | zoom-in | zoom-out | move]` - `<uri>` 图片资源地址代替鼠标默认形状 - `<default>` 默认光标 - `<none>` 隐藏光标 - `<pointer>` 手型光标 - `<zoom-in>` - `<zoom-out>` - `<move>` ```css cursor: pointer; cursor: url(image-name.cur), pointer; /* 当 uri 失效时或者则会起作用 */ ``` ##### 强制继承 `inherit` 会强制继承父元素的属性值。 ```css font-size: inherit; font-family: inherit; font-weight: inherit; ... word-wrap: inherit; work-break: inherit text-showdow: inherit ``` NOTE:具体在使用时可查询文档 ================================================ FILE: 03-FEND_Note-master/chapter1/04_04_box_model.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [盒模型](#%E7%9B%92%E6%A8%A1%E5%9E%8B) - [属性](#%E5%B1%9E%E6%80%A7) - [width](#width) - [height](#height) - [padding](#padding) - [margin](#margin) - [margin 合并](#margin-%E5%90%88%E5%B9%B6) - [border](#border) - [border-radius](#border-radius) - [overflow](#overflow) - [box-sizing](#box-sizing) - [box-shadow](#box-shadow) - [outline](#outline) - [TRBL](#trbl) - [值缩写](#%E5%80%BC%E7%BC%A9%E5%86%99) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 盒模型 盒子模型是网页布局的基石。它有**边框**、**外边距**、**内边距**、**内容**组成。 **盒子 3D 模型** ![](../img/B/box-model-3d.png) 盒子由上到下依次分为五层,它们自上而下的顺序是。 1. border 边框 2. content + padding 内容与内边距 3. background-image 背景图片 4. background-color 背景颜色 4. margin 外边距 <p data-height="268" data-theme-id="15197" data-slug-hash="qdmPEE" data-default-tab="result" data-user="li-xinyang" class='codepen'>See the Pen <a href='http://codepen.io/li-xinyang/pen/qdmPEE/'>FEND_003_BoxModel</a> by Li Xinyang (<a href='http://codepen.io/li-xinyang'>@li-xinyang</a>) on <a href='http://codepen.io'>CodePen</a>.</p> <script async src="//assets.codepen.io/assets/embed/ei.js"></script> #### 属性 ![](../img/B/box-module.jpg) ##### width **内容盒子宽** `width: <length> | <percentage> | auto | inherit` NOTE:通常情况下百分比得参照物为元素的父元素。`max-width` 与 `min-width` 可以设置最大与最小宽度。 ##### height **内容盒子高** `height: <length> | <percentage> | auto | inherit` NOTE:默认情况元素的高度为内容高度。`max-height` 与 `min-height` 可以设置最大与最小高度。 ##### padding ![](../img/P/padding-sample.png) `padding: [<length> | <percentage>]{1,4} | inherit` ##### margin ![](../img/M/margin-sample.png) `margin: [<length> | <percentage> | auto]{1,4} | inherit` NOTE:`margin` 默认值为 `auto` Trick: ``` /* 可用于水平居中 */ margin: 0 auto; ``` ###### margin 合并 ![](../img/M/margin-merge.png) 毗邻元素外间距(margin)会合并,既取相对较大的值。父元素与第一个和最后一个子元素的外间距也可合并。 ##### border ![](../img/B/border-sample.png) ``` border: [<br-width> || <br-style> || <color>] | inherit border-width: [<length> | thin | medium | thick]{1,4} | inherit border-style: [solid | dashed | dotted | ...]{1,4} |inherit border-colro: [<color> | transparent]{1,4} | inherit ``` NOTE:`border-color` 默认为元素字体颜色。 ##### border-radius ![](../img/B/border-radius-sample1.png) ``` /* 水平半径/垂直半径 */ border-radius: [ <length> | <percentage> ]{1,4} [ / [ <length> | <percentage> ]{1,4} ]? ``` NOTE:四个角的分解属性由左上角顺时针附值。 ##### overflow ![](../img/O/overflow-sample.png) `overflow: visible | hidden | scroll | auto` NOTE:默认属性为 `visible`。使用 `overflow-x` 与 `overflow-y` 单独的设置水平和垂直方向的滚动条。 ##### box-sizing ![](../img/B/box-sizing.png) ![](../img/B/box-sizing1.png) `box-sizing: content-box | border-box | inherit` - `content-box` = 内容盒子宽高 + 填充(`Padding`)+ 边框宽(`border-width`) - `border-box` = 内容盒子宽高 ##### box-shadow ![](../img/B/box-shadow.png) `box-shadown: none | [inset? && [ <offset-x> <offset-y> <blur-radius>? <spread-radius>? <color>? ] ]#` ```html box-shadow: 4px 6px 3px 0px red; | | | | 水平偏移| | | 垂直偏移 | | 模糊半径 | 阴影大小 ``` NOTE:水平与垂直偏移可以为负值即相反方向偏移。颜色默认为文字颜色。阴影不占据空间,仅为修饰效果。 ##### outline ``` outline: [ <'outline-color'> || <'outline-style'> || <'outline-width'> ] outline-width: <length> | thin | medium | thick | inherit outline-style: solid | dashed | dotted | ... | inherit outline-color: <color> | invert | inherit /* invert 与当前颜色取反色 */ ``` NOTE:`outline` 与 `border` 相似但无法分别设置四个方向的属性。`outline` 并不占据空间,而 `border` 占据空间,且显示位于 `border` 以外。 #### TRBL ![](../img/T/TRBL.png) ![](../img/B/border-radius-sample.png) TRBL (Top, Right, Bottom, Left) 即为顺时针从顶部开始。具有四个方向的属性都可以通过 `*-top` `*-right` `*-bottom` 与 `*-left` 单独对其进行设置。 #### 值缩写 下面的值缩写以 `padding` 为例。 > 对面相等,后者省略;四面相等,只设一个。 ```html /* 四面值 */ padding: 20px; padding: 20px 20px 20px 20px; /* 上下值 右左值 */ padding: 20px 10px; padding: 20px 10px 20px 10px; /* 上值 右左值 下值 */ padding: 20px 10px 30px; padding: 20px 10px 30px 10px; ``` ================================================ FILE: 03-FEND_Note-master/chapter1/04_05_background.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [背景](#%E8%83%8C%E6%99%AF) - [background-color](#background-color) - [background-image](#background-image) - [background-repeat](#background-repeat) - [background-attachment](#background-attachment) - [background-position](#background-position) - [Sprite 的使用](#sprite-%E7%9A%84%E4%BD%BF%E7%94%A8) - [linear-gradient](#linear-gradient) - [radial-gradient](#radial-gradient) - [repeat-*-gradient](#repeat--gradient) - [background-origin](#background-origin) - [background-clip](#background-clip) - [background-size](#background-size) - [background shorthand](#background-shorthand) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 背景 #### background-color ``` background-color: <color> background-color: #f00; background-color: rgba(255, 0, 0, 0.5); background-color: transparent; /* 默认值 */ ``` #### background-image ``` background-image: <bg-image>[, <bg-image>]* /* <bg-image> = <image> | none */ background-image: url("../image/pic.png"); background-image: url("../image/pic.png0"), url("../image/pic1.png"); /* 多张背景图时,先引入的图片在上一层后引入则在下一层 */ ``` NOTE:当`background-color` 与 `background-image` 共存时,背景颜色永远在最底层(于背景图片之下)。 #### background-repeat `background-repeat` 需与背景图片数量一致。 ``` background-repeat: <repeat-style>[, <repeat-style]* <repeat-style> = repeat-x | repeat-y | [repeat | space | round | no-repeat]{1,2} /* X 轴 Y 轴 */ background-repeat: no-repeat repeat; ``` - `space` 平铺并在水平和垂直留有空隙,空隙的大小为图片均匀分布后完整覆盖显示区域的宽高 - `round` 不留空隙平铺且覆盖显示区域,图标会被缩放以达到覆盖效果(缩放不一定等比) #### background-attachment 当页面内容超过显示区域时,使用 `local` 使背景图片同页面内容一同滚动。 ``` background-attachment: <attachment>[, <attachment>]* <attachment> = scroll | fixed | local ``` #### background-position ``` background-position: <position>[, <position>]* <position> = [left|center|right|top|bottom|<percentage>|<length>]|[left|center|right|top|bottom|<percentage>|<length>] [left|center|right|top|bottom|<percentage>|<length>] | [center |[left|right][<percentage>|<length>]?]&&[center |[left|right][<percentage>|<length>]?] /* 默认位置为 */ background-position: 0 0; /* percentage 是容器与图片的百分比重合之处*/ background-position: 20% 50%; /* 等同效果 */ background-position: 50% 50%; background-position: center center; background-position: 0 0; background-position: left top; background-position: 100% 100%; background-position: right bottom; /* 四个值时方向只为参照物 */ background-position: right 10px top 20px; ``` ![](../img/B/background-position.jpg) ##### Sprite 的使用 ```html background-image: url(sprite.png) background-repeat: no-repeat; background-positon: 0 -100px ``` 使用位置为负值将图片偏移使需要的图片位置上移并显示正确的图案。 #### linear-gradient ``` linear-gradient() [[<angle> | to <side-or-corner],]? <color-step>[, <color-stop>]+ <side-or-corner> = [left | right] || [top | bottom] <color-stop> = <color> [<percentage> | <length>]? background-image: linear-gradient(red, blue); background-image: linear-gradient(to top, red, blue); background-image: linear-gradient(to right bottom, red, blue); background-image: linear-gradient(0deg, red, blue); background-image: linear-gradient(45deg, red, blue); background-image: linear-gradient(red, green, blue); background-image: linear-gradient(red, green 20%, blue); ``` ![](../img/L/linear-gradient.jpg) #### radial-gradient ``` radial-gradient( [ circle || <length> ] [ at <position> ]? , | [ ellipse || [<length> | <percentage> ]{2}] [ at <position> ]? , | [ [ circle | ellipse ] || <extent-keyword> ] [ at <position> ]? , | at <position> , <color-stop> [ , <color-stop> ]+ ) <extent-keyword> = closest-corner | closest-side | farthest-corner | farthest-side <color-stop> = <color> [ <percentage> | <length> ]? background-image: radial-gradient(cloest-side, red, blue); background-image: radial-gradient(circle, red, blue); background-image: radial-gradient(circle 100px, red, blue); background-image: radial-gradient(red, blue); background-image: radial-gradient(100px 50px, red, blue); background-image: radial-gradient(100px 50px at 0 0, red, blue); background-image: radial-gradient(red, green 20%, blue); ``` ![](../img/R/radial-gradient.jpg) #### repeat-*-gradient ``` background-image: repeating-linear-gradient(red, blue 20px, red 40px); background-image: repeating-radial-gradient(red, blue 20px, red 40px); ``` ![](../img/R/repeating-gradient.jpg) #### background-origin **案例模型** ![](../img/B/background-box-model.png) 决定背景 (0,0) 坐标与 100% 坐标的区域。默认值为 `padding-box`。 ``` <box>[, <box>]* <box> = border-box | padding-box | content-box background-image: url(red.png); background-repeat: no-repeat; background-origin: padding-box; background-origin: border-box; background-origin: content-box; ``` ![](../img/B/background-origin.jpg) #### background-clip 裁剪背景,默认值为`border-box`。 ``` <box>[, <box>]* <box> = border-box | padding-box | content-box background-image: url(red.png); background-repeat: no-repeat; background-clip: border-box; background-clip: padding-box; background-clip: content-box; ``` ![](../img/B/background-clip.jpg) #### background-size ``` <bg-size>[, <bg-size>]* <bg-size> = [<length> | <percentage> | auto] {1, 2} | cover | contain background-image: url(red.png); background-repeat: no-repeat; background-position: 50% 50%; background-size: auto; background-size: 20px 20px; /* % 参照物为容器*/ background-size: 50% 50%; /* 尽可能小,但宽度与高度不小过容器(充满容器) */ background-size: cover; /* 尽可能大,但宽度与高度不超过容器(最大完全显示)*/ background-size: contain; ``` ![](../img/B/background-size.jpg) #### background shorthand ``` [<bg-layer,]* <final-bg-layer> <bg-layer> = <bg-image> || <position> [/ <bg-size>]? || <repeat-style> || <attachment> || <box> || <box> /* 两个 <box> 第一个为 background-origin */ /* 两个 <box> 第二个为 background-clip */ /* 只出现一个 <box> 则即是 background-origin 也是 background-clip */ <final-bg-layer> = <bg-layer> || <'background-color'> background: url(red.png) 0 0/20px 20px no-repeat, url(blue.png) 50% 50%/contain no-repeat content-box green; ``` ![](../img/B/background-shorthand.png) ================================================ FILE: 03-FEND_Note-master/chapter1/04_06_layout.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [布局](#%E5%B8%83%E5%B1%80) - [display](#display) - [display:block](#displayblock) - [display:inline](#displayinline) - [display:inline-block](#displayinline-block) - [display:none](#displaynone) - [position](#position) - [position:relative](#positionrelative) - [position:absolute](#positionabsolute) - [position:fixed](#positionfixed) - [top/right/bottom/left](#toprightbottomleft) - [z-index](#z-index) - [z-index 栈](#z-index-%E6%A0%88) - [float](#float) - [clear](#clear) - [flex](#flex) - [flex 方向](#flex-%E6%96%B9%E5%90%91) - [flex-direction](#flex-direction) - [flex-wrap](#flex-wrap) - [flex-flow](#flex-flow) - [order](#order) - [flex 弹性](#flex-%E5%BC%B9%E6%80%A7) - [flex-basis](#flex-basis) - [flex-grow](#flex-grow) - [flex-shrink](#flex-shrink) - [flex](#flex-1) - [flex 对齐](#flex-%E5%AF%B9%E9%BD%90) - [justify-content](#justify-content) - [align-items](#align-items) - [align-self](#align-self) - [align-content](#align-content) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 布局 学习布局前须知道 CSS 中的定位机制。 - 标准文档流(Normal Flow) - 浮动(Float) - 绝对定位(Absolute Positioning) **标准文档流**,从上到下,从左到右的输出文档内容。它由*块级*(block)元素和*行级*元素组成,且它们都是盒子模型。 下面为 Firefox 布局可视化 **Gecko Reflow Visualisation**,布局是指浏览器将元素以正确的大小摆放在正确的位置上。 ![](../img/G/gecko-reflow-visualisation.gif) #### display 设置元素的显示方式 |display|默认宽度|可设置宽高|起始位置| |-------|--------|----------|--------| |block|父元素宽度|是|换行| |inline|内容宽度|否|同行| |inline-block|内容宽度|是|同行| ##### display:block - 默认宽高为父元素宽高 - 可设置宽高 - 换行显示 - 默认为block的元素:`<div>`, `<p>`, `<h1>` ~ `<h6>`, `<ul>`, `<form>` ##### display:inline - 默认宽度为内容宽度 - 不可设置宽高 - 同行显示(元素内部可换行) - 默认为inline的元素:`<span>`, `<a>`, `<label>`, `<cite>`, `<em>` ##### display:inline-block - 默认宽度为内容宽度 - 可设置宽高 - 同行显示 - 整块换行 - 默认为inline-block的元素:`<input>`, `<textarea>`, `<select>`, `<button>` ##### display:none - 设置元素不显示 `display:none` 与 `visibility:hidden` 的区别为 `display:none` 不显示且不占位,但 `visibility:hidden` 不显示但占位。 #### position `position` 用于设置定位的方式与`top` `right` `bottom` `left` `z-index` 则用于设置参照物位置(必须配合定位一同使用)。 **三种定位形式** - 静态定位(static) - 相对定位(relative) - 绝对定位(absolute、fixed) ``` position: static | relative | absolute | fixed /* 默认值为 static */ ``` ##### position:relative - 相对定位的元素仍在文档流之中,并按照文档流中的顺序进行排列。 - 参照物为元素本身的位置。 NOTE:最常用的目的为改变元素层级和设置为绝对定位的参照物。 ![](../img/P/position-relative.png) ##### position:absolute 建立以包含块为基准的定位,其随即拥有偏移属性和 `z-index` 属性。 - 默认宽度为内容宽度 - 脱离文档流 - 参照物为第一个定位祖先或根元素(`<html>` 元素) ![](../img/P/position-absolute.png) ##### position:fixed - 默认宽度为内容宽度 - 脱离文档流 - 参照物为视窗 NOTE:宽高的100%的参照依然为视窗(例:网页遮罩效果) ![](../img/P/position-fixed.png) ##### top/right/bottom/left ![](../img/L/layout-position.png) 其用于设置元素边缘与参照物边缘的距离,且设置的值可为负值。在同时设置相对方向时,元素将被拉伸。 ##### z-index 其用于设置 Z 轴上得排序,默认值为 0 但可设置为负值。(如不做设置,则按照文档流的顺序排列。后面的元素将置于前面的元素之上) ![](../img/L/layouy-position-zindex.png) ###### z-index 栈 父类容器的 `z-index` 优于子类 `z-index` 如图 ![](../img/L/layout-position-zindex-stack.jpg) #### float CSS 中规定的定位机制,其可实现块级元素同行显示并存在于文档流之中。浮动仅仅影响文档流中下一个紧邻的元素。 ``` float: left | right | none | inherit ``` - 默认宽度为内容宽度 - 脱离文档流(会被父元素边界阻挡与`position`脱离文档流的方式不同) - 指的方向一直移动 ![](../img/F/float-right.png) **float 元素在同一文档流中**,当同时进行 `float` 时它们会按照文档流中的顺序排列。(当所有父元素中的所有元素脱离文档流之后,父元素将失去原有默认的内容高度) ![](../img/F/float-right-all.jpg) 注意:**float 元素是半脱离文档流的**,对元素是脱离文档流,但对于内容则是在文档流之中的(既元素重叠但内容不重叠)。 ![](../img/F/float-half-off.png) ##### clear ``` clear: both | left | right | none | inherit ``` - 应用于后续元素 - 应用于块级元素(block) **使用方法**: 优先级自上而下 1. clearfix 于父元素 1. 浮动后续空白元素 `.emptyDiv {clear: both}` 1. 为受到影响的元素设置 `width: 100% overflow: hidden` 也可 1. 块级元素可以使用 `<br>` 不建议使用,影响 HTML 结构 ``` /* clearfix */ .clearfix:after { content: "."; /* Older browser do not support empty content */ visibility: hidden; display: block; height: 0; clear: both; } .clearfix {zoom: 1;} /* 针对 IE 不支持 :after */ ``` #### flex ![](../img/F/flex-container-and-item.jpg) 弹性布局可用于多行自适应,多列自适应,间距自适应和任意对齐。 **创建 flex container** ``` display: flex /* 弹性容器内的均为弹性元素*/ ``` **flex item** 只有弹性容器在文档流中的子元素才属于弹性元素。 ``` <div style="display: flex;"> <div>Block Element</div> <!-- flex item: YES--> <span>Inline Element</span> <!-- flex item: YES--> <div style="position:absolute;">Absolute Block Element</div> <!-- flex item: YES--> </div> ``` ##### flex 方向 ###### flex-direction ``` <!-- 默认值为 row --> flex-direction: row | row-reverse | column | column-reverse ``` ![](../img/F/flex-direciton.png) ###### flex-wrap ``` <!-- 默认值为 nowrap --> flex-wrap: nowrap | wrap | wrap-reverse ``` ![](../img/F/flex-wrap.png) ###### flex-flow `flex-flow` 为 `flex-wrap` 与 `flex-direction` 的简写,建议使用此属性(避免同时使用两个属性来修改)。 ``` flex-flow: <'flex-direction'> || <'flex-wrap'> ``` ![](../img/F/flex-flow.png) ###### order `order` 的值为相对的(同被设置和未被设置的值相比较),当均为设置时默认值为 0 则按照文档流中的顺序排列。 ``` order: <integer> <!-- 默认为 0 --> ``` ![](../img/F/flex-order0.png) ![](../img/F/flex-order1.png) ##### flex 弹性 ###### flex-basis 其用于设置 `flex-item` 的初始宽高(并作为弹性的基础)。如果 `flex-direction` 是以 `row` 排列则设置**宽**,如以 `column` 排列则设置**高**。 ``` flex-basis: main-size | <width> ``` ###### flex-grow 伸展因子,其为弹性布局中最重要的元素之一,`flex-grow` 设置元素可用空余空间的比例。`flex-container` 先安装宽度(`flex-basis`)进行布局,如果有空余空间就按照 `flex-grow` 中的比例进行分配。 **Width/Height = flex-basis + flex-grow/sum(flow-grow) * remain** ``` flex-grow: <number> initial: 0 <!-- 默认值为 0 --> ``` ![](../img/F/flex-grow0.png) ![](../img/F/flex-grow1.png) ![](../img/F/flex-grow2.png) ###### flex-shrink 收缩因子,用于分配超出的负空间如何从可用空间中进行缩减。 ``` flex-shrink: <number> initial: 1 <!-- 默认值为 1 --> ``` **Width/Height = flex-basis + flow-shrink/sum(flow-shrink) * remain** remain 为负值,既超出的区域。 ![](../img/F/flex-shrink.png) ###### flex 其为 `flex-grow` `flex-shrink` `flex-basis` 的值缩写。 ``` flex: <'flex-grow'> || <'flex-shrink'> || <'flex-basis'> initial: 0 1 main-size ``` ##### flex 对齐 ###### justify-content 其用于设置主轴(main-axis)上的对其方式。弹性元素根据主轴(横向和纵向均可)定位所以不可使用 `left` 与 `right` 因为位置为相对的。(行为相似的属性有 `text-align`) ``` justify-content: flex-start | flex-end | center | space-between | space-around <!-- 默认值为 flex-start --> ``` ![](../img/F/flex-justify-content.png) ###### align-items 其用于设置副轴(cross-axis)上的对其方式。(行为相似的属性有 `vertical-align`) ``` align-items: flex-start | flex-end | center | baseline | stretch <!-- 默认值为 stretch --> ``` ![](../img/F/flex-align-items.png) ###### align-self 其用于设置单个 `flex-item` 在 cross-axis 方向上的对其方式。 ``` align-self: auto | flex-start | flex-end | center | baseline | stretch <!-- 默认值为 auto --> ``` ![](../img/F/flex-align-self.png) ###### align-content 其用于设置 cross-axis 方向上的对其方式。 ``` align-content:flex-start | flex-end | center | space-between | space-around | stretch <!-- 默认为 stretch --> ``` ![](../img/F/flex-align-content.png) ================================================ FILE: 03-FEND_Note-master/chapter1/04_07_transform.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [变形](#%E5%8F%98%E5%BD%A2) - [2D 变形](#2d-%E5%8F%98%E5%BD%A2) - [transform](#transform) - [rotate()](#rotate) - [transform-origin](#transform-origin) - [translate()](#translate) - [scale()](#scale) - [skew()](#skew) - [3D 变形](#3d-%E5%8F%98%E5%BD%A2) - [rotateY()](#rotatey) - [perspective](#perspective) - [perspective-origin](#perspective-origin) - [translate3d()](#translate3d) - [scale3d()](#scale3d) - [rotate3d()](#rotate3d) - [transform-style](#transform-style) - [backface-visibility](#backface-visibility) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 变形 #### 2D 变形 [2D 变形示例代码](../SampleCode/CSS/Transform2D.html) ##### transform `transform` 中可以写一个或多个方法。 ``` transform: none | <transform-function>+ transform: none <!-- 默认值为 none --> transform: <transform-function>+ transform: translate(50%) rotate(45deg); transform: rotate(45deg) transform(50%) <!-- 变形函数顺序普通结果不同,原因是坐标位置发生了改变 --> ``` ![](../img/T/transform-transform-function.png) ###### rotate() ``` rotate(<angle>) rotate(45deg); <!-- 右边旋转,顺时针 --> rotate(-60deg); <!-- 左边旋转,逆时针 --> ``` ![](../img/T/transform-rotate.png) ###### transform-origin 其用于设置原点的位置(默认位置为元素中心)第一值为 X 方向,第二值为 Y 方向, 第三值为 Z 方向。(当值空出未写的情况下默认为 50%) ``` transform-origin: [ <percentage> | <length> | left | center | right | top | bottom] | [ [ <percentage> | <length> | left | center | right ] && [ <percentage> | <length> | top | center | bottom ] ] <length>? <!-- 默认值为 50% 50% --> transform-origin: 50% 50%; transform-origin: 0 0; transform-origin: right 50px 20px; transform-origin: top right 20px; ``` ![](../img/T/transform-origin.png) ###### translate() 移动方法,参数分别代表 X 与 Y 轴的移动(偏移值均可为负值)。 ``` translate(<translation-value>[, <translation-value>]?) <!-- 也可单独设置 X 与 Y 轴的偏移 --> translationX(<translation-value>) translationY(<translation-value>) transform: translate(50px); transform: translate(50px, 20%); <!-- Y 轴偏移为偏移对象的高度,X 轴为宽度 --> transform: translate(-50px); transform: translate(20%); ``` ![](../img/T/transform-traslate.png) ###### scale() 缩放方法,参数分别代表 X 与 Y 轴的缩放(缩放值均可为小数)。当第二值忽略时,默认设置为等同第一值。 ``` scale(<number> [, <number>]?) scaleX(<number>) scaleY(<number>) <!-- 整体放大 1.2 倍 --> transform: scale(1.2); <!-- 高度拉伸 --> transform: scale(1, 1.2); <!-- 宽度拉伸 --> transform: scaleX(1.2); <!-- 高度拉伸 --> transform: scaleY(1.2); ``` ![](../img/T/transform-scale.png) ###### skew() 其为倾斜的方法。第一值为 Y 轴往 X 方向倾斜(逆时针),第二值为 X 轴往 Y 方向倾斜(顺时针)。(倾斜值可为负值) ``` skew(<angle>[, <angle>]?) skewX(<angle>) skewY(<angle>) transform: skew(30deg); transform: skew(30deg, 30deg); transform: skewX(30deg); transform: skewY(30deg); ``` ![](../img/T/transform-skew.png) #### 3D 变形 [3D 变形示例代码](../SampleCode/CSS/Transform3D.html) ##### rotateY() 3D 空间旋转。 ``` transform: rotateY(<angle>) ``` ##### perspective ![](../img/P/perspective-3d.PNG) 其用于设置图片 Y 轴旋转后的透视效果。`<length>` 可以理解为人眼与元素之间的距离,越紧则效果越明显。 ``` perspective: none | <length> perspective: none; perspective: 2000px; perspective: 500px; ``` ![](../img/T/transform-perspective.png) ##### perspective-origin 其为设定透视的角度(透视位置均可设定为负值)。 ``` perspective-origin: [ <percentage> | <length> | left | center | right | top | bottom] | [ [ <percentage> | <length> | left | center | right ] && [ <percentage> | <length> | top | center | bottom ] ] perspective-origin: 50% 50% <!-- 默认值为 50% 50% 正中间的位置进行透视--> perspective-origin: left bottom; perspective-origin: 50% -800px; perspective-origin: right; ``` ![](../img/T/transform-perspective-origin.png) ##### translate3d() ``` translate3d(<translate-value>, <translate-value>, <length>) translateX(<translate-value>) translateY(<translate-value>) translateZ(<length>) transform: translate3d(10px, 20%, 50px); <!-- %的参照物为自身元素 --> transform: translateX(10px); transform: translateY(20%); transform: translateZ(-100px); ``` ![](../img/T/transform-translate3d.png) ##### scale3d() ``` scale3d(<number>, <number>, <number>) scaleX(<number>) scaleY(<number>) scaleZ(<number>) transform: scale3d(1.2, 1.2, 1); transform: scale3d(1, 1.2, 1); transform: scale3d(1.2, 1, 1); transform: scaleZ(5); <!-- Z 轴的缩放扩大并不影响盒子大小 --> ``` ![](../img/T/transform-scale3d.png) ##### rotate3d() 取 X Y Z 三轴上的一点并于坐标原点连线,以连线为轴进行旋转(逆时针)。 ``` rotate3d(<number>, <number>, <number>, <angle>) rotateX(<angle>) rotateY(<angle>) rotateZ(<angle>) transform: rotate3d(1, 0, 0, 45deg); <!-- 上面等同于 X 轴旋转 --> transform: rotate3d(0, 1, 0, 45deg); <!-- 上面等同于 Y 轴旋转 --> transform: rotate3d(0, 0, 1, 45deg); <!-- 上面等同于 2D 旋转 --> transform: rotate3d(1, 1, 1, 45deg); ``` ![](../img/T/transform-rotate3d.png) ##### transform-style 其用于设置保留内部的 3D 空间,原因是一个元素进行`transform`之后内部默认为`flat`。 ``` transform-style: flat | perserve-3d <!-- 默认为 flat --> transform-style: flat; transform-style: preserve-3d; ``` ![](../img/T/transform-transform-style.png) ##### backface-visibility 其用于设置背面不可见。 ``` backface-visibility: visible | hidden backface-visibility: visible; backface-visibility: hidden; ``` ![](../img/T/transform-backface-visibility.png) ================================================ FILE: 03-FEND_Note-master/chapter1/04_08_animation.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [动画](#%E5%8A%A8%E7%94%BB) - [transition](#transition) - [transition-property](#transition-property) - [transition-duration](#transition-duration) - [transition-delay](#transition-delay) - [transition-timing-function](#transition-timing-function) - [animation](#animation) - [animation-name](#animation-name) - [animation-duration](#animation-duration) - [animation-timing-function](#animation-timing-function) - [animation-iteration-count](#animation-iteration-count) - [animation-direction](#animation-direction) - [animation-play-state](#animation-play-state) - [animation-delay](#animation-delay) - [animation-fill-mode](#animation-fill-mode) - [@keyframes](#@keyframes) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ### 动画 [动画示例代码](../SampleCode/CSS/Animation.html) #### transition [过度动画](../SampleCode/CSS/Transition.html) 其为众多 `<single-transition>` 的值缩写。(当两个时间同时出现是,第一个时间为动画长度,第二个时间为动画延时) ``` transition: <single-transition> [',' <single-transition>]* <single-transition> = [none | <single-transition-property>] || <time> || <single-transition-timing-function> || <time> transition: none; transition: left 2s ease 1s, color 2s; transition: 2s; ``` ##### transition-property ``` transition-property: none | <single-traisition-property> [ ',' <single-transition-property>]* <single-transition-property> = all | <IDENT> transition-property: none; <!-- 默认值为 none --> transition-property: all; transition-property: left; transition-property: left, color; ``` ##### transition-duration ``` transition-duration: <time>[, <time>]* transition-duration: 0s; transition-duration: 1s; transition-duration: 1s, 2s, 3s; ``` ##### transition-delay ``` transition-delay: <time>[,<time>]* transition-delay: 0s; transition-delay: 1s; transition-delay: 1s, 2s, 3s; ``` ##### transition-timing-function ``` transition-timing-function: <single-transition-timing-function>[',' <single-transition-timing-function>]* <!-- 默认函数为 ease --> <single-transition-timing-function> = ease | linear | ease-in | ease-out | ease-in-out | cubic-bezier(<number>,<number>,<number>,<number>) | step-start | step-end | steps(<integer>)[, [start | end]]?) <!-- 对于 cubic-bezier 的曲线,前两个值为 P1 的坐标,后两值为 P2 的坐标 --> transition-timing-function: ease; transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); transition-timing-function: linear; transition-timing-function: cubic-bezier(0, 0, 1, 1); ``` ![](../img/T/transition-timing-function.png) #### animation ``` animation: <single-animation> [',' <single-animation>]* <single-animation> = <single-animation-name> || <time> || <single-animation-timing-function> || <time> || <single-animation-iteration-count> || <single-animation-direction> || <single-animation-fill-mode> || single-animation-play-state> animation: none; animation: abc 2s ease 0s 1 normal none running; animation: abc 2s; animation: abc 1s 2s both, abcd 2s both; <!-- 调用多个动画 --> ``` 动画可自动运行,但`transition`需要触发。 ##### animation-name `animation-name` 的名字可自由定义。 ``` animation-name: <single-animation-name># <single-animation-name> = none | <IDENT> animation-name: none; animation-name: abc; animation-name: abc, abcd; ``` ##### animation-duration 与 `transition-duration` 属性值类似。 ``` animation-duration: <time>[, <time>]* animation-duration: 0s; animation-duration: 1s; animation-duration: 1s, 2s, 3s; ``` ##### animation-timing-function 其与之前的 `transition-timing-function` 完全一模一样。 ``` animation-timing-function: <timing-function># <single-timing-function> = <single-transition-timing-function> animation-timing-function: ease; animation-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1); animation-timing-function: linear; animation-timing-function: cubic-bezier(0, 0, 1, 1); animation-timing-function: ease, linear; ``` ##### animation-iteration-count 其用于动画执行的次数(其默认值为 1)。 ``` animation-iteration-count: <single-animation-iteration-count># <single-animation-iteration-count> = infinite | <number> animation-iteration-count: 1; animation-iteration-count: infinite; animation-iteration-count: 1, 2, infinite; ``` ##### animation-direction 其用于定义动画的运动方向。 ``` animation-direction:<single-animation-direction># <single-animation-direction> = normal | reverse | alternate | alternate-revers animation-direction: reverse <!-- 动画相反帧的播放 --> animation-direction: alternate <!-- 往返执行动画 --> animation-direction: alternate-revers <!-- 相反的往返动画 --> ``` ##### animation-play-state 其用于设定动画的播放状态。 ``` animation-play-state: <single-animation-play-state># <single-animation-play-state> = running | paused animation-play-state: running; animation-play-state: pasued; animation-play-state: running, paused; ``` ##### animation-delay 其用于设置动画的延时,同 `transition-delay` 值相同。 ``` animation-delay: <time>[, <time>]* anim animation-delay: 0s; animation-delay: 1s; animation-delay: 1s, 2s, 3s; ``` ##### animation-fill-mode 其用于设置动画开始时,是否保持第一帧的动画和动画在结束时时候保持最后的状态。 ``` animation-fill-mode: <single-animation-fill-mode>[',' <single-animation-fill-mode>]* <single-animation-fill-mode> = none | backwards | forwards | both animation-fill-mode: none; <!-- 不做设置 --> animation-fill-mode: backwards; <!-- 动画开始时出现在第一帧的状态 --> animation-fill-mode: forwards; <!-- 动画结束时保留动画结束时的状态 --> animation-fill-mode: both; <!-- 开始和结束时都应保留关键帧定义的状态(通常设定) --> animation-fill-mode: forwards, backwards; ``` ##### @keyframes 其用于定义关键帧。 ``` <!-- 写法一 --> @keyframes abc { from {opacity: 1; height: 100px;} to {opacity: 0.5; height: 200px;} } <!-- 写法二 --> @keyframes abcd { 0% {opacity: 1; height: 100px;} 100% {opacity: 0.5; height: 200px} } @keyframes flash { 0%, 50%, 100% {opacity: 1;} 25%, 75% {opacity: 0;} } <!-- 例子 --> animation: abc 0.5s both; animation: flash 0.5s both; animaiton: abc 0.5s both, flash 0.5s both; ``` ![](../img/A/animation-sample.gif) ================================================ FILE: 03-FEND_Note-master/chapter1/04_09_layout_demo.md ================================================ ### 常见布局样例 #### 自动居中一列布局 所需知识: - 标准文档流 - 块级元素 - margin 外边距属性 ```html <style type="text/css" media="screen"> article { width: 800px; margin: 0 auto; } </style> <body> <article> <h1>Title</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. A natus repellendus, modi culpa, dolor ducimus debitis, facere dolorum cum aspernatur, soluta molestias est illo vel iusto esse ex ullam amet!</p> </article> </body> ``` NOTE:设置 auto 会根据浏览器宽度自动设置外边距尺寸。在设置浮动或绝对定位则会使自动居中失效,因为其会脱离文档流。 `(浏览器宽度 - 外包含层的宽度)/ 2 = 外边距`。 #### 横向两列布局 此方法也同时可以实现横向多列布局(原理与两列布局相同)。 所需知识: - float 属性,使纵向排列的块级元素,横向排列 - margin 属性,设置列之间的间距 ```html <style type="text/css" media="screen"> .clearfix:after { content: "."; /* Older browser do not support empty content */ visibility: hidden; display: block; height: 0; clear: both; } .clearfix {zoom: 1;} /* 针对 IE 不支持 :after */ body { width: 930px; margin: 0 auto; /* 横向居中 */ } article { width: 800px; float: left; margin-right: 10px; } aside { width: 120px; float: right; } </style> <body class="clearfix"> <article> <h1>Title</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Explicabo, quam, fugit. Accusamus voluptates nesciunt in, autem ipsa assumenda a iusto, reiciendis earum repudiandae, nulla natus blanditiis, aliquam asperiores commodi qui.</p> </article> <aside> <h3>Aside Title</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Expedita, molestiae!</p> </aside> </body> ``` #### 绝对定位的横向两列布局 应用场合较少,常用与一列定宽,另一列自适应。 需要知识: - relative positon 父元素相对定位 - absolute 自适应宽度元素绝对定位 注意:固定宽度列的高度需大于自适应的列(原因是绝对定位会脱离文档流,不能撑开父元素)。 ```html <style type="text/css" media="screen"> body { position: relative; width: 100%; } article { position: absolute; top: 0; right: 0; width: 800px; } aside { position: absolute; top: 0; right:800px; left: 0; } </style> <body> <article> <h1>Title</h1> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error obcaecati sint minima totam fuga, tempora, id quia soluta officia iure eligendi sequi non dicta, doloribus accusamus odit fugiat quam quibusdam.</p> </article> <aside> <h3>Aside Title</h3> <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Atque, doloremque.</p> </aside> </body> ``` ================================================ FILE: 03-FEND_Note-master/chapter1/04_css_intro.md ================================================ <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [CSS](#css) - [简介](#%E7%AE%80%E4%BB%8B) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ## CSS ### 简介 CSS (Cascading Stylesheet) 它用于设置页面的表现。CSS3 并不是一个完整的独立版本而是将不同的功能拆分成独立模块(例如,选择器模块,盒模型模块),这有利于不同功能的及时更新与发布也利于浏览器厂商的实习。 ![](../img/C/css3-history.png) **CSS 引入方法** ```html // 外部样式表 <head> <link rel="stylesheet" type="text/css" href="style.css"> </head> // 内部样式表 <head> <style type="text/css"> p { margin: 10px; } </style> </head> // 内嵌样式(可在动态效果中同 JavaScript 一同使用) <p style="color: red">Sample Text</p> ``` ================================================ FILE: 03-FEND_Note-master/chapter2/00_intro.md ================================================ # JavaScript 程序设计 Javascript 程序设计以 ECMAScript 5.1 为标准,从基本语法到原理深入,理解和编写Javascript程序。核心内容有语言简介、调试器、类型系统、内置对象、基本语法、变量作用域、闭包、面向对象编程等。 ================================================ FILE: 03-FEND_Note-master/chapter2/01_javascript_intro.md ================================================ ## JavaScript 介绍 前端开发三要素,`HTML`(描述网页内容),`CSS`(描述样式),`JavaScript`(控制网页行为)。JavaScript 为解释型编程语(程序不需要编译,程序在运行时才翻译成机器语言,每执 行一次都要翻译一次),运行环境也很广泛(浏览器或操作系统中 NodeJS)。 JavaScript 又分为两部分,ECMAScript 和 DOM。其中 JavaScript 的基本的语言属性(例如,变量,作用域等)都在 ECMAScript 中有详细的定义好比汉子在字典中的定义。DOM 中则定义如何修改文档结构(HTML 或 XML 等)。 **JavaScript**的引入方法如下: ```javascript <!DOCTYPE html> <html> <head> <title>Demo Page ``` ![](../img/J/javascript-history.png) ![](../img/J/javascript-env.png) ================================================ FILE: 03-FEND_Note-master/chapter2/02_dev_tools.md ================================================ ## 调试器 调试工具都内置于主流浏览器中(Firefox 中需独立下载 Firebug)。更多关于 Google Chrome DevTools 的信息可以在[这里](https://developer.chrome.com/devtools)找到。 ================================================ FILE: 03-FEND_Note-master/chapter2/03_basic_syntax.md ================================================ ## 基本语法 ### 变量标示符 **变量**的命名 ```javascript var _name = null; var $name = null; var name0 = null; ``` ### 关键字与保留字 JavaScript 在语言定义中保留的字段,这些字段在语言使用中存在特殊意义或功能,在程序编写的过程中不可以当做变量或函数名称使用。无需记忆,报错修改即可。 关键字与保留字可以在 [Mozilla 开发者站点](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar)找到。 ### 字符敏感 字符串的大小写是有所区分的,不同字符指代不同的变量。 ### 严格模式 **增益** - 消除语法中不合理与不安全的问题,保证代码正常运行 - 提高编译效率,增加运行速度 **使用方法** ```javascript "use strict"; (function(){ console.log('>>> Hello, world!'); })() (function(){ "use strict"; console.log('>>> Hello, world!'); })() ``` 严格模式与标准模式的区别: - 严格模式下隐式声明或定义变量被静止 - 严格模式下对象重名的属性在严格模式下被静止 - 严格模式下 `arguments.callee()` 被禁用 - 严格模式下 `with()` 语句 - 更多限制 ### 注释 ```javascript /* 多行注释,不可嵌套 */ // 单行注释 ``` ================================================ FILE: 03-FEND_Note-master/chapter2/04_data_type.md ================================================ ## 类型系统 javascript 类型系统可以分为标准类型和对象类型,进一步标准类型又可以分为原始类型和引用类型,而对象类型又可以分为内置对象类型、普通对象类型、自定义对象类型。 ![](../img/J/javascript-variable-type.jpg) ### 标准类型 标准类型共包括了**6**个分别是: **原始类型(值类型)**: - Undefined `undefined` - Null `null` - Boolean `true` - String `'hello'` - Number `123` **引用类型(对象类型)**: - Object ```javascript var obj = {}; var bool = new Boolean(true); var str = new String("hello"); var num = new Number(1); var obj0 = new Object(); ``` 原始类型和引用类型的区别: 原始类型储存在栈(Stack)中储存变量的值,而引用类型在栈中保存的是所引用内容储存在堆(Heap)中的值。类似于指针的概念,引用类型并非储存变量真实数值而是地址,所以对已引用类型的复制其实只是复制了相同的地址而非实际的变量值。 **Undefined** 值:undefined 出现场景: - 以声明为赋值的变量 `var obj;` - 获取对象不存在的属性 `var obj = {x: 0}; obj.y;` - 无返回值函数的执行结果 `function f(){}; var obj = f();` - 函数参数没有传入 `function f(i){console.log(i)}; f();` - `void(expression)` **Null** 值:null 出现场景: - 获取不存在的对象 `document.getElementById('not-exist-element')` **Boolean** 值:true, false 出现场景: - 条件语句导致的系统执行的隐式类型转换 `if(隐式转换){}` - 字面量或变量定义 `var bool = true;` **String** 值:字符串 出现场景: - `var str = 'Hello, world!';` **Number** 值:整型直接量,八进制直接量(0-),十六进制直接量(0x-),浮点型直接量 出现场景: - `1026` - `3.14` - `1.2e5` - `0x10` **Object** 值:属性集合 出现场景: - `var obj = {name: 'Xinyang'};` ### 变量转换表 |Value|Boolean|Number|String| |-----|-------|------|------| |undefined|false|NaN|"undefined"| |null|false|0|"null"| |true|true|1|"true"| |false|false|0|"false"| |''|false|0|''| |'123'|true|123|'123'| |'1a'|true|NaN|'1a'| |0|false|0|"0"| |1|true|1|"1"| |Infinity|true|Infinity|"Infinity"| |NaN|false|NaN|'NaN'| |{}|true|NaN|"[object Object]"| ### 类型识别 - `typeof` - `Object.prototype.toString` - `constructor` - `instanceof` **typeof**: - 可以是标准类型(Null 除外) - 不可识别具体的对象类型(Function 除外) **Object.prototype.toString**: - 可是识别标准类型及内置对象类型(例如,Object, Date, Array) - 不能识别自定义对象类型 **constructor**: - 可以识别标准类型(Undefined/Null 除外) - 可识别内置对象类型 - 可识别自定义对象类型 ```javascript function getConstructiorName(obj) { return obj && obj.constructor && obj.constructor.toString().match(/function\s*([^(]*)/)[1]; } getConstructiorName([]) === "Array"; // true ``` **instanceof**: - 不可判别原始类型 - 可判别内置对象类型 - 可判别自定义对象类型 ================================================ FILE: 03-FEND_Note-master/chapter2/05_internal_object.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [内置对象](#%E5%86%85%E7%BD%AE%E5%AF%B9%E8%B1%A1) - [标准内置对象](#%E6%A0%87%E5%87%86%E5%86%85%E7%BD%AE%E5%AF%B9%E8%B1%A1) - [Object](#object) - [Object.create](#objectcreate) - [Object.prototype.toString](#objectprototypetostring) - [Object.prototype.hasOwnProperty](#objectprototypehasownproperty) - [Boolean](#boolean) - [String](#string) - [String.prototype.indexOf](#stringprototypeindexof) - [String.prototype.replace](#stringprototypereplace) - [String.prototype.split](#stringprototypesplit) - [Number](#number) - [Number.prototype.toFixed](#numberprototypetofixed) - [Array](#array) - [Array.prototype.splice](#arrayprototypesplice) - [Array.prototype.forEach](#arrayprototypeforeach) - [Function](#function) - [自定义对象构造器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AF%B9%E8%B1%A1%E6%9E%84%E9%80%A0%E5%99%A8) - [Function.prototype.apply](#functionprototypeapply) - [Function.prototype.bind](#functionprototypebind) - [子类构造器](#%E5%AD%90%E7%B1%BB%E6%9E%84%E9%80%A0%E5%99%A8) - [函数调用](#%E5%87%BD%E6%95%B0%E8%B0%83%E7%94%A8) - [函数参数](#%E5%87%BD%E6%95%B0%E5%8F%82%E6%95%B0) - [arguments](#arguments) - [值专递](#%E5%80%BC%E4%B8%93%E9%80%92) - [函数重载](#%E5%87%BD%E6%95%B0%E9%87%8D%E8%BD%BD) - [RegExp](#regexp) - [RegExp.prototype.test](#regexpprototypetest) - [Date](#date) - [标准内置对象](#%E6%A0%87%E5%87%86%E5%86%85%E7%BD%AE%E5%AF%B9%E8%B1%A1-1) - [Math](#math) - [Math.floor](#mathfloor) - [Math.random](#mathrandom) - [JSON](#json) - [JSON.stringify](#jsonstringify) - [JSON.parse](#jsonparse) - [全局对象](#%E5%85%A8%E5%B1%80%E5%AF%B9%E8%B1%A1) - [NaA](#naa) - [parseInt](#parseint) - [eval](#eval) - [encodedURIComponent](#encodeduricomponent) ## 内置对象 通常情况下只有对象才存在方法,但 JavaScript 不同它具有12种内置对象。内置对象又分为两类,普通对象(属性和方法)与构造器对象(可用于实例化普通对象,它还包含原型对象属性和方法,及实例对象属性和方法)。 **JavaScript 对象原型链的简要说明** ```javascript function Point(x, y) { this.x = x; this.y = y; } Point.prototype.move = function(x, y) { this.x += x; this.y += y; } var p = new Point(1, 1); p.move(2,2); ``` `__proto__` 称之为原型链,有如下特点: 1. `__proto__` 为对象内部的隐藏属性 1. `__proto__` 为实例化该对象的构造器的 `prototype` 对象的引用,因此可以直接方法 `prototype` 的所有属性和方法 1. 除了 `Object` 每个对象都有一个 `__proto__` 属性且逐级增长形成一个链,原型链顶端是一个 `Object` 对象。 1. 在调用属性或方法时,引擎会查找自身的属性如果没有则会继续沿着原型链逐级向上查找,直到找到该方法并调用。 1. `__proto__` 跟浏览器引擎实现相关,不同的引擎中名字和实现不尽相同(chrome、firefox中名称是 `__proto__` ,并且可以被访问到,IE中无法访问)。基于代码兼容性、可读性等方面的考虑,不建议开发者显式访问 `__proto__` 属性或通过 `__proto__`更改原型链上的属性和方法,可以通过更改构造器` prototype` 对象来更改对象的 `__proto__` 属性。 **构造器对象与普通对象的区别** ![](../img/O/object-with-constructor-and-regular-object.png) 1. 构造器对象原型链中的 `__proto__` 是一个 `Function.prototype` 对象的引用,因此可以调用 `Function.prototype`的属性及方法 1. 构造器对象本身有一个 `prototype` 属性,用该构造器实例化对象时该 `prototype` 会被实例对象的 `__proto__` 所引用 1. 构造器对象本身是一个 `function` 对象,因此也会有自身属性 ### 标准内置对象 **构造器对象** - Object - Boolean - String - Number - Function - Array - RegExp - Date - Error **其他对象** - Math - JSON - 全局对象 内置对象,其实也叫内置构造器,它们可以通过 `new` 的方式创建一个新的实例对象。内置对象所属的类型就叫内置对象类型。其声明方式如下: ```javascript var i = new String("str"); // String Object var h = new Number(1); // Number Object var g = new Boolean(true); // Boolean Object var j = new Object({name : "Tom"}); // Object Object var k = new Array([1, 2, 3, 4]); // Array Object var l = new Date(); // Date Object var m = new Error(); var n = new Function(); var o = new RegExp("\\d"); ``` 注意:虽然标准类型中有`Boolean` `String` `Number` `Object`,内置对象类型中也有`Boolean` `String` `Number` `Object`,但它们其实是通过不同的声明方式来进行区别的。标准类型通过直接赋值,而对象类型则是通过构造器实现初始化。 ### Object > 构造器的原型对象在对象实例化时将会被添加到实例对象的原型链当中。 > `__proto__` 为原型链属性,编码时不可被显像调用。但是实例化对象可以调用原型链上的方法。 用 String/Number 等构造器创建的对象原型链顶端对象始终是一个Object对象,因此这些对象可以调用Object的原型对象属性和方法。所以 String/Number 等构造器是 Object 的子类。 更多关于 Object 的内容可以在[这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)找到。 **构造器说明**: - Object 是属性和方法的集合 - String/Number/Boolean/Array/Date/Error 构造器均为 Object 的子类并集成 Object 原型对象的属性及方法。 **实例化方法** ``` var obj0 = new Object({name: 'X', age: 13}); // 常用方法 var obj1 = {name: 'Q', age: 14}; ``` **属性及方法** - prototype - create - keys - ... **原型对象属性及其方法 - constructor - toString - valueOf - hasOwnProperty - ... **实例对象属性及方法** 无 #### Object.create 功能:基于原型对象创造新对象 ```javascript // Object.create(prototype[, propertiesObject]) var prototype = {name: 'X', age: 13}; var obj = Object.create(proto); ``` #### Object.prototype.toString 功能:获取方法调用者的标准类型 ```javascript // objectInstance.toString() var obj = {}; obj.toString(); // Object ``` #### Object.prototype.hasOwnProperty 功能:判断一个属性是否是一个对象的自身属性 ```javascript // objectInstance.hasOwnProperty("propertyName") var obj = Object.create({a: 1, b: 2}); obj.c = 3; obj.hasOwnProperty('a'); // false obj.hasOwnProperty('c'); // true ``` ### Boolean **构造器说明**:值为 true 与 false **属性及方法** - prototype **原型对象属性及其方法 - constructor, toString, valueOf ### String **构造器说明**:单双引号内的字符串 **实例化方法** ```javascript 'Hello, world!' var str0 = 'Xinyang'; var str1 = new String('Xinyang'); ``` **属性及方法** - prototype - fromCharCode(转换 ASCII 代码为字符) **原型对象属性及其方法** - constructor - indexOf - replace - slice - split - charCodeAt - toLowerCase - ... #### String.prototype.indexOf 功能:获取子字符串在字符串中的索引 ```javascript // stringObject.indexOf(searchValue, fromIndex) var str = "I am X. From China!"; var index = str.indexOf('a'); // 2 str.indexOf('a', index + 1); // 16 str.indexOf('Stupid'); // -1 字符串不存在 ``` #### String.prototype.replace 功能:查找字符串替换成目标文字 ```javascript // stringObject.replace(regexp/substr, replacement) var str = "apple is bad"; str = str.replace('bad', 'awesome'); ``` #### String.prototype.split 功能:按分隔符将分隔符分成字符串数组 ```javascript // stringObject.split(separator, arrayLength) var str = '1 2 3 4'; str.split(' '); // ['1', '2', '3', '4']; str.split(' ', 3); // ['1', '2', '3']; str.split(/\d+/); // ["", " ", " ", " ", ""] ``` ### Number **构造器说明**:整型直接量,八进制直接量(0-),十六进制直接量(0x-),浮点型直接量 **实例化方法** ```javascript 10 1.2e5 var count = 0x10; var pi = new Number(3.1415); ``` **属性及方法** - prototype - MAX_VALUE - MIN_VALUE - NaN - NEGATIVE_INFINITY - POSITIVE_INFINITY **原型对象属性及其方法** - constructor - toFixed - toExponential - ... #### Number.prototype.toFixed 功能:四舍五入至指定小数位 ```javascript // numberObject.toFixed(num) var num0 = 3.14; num0.toFixed(1); // 3.1 var num1 = 3.35; num1.toFixed(1); // 3.4 ``` ### Array **构造器说明**:定义数组对象 **实例化方法** ```javascript var a0 = [1, 'abc', true, function(){}]; var a1 = new Array(); var a2 = new Array(1, 'abc', true); ``` **属性及方法** - prototype - isArray **原型对象属性及其方法** - constructor - splice - forEach - find - concat - pop - push - reverse - shift - slice - ... #### Array.prototype.splice 功能:从数组中删除或添加元素,返回被删除的元素列表(作用域原有数组) ```javascript // arrayObject.splice(start, deleteCount[, item1[, item2[, ...]]]) var arr = ['1', '2', 'a', 'b', '6']; var ret = arr.splice(2, 2, '3', '4', '5'); // ['a', 'b'] arr; // ['1', '2', '3', '4', 5', '6'] ``` #### Array.prototype.forEach 功能:遍历元素组并调用回调函数 ```javascript // arrayObject.forEach(callback[, thisArg]) // 回调函数 // function callback(value, index, arrayObject) {...} // value - 当前值 index - 当前索引 arrayObject - 数组本身 function logArray(value, index, arrayObject) { console.log(value); console.log(value === array[index]); } [2, 5, 6, 9].forEach(logArray); ``` ### Function **构造器说明**:定义函数或新增对象构造器 **实例化方法** ```javascript // 对象实例化 var f0 = new Function('i', 'j', 'return (i + j)'); // 函数关键字语句 function f1(i, j){return i + j;} // 函数表达式 var f3 = function(i, j){return i + j;}; ``` **属性及方法** - prototype **原型对象属性及其方法** - constructor - apply - call - bind **实例对象属性和方法** - length - prototype - arguments - caller #### 自定义对象构造器 下面的代码声明一个 Point 增加了一个move方法,最后创建了一个 Point 的实例对象。 ```javascript function Point(x, y) { this.x = x; this.y = y; } Point.prototype.move = function(x, y) { this.x += x; this.y += y; } var p = new Point(1, 2); ``` #### Function.prototype.apply 功能:通过参数指定调用者和函数参数并执行该函数 ```javascript // functionObj.apply(thisArg[, argsArray]) function Point(x, y) { this.x = x; this.y = y; } Point.prototype.move = function(x, y) { this.x += x; this.y += y; } var p = new Point(1, 1); var circle = {x: 1, y: 1, r: 1}; p.move.apply(circle, [2, 1]); // {x: 3, y: 2, r: 1} ``` #### Function.prototype.bind 功能:通过参数指定函数调用者和函数参数并返回该函数的引用 ```javascript // functionObj.bind(thisArg[, arg1[, arg2[, ...]]]) function Point(x, y) { this.x = x; this.y = y; } Point.prototype.move = function(x, y) { this.x += x; this.y += y; } var p = new Point(1, 1); var circle = {x: 1, y: 1, r: 1}; var circleMoveRef = p.move.bind(circle, 2, 1); setTimeout(circleMoveRef, 1000); // {x: 3, y: 2, r: 1} // 之间使用 circleMoveRef() 效果等同于 apply() circleMoveRef(); ``` #### 子类构造器 ```javascript function Circle(x, y, r) { Point.apply(this, [x, y]); this.radius = r; } Circle.prototype = Object.create(Point.prototype); Circle.prototype.constructor = Circle; Circle.prototype.area = function(){ return Math.PI * this.radius * this.radius; } var c = new Circle(1, 2, 3); c.move(2, 2); c.area(); ``` #### 函数调用 - `()` - `apply` - `call` #### 函数参数 - 形参个数不一定等于实参个数 - 值专递 - 通过参数类型检查实现函数重载 ##### arguments arguments 的常用属性 - length 实参个数 - 0...arguments.length-1 实参属性名称(key) - callee 函数本身 ```javascript function max(a, b) { if (max.length === arguments.length) { return a>b?a:b; } else { var _max = arguments[0]; for(var i = 0; i < arguments.length; i++) { if (_max < arguments[i]) { _max = arguments[i]; } } return _max; } } ``` ##### 值专递 函数参数的值专递是参数复制都是栈内存中的复制。 ![](../img/M/memory-management.jpg) ```javascript // 原始类型 function plusplus(num) { return num++; } var count = 0; var result = plusplus(count); // result = 1; count = 0; // 引用类型 function setName(obj) { obj.name = 'obama'; } var president = {name: 'bush'}; setName(president); // {name: 'obama'}; ``` ##### 函数重载 以 `Require.JS` 中的 `define()` 为例: ```javascript define(function(){ var add = function(x, y) { return x + y; }; return { add: add }; }) define(['lib'], function(){ var add = function(x, y) { return x + y; }; return { add: add }; }) define('math', ['lib'], function(){ var add = function(x, y) { return x + y; }; return { add: add }; }) // define 的实现代码 /** * The function that handles definitions of modules. Differs from * require() in that a string for the module should be the first argument, * and the function to execute after dependencies are loaded should * return a value to define the module corresponding to the first argument's * name. */ define = function (name, deps, callback) { var node, context; //Allow for anonymous modules if (typeof name !== 'string') { //Adjust args appropriately callback = deps; deps = name; name = null; } //This module may not have dependencies if (!isArray(deps)) { callback = deps; deps = null; } // 省略以下代码 // ... }; ``` ### RegExp **构造器说明**:用于定义正则表达式,一个 RegExp 对象包含一个正则表达式和关联的标志 **定义方法** - `/pattern/flags` - `new RegExp(pattern[, flags]);` **属性及方法** - prototype **原型对象属性及其方法** - constructor - test - exec - ... #### RegExp.prototype.test 功能:使用正则表达式对字符串进行测试,并返回测试结果 ```javascript // regexObj.text(str) var reg = /^abc/i; reg.test('Abc123'); // true reg.test('1Abc1234'); // false ``` ### Date **构造器说明**:用于定义日期对象 **定义方法** ```javascript var date0 = new Date(); var date1 = new Date(2014, 3, 1, 7, 1, 1, 100); ``` **属性及方法** - prototype - parse - now - ... **原型对象属性及其方法** - constructor - Date - getDate - getHours - setDate - setHours - ... ### 标准内置对象 #### Math **对象说明**:拥有属性和方法的单一对象主要用于数字计算 **对象属性**: - E - PI - SQRT2 - ... **对象方法**: - floor - random - abs - max - cos - ceil ##### Math.floor 功能:向下取整 ```javascript // Math.floor(num) Math.floor(0.97); // 0 Math.floor(5.1); // 5 Math.floor(-5.1); //6 ``` 相似方法:`ceil`,`round` ##### Math.random 功能:返回 0~1 之间的浮点数 ```javascript // Math.random() Math.random(); // 0.14523562323461 ``` #### JSON **对象说明**:用于存储和交换文本信息 **对象方法**: - parse - stringify ##### JSON.stringify 功能:将 JSON 对象转换为字符转 ```javascript // JSON.stringify(value[, replacer[, space]]) var json = {'name': 'X'}; JSON.stringify(json); // "{"name":"X"}" ``` ##### JSON.parse 功能:将 JSON 字符转转换为对象 ``` // JSON.parse(text[, reviver]) var jsonStr = '{"name":"X"}'; JSON.parse(jsonStr); // {name: 'X'} ``` #### 全局对象 全局对象定义了一系列的属性和方法在编程过程中可以被之间调用。 属性:NaN,Infinity,undefined 方法: - parseInt - parseFloat - isNaN - isFinite - eval 处理 URI 方法: - encodedURIComponent - decodeURIComponent - encodedURI - decodeURI 构造器属性: - Boolean - String - Number - Object - Function - Array - Date - Error - ... 对象属性: - Math - JSON ##### NaA 非数字值:表示错误或无意义的运算结果,NaN 参与运算仍会返回 NaA,且 NaN 不等于任何值,包括它本身。可以使用 `isNaN()` 判断运算结果的类型是否为 NaN。 ```javascript isNaN(NaN); // true isNaN(4 - '2a'); // true; ``` ##### parseInt 功能:转换字符串成数字 ```javascript // parseInt(string[, radix]) // radix - 为进制数 parseInt('010'); // 10 parseInt('010', 8) // 8 parseInt('010', 16) // 16 parseInt('0x1f'); // 31 parseInt('0x1f', 16); // 31 parseInt('1f'); // 1 parseInt('1f', 16); // 31 ``` ##### eval 功能:计算字符串并执行其中的 JavaScript 代码(会带来安全性和代码逻辑问题,通常不建议使用) ```javascript // eval(string) var res = '{"error": "0", "msg": "OK"}; var obj; if (!JSON) { obj = eval('(' + res + ')'); } else { obj = JSON.parse(res); } ``` ##### encodedURIComponent 功能:将 URI 参数中的特殊字符,中文等作为 URI 的一部分进行编码 ```javascript var uri = "http://w3schools.com/my test.asp?name=ståle&car=saab"; var res = encodeURIComponent(uri); // 结果 // http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab ``` ================================================ FILE: 03-FEND_Note-master/chapter2/06_scope.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [变量作用域](#%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F) - [作用域介绍](#%E4%BD%9C%E7%94%A8%E5%9F%9F%E4%BB%8B%E7%BB%8D) - [静态作用域](#%E9%9D%99%E6%80%81%E4%BD%9C%E7%94%A8%E5%9F%9F) - [动态作用域](#%E5%8A%A8%E6%80%81%E4%BD%9C%E7%94%A8%E5%9F%9F) - [JavaScript 变量作用域](#javascript-%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F) - [词法环境](#%E8%AF%8D%E6%B3%95%E7%8E%AF%E5%A2%83) - [组成](#%E7%BB%84%E6%88%90) - [创建](#%E5%88%9B%E5%BB%BA) - [结构](#%E7%BB%93%E6%9E%84) - [关于词法环境的问题](#%E5%85%B3%E4%BA%8E%E8%AF%8D%E6%B3%95%E7%8E%AF%E5%A2%83%E7%9A%84%E9%97%AE%E9%A2%98) - [with 语句](#with-%E8%AF%AD%E5%8F%A5) - [try-catch 句法](#try-catch-%E5%8F%A5%E6%B3%95) - [带名称的函数表达式](#%E5%B8%A6%E5%90%8D%E7%A7%B0%E7%9A%84%E5%87%BD%E6%95%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F) ## 变量作用域 变量的作用域值的是变量的生命周期和作用范围(全局与局部作用域的区别)。 ### 作用域介绍 #### 静态作用域 静态作用域有称为词法作用域,即指其在编译的阶段就可以决定变量的引用。**静态作用域**只更变量定义的位置有关与代码执行的顺序无关。 ```javascript var x = 0; function foo() { alert(x); } function bar() { var x = 20; foo(); } foo(); ``` ![](../img/S/scope-lexical-scope.png) #### 动态作用域 动态作用域的变量引用只可在程序运行时刻决定(其通常通过动态栈来进行管理)。 ```javascript var x = 0; function foo() { alert(x); } function bar() { var x = 20; foo(); } foo(); ``` ![](../img/S/scope-dynamic-scope.gif) ### JavaScript 变量作用域 JavaScript (1)使用静态作用域,(2)其没有块级作用域(只有函数作用域,就是只有 function 用于可以定义作用域),(3)在 ES5 之作使用词法环境来管理作用域。 #### 词法环境 ##### 组成 词法环境用来描述静态作用域的数据结构。它由`环节记录`和`外部词法环境的引用`组成。 - 环境记录(record)(指形参,变量,函数等) - 外部词法环境的引用(outer) ##### 创建 在一段代码执行之前,先初始化词法环境。会被初始化的有: - 形参 - 函数定义(创建函数对象,会保存当前作用域。见下图) - 变量定义(所有初始化值均为 `undefined`) ![](../img/S/scope-function-init.png) ##### 结构 ```javascript var x = 10; function foo(y) { var z = 30; function bar(q) { return x + y + z + q; } return bar; } var bar = foo(20); bar(40); ``` **全局词法作用域(初始化状态)** ![](../img/S/scope-global-init.png) **函数词法作用域** ![](../img/S/scope-structure.jpg) #### 关于词法环境的问题 **命名冲突** 形参,函数定义,变量名称命名冲突。其中的优先级的排序如下: ``` 函数定义 > 形参 > 变量 ``` **`arguments` 的使用** 为函数中定义好的变量。 **函数表达式与函数定义的区别** - 函数表达式是在执行时才创建函数对象。 - 函数定义为在代码执行之前就进行创建的。 #### with 语句 `with` 会创造一个临时作用域。 ```javascript var foo = 'abc'; with({ foo: 'bar'; }) { function f() { alert(foo); }; (function() { alert(foo); })(); f(); } ``` #### try-catch 句法 ``` try { var e = 10; throw new Error(); } catch (e) { function f() { alert(e); } (function() { alert(e); })(); f(); } ``` #### 带名称的函数表达式 当一个函数表达式有了名称之后,JavaScript 会创建一个新的词法环境。并在这个词法环境中用有一个属性 A 指向这个函数,同时这个属性 A 指向的函数是不可被修改的。 **下面例子为不常规的写法** ``` (function A(){ A = 1; alert(A); })(); ``` ![](../img/S/scope-function-with-name.png) ================================================ FILE: 03-FEND_Note-master/chapter2/07_statement_and_operator.md ================================================ ## 表达式与运算符 ### 表达式 表达式为 JavaScript 的短语可执行并生成值。 ```javascript 1.7 // 字面量 "1.7" var a = 1; var b = '2'; var c = (1.7 + a) * '3' - b ``` ### 运算符 - 算数运算符 (`+` `-` `*` `/` `%`) - 关系运算符 (`>` `<` `==` `!=` `>=` `<=` `===` `!==`) - 逻辑运算符 (`!` `&&` `||`) - 位运算符 (`&` `|` `^` `~` `<<` `>>`) - 负值运算符 (`=`) - 条件运算符 (`?:`) - 逗号运算符 (`,`) - 对象运算符 (`new` `delete` `.` `[]` `instanceof`) #### === 全等符号 全等运算符用于盘对左右两边的对象或值是否类型相同且值相等。 **伪代码拆解** ```javascript function totalEqual(a, b) { if (a 和 b 类型相同) { if (a 和 b 是引用类型) { if (a 和 b 是同一引用) return true; else return false; } else { // 值类型 if (a 和 b 值相等) return true; else return false; } } else { return false; } } ``` **例子** ```javascript var a = "123"; var b = "123"; var c = "4"; var aObj = new String("123"); var bObj = new String("123"); var cObj = aObj; a === aObj // false aObj === bObj // false aObj === cObj // true a === b // true a === c // false ``` #### == `==` 用于判断操作符两边的对象或值是否相等。 **伪代码拆解** ```javascript function equal(a, b) { if (a 和 b 类型相同) { return a === b; } else { // 类型不同 return Number(a) === Number(b); // 优先转换数值类型 } } ``` **例子** ```javascript "99" == 99; // true new String("99") == 99; // true true == 1; // true false == 0; // true '\n\n\n' == // true ``` ##### 例外规则 - `null == undefined` 结果为真 `true` - 在有 `null`/`undefined` 参与的 `==` 运算是不进行隐式转换。 ```javascript 0 == null; // false null == false; // false "undefined" == undefined; // false ``` #### ! 取反 `!x` 用于表达 x 表达式的运行结果转换成布尔值(Boolean)之后取反的结果。`!!x` 则表示取 x 表达式的运行结果的布尔值。 ```javascript var obj = {}; var a = !obj // false; var a = !!obj // true; ``` #### && 逻辑与 `x && y` 如果 x 表达式的运行交过转换成 Boolean 值为 false 则不运行表达式 y 而直接返回 x 表达式的运行结果。**相反**,如果 x 表达式的运行交过转换成 Boolean 值为 true 则运行表达式 y 并返回 y 表达式的运行结果。 **伪代码拆解** ```javascript var ret = null; if (!!(ret = x)) { return y; } else { return ret; } ``` **例子** ```javascript var a = 0 && (function(){return 1 + 1;})(); // 0 var b = 1 && (function(){return 1 + 1;})(); // 2 ``` #### || 逻辑或 `x || y` 如果 x 表达式的运行结果转换为 Boolean 值为 true,则不运行 表达式 y 而直接返回表达式 x 的运算结果。(与 `&&` 方式相反) **伪代码拆解** ```javascript var ret = null; if (!!(ret = x)) { return ret; } else { return y; } ``` **例子** ```javascript var a = 0 || (function(){return 1 + 1;})(); // 2 var b = 1 || (function(){return 1 + 1;})(); // 1 ``` ### 元算符优先级(Operator Precedence) - `+` `-` `*` `/` 高于 `&&` - `*` `/` 高于 `+` `-` - `&&` 高于 `?:` - `()` 内优先级高于之外 NOTE:和数学上的算术优先级类似,同级从**左到右**计算。如有疑问加上 `()` 既可解决优先级问题。
Precedence Operator type Associativity Individual operators
19 Grouping n/a ( … )
18 Member Access left-to-right … . …
Computed Member Access left-to-right … [ … ]
new (with argument list) n/a new … ( … )
17 Function Call left-to-right … ( … )
new (without argument list) right-to-left new …
16 Postfix Increment n/a … ++
Postfix Decrement n/a … --
15 Logical NOT right-to-left ! …
Bitwise NOT right-to-left ~ …
Unary Plus right-to-left + …
Unary Negation right-to-left - …
Prefix Increment right-to-left ++ …
Prefix Decrement right-to-left -- …
typeof right-to-left typeof …
void right-to-left void …
delete right-to-left delete …
14 Multiplication left-to-right … * …
Division left-to-right … / …
Remainder left-to-right … % …
13 Addition left-to-right … + …
Subtraction left-to-right … - …
12 Bitwise Left Shift left-to-right … << …
Bitwise Right Shift left-to-right … >> …
Bitwise Unsigned Right Shift left-to-right … >>> …
11 Less Than left-to-right … < …
Less Than Or Equal left-to-right … <= …
Greater Than left-to-right … > …
Greater Than Or Equal left-to-right … >= …
in left-to-right … in …
instanceof left-to-right … instanceof …
10 Equality left-to-right … == …
Inequality left-to-right … != …
Strict Equality left-to-right … === …
Strict Inequality left-to-right … !== …
9 Bitwise AND left-to-right … & …
8 Bitwise XOR left-to-right … ^ …
7 Bitwise OR left-to-right … | …
6 Logical AND left-to-right … && …
5 Logical OR left-to-right … || …
4 Conditional right-to-left … ? … : …
3 Assignment right-to-left … = …
… += …
… -= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
2 yield right-to-left yield …
1 Spread n/a ... …
0 Comma / Sequence left-to-right … , …
================================================ FILE: 03-FEND_Note-master/chapter2/08_statement.md ================================================ ## 语句 ### 条件控制语句 其中expression可以使用整型,字符串,甚至表达式 ```javascript if (expression0) {statement0} else if (expression1) {statement1} else {statement2} // JavaScript 中的 case 可以使用整型,字符串,甚至表达式 switch(persion.type) { case "teacher": statement1 break; case "student": statement2 break; default: statement3 break; } ``` ### 循环控制语句 ```javascript while(expression) {statement} // 至少执行一次 do {statement} while(expression); for (initialise; test_expresiion; increment) {statement} // 跳过下面代码并进入下一轮循环 continue; // 退出当前循环 break; ``` #### for-in 用于遍历对象的**全部**属性。 ```javascript function Car(id, type, color) { this.type = type; this.color = color; this.id = id; } var benz = new Car('benz', 'black', 'red'); Car.prototype.start = function(){ console.log(this.type + ' start'); } for (var key in benz) { console.log(key + ':' benz[key]); } // 输出结果 type:black color:red id:benz start:function (){ console.log(this.type + ' start'); } // ----------- // 如不需原型对象上的属性可以使用 hasOwnProperty for (var key in benz) { if (benz.hasOwnProperty(key)) { console.log(key + ':' benz[key]); } } // 输出结果 type:black color:red id:benz ``` 如不需原型对象上的属性可以使用 hasOwnProperty ```javascript for (var key in benz) { if (benz.hasOwnProperty(key)) { console.log(key + ':' benz[key]); } } /* 输出结果 type:black color:red id:benz */ ``` ### 异常处理语句 ```javascript try{ // statements throw new Error(); catch(e){ // statements } finally{ // statements } ``` ### with 语句 `with` 语句是 JavaScript 中特有的语句形式,它主要有两个作用: 其一,其用于缩短特定情况下必须书写的代码量。它可以暂时改变变量的作用域。 ```javascript // 使用 with 之前 (function(){ var x = Math.cos(3 * Math.PI) + Math.sin(Math.LN10); var y = Math.tan(14 * Math.E); })(); // 使用 with (function(){ with(Math) { var x = cos(3 * PI) + sin(LN10); var y = tan(14 * E); } })(); ``` ![](../img/W/with-scope.png) 其二,改变变量的作用域,将`with`语句中的对象添加至作用域链的头部。 ```javascript frame[1].document.forms[0].name.value = ""; frame[1].document.forms[0].address.value = ""; frame[1].document.forms[0].email.value = ""; with(frame[1].document.[0]) { name.value = ""; address.value = "" email.value = ""; } ``` **缺点**就是导致 JavaScript 语句的可执行性下降,所以通常情况下因尽可能的避免使用。 ================================================ FILE: 03-FEND_Note-master/chapter2/09_closure.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [闭包](#%E9%97%AD%E5%8C%85) - [闭包的应用](#%E9%97%AD%E5%8C%85%E7%9A%84%E5%BA%94%E7%94%A8) - [保存变量现场](#%E4%BF%9D%E5%AD%98%E5%8F%98%E9%87%8F%E7%8E%B0%E5%9C%BA) - [封装](#%E5%B0%81%E8%A3%85) ## 闭包 - 闭包有函数和与其相关的引用环境的组合而成 - 闭包允许函数访问其引用环境中的变量(又称自由变量) - 广义上来说,所有 JavaScript 的函数都可以成为闭包,因为 JavaScript 函数在创建时保存了当前的词法环境。 ``` function add() { var i = 0; return function() { alert(i++); } } var f = add(); f(); f(); ``` ### 闭包的应用 #### 保存变量现场 ```javascript // 错误方法 var addHandlers = function(nodes) { for (var i = 0, len = nodes.length; i < len; i++) { nodes[i].onclick = function(){ alert(i); } } } // 正确方法 var addHandlers = function(nodes) { var helper = function(i) { return function() { alert(i); } } var (var i = 0, len = nodes.length; i < len; i++) { nodes[i].onclick = helper(i); } } ``` #### 封装 ```javascript // 将 observerList 封装在 observer 中 var observer = (function(){ var observerList = []; return { add: function(obj) { observerList.push(obj); }, empty: function() { observerList = []; }, getCount: function() { return observerList.length; }, get: function() { return observerList; } }; })(); ``` ================================================ FILE: 03-FEND_Note-master/chapter2/10_object.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [面向对象](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) - [程序设计方法](#%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E6%96%B9%E6%B3%95) - [面向过程](#%E9%9D%A2%E5%90%91%E8%BF%87%E7%A8%8B) - [面向对象](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1-1) - [概念](#%E6%A6%82%E5%BF%B5) - [基本特点](#%E5%9F%BA%E6%9C%AC%E7%89%B9%E7%82%B9) - [JavaScript 面向对象](#javascript-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1) - [constructor](#constructor) - [自定义构造器](#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%84%E9%80%A0%E5%99%A8) - [创建构造器的方法(3 种)](#%E5%88%9B%E5%BB%BA%E6%9E%84%E9%80%A0%E5%99%A8%E7%9A%84%E6%96%B9%E6%B3%95%EF%BC%883-%E7%A7%8D%EF%BC%89) - [this](#this) - [全局环境中](#%E5%85%A8%E5%B1%80%E7%8E%AF%E5%A2%83%E4%B8%AD) - [构造器中](#%E6%9E%84%E9%80%A0%E5%99%A8%E4%B8%AD) - [函数中](#%E5%87%BD%E6%95%B0%E4%B8%AD) - [this 实例](#this-%E5%AE%9E%E4%BE%8B) - [原型继承](#%E5%8E%9F%E5%9E%8B%E7%BB%A7%E6%89%BF) - [原型链](#%E5%8E%9F%E5%9E%8B%E9%93%BE) - [属性查找](#%E5%B1%9E%E6%80%A7%E6%9F%A5%E6%89%BE) - [属性修改](#%E5%B1%9E%E6%80%A7%E4%BF%AE%E6%94%B9) - [属性删除](#%E5%B1%9E%E6%80%A7%E5%88%A0%E9%99%A4) - [Object.create(proto[, propertiesObject])](#objectcreateproto-propertiesobject) - [面向对象的应用](#%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%9A%84%E5%BA%94%E7%94%A8) - [全局变量](#%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F) - [封装](#%E5%B0%81%E8%A3%85) - [继承](#%E7%BB%A7%E6%89%BF) - [原型继承](#%E5%8E%9F%E5%9E%8B%E7%BB%A7%E6%89%BF-1) - [类继承](#%E7%B1%BB%E7%BB%A7%E6%89%BF) ## 面向对象 ### 程序设计方法 程序设计描述系统如何通过程序来实现的过程,其为一种设计方法与语言实现无关。常见的设计方法有面向流程与面向对象。 #### 面向过程 以程序的过程为中心,采用自定而下逐步细化的方法来实现。常见的面向过程语言有 C、Fortran、Pascall。 ![](../img/P/procedure_programming.png) #### 面向对象 将对象作为程序的基本单元,将程序分解为数据和操作的集合。常见的面向过程语言有 smalltalk(也是 Objective-C 的父亲)、Java、C++。 ![](../img/O/object-oriented-programming.png) ##### 概念 - 类(Class)、对象(Object) - 属性(Property)、方法(Method) ##### 基本特点 - 继承(Inheritance) - 封装(Encapsulation) - 多态(Polymorphism) ## JavaScript 面向对象 ### constructor 对象的构造器,也可称之为构造类型。 ```javascript // 使用 new 关键字创建 var o = new Object(); var a = new Array(); var d = new Date(); | | object constructor // 使用直接量创建 var o = {name: 'Xinyang'}; var a = [1, 2, 3]; ``` #### 自定义构造器 ```javascript // constructor function Person(name, age, birthdate) { this.name = name; this.age = age; this.birthdate = birthdate; this.changeName = function(newAge) { this.age = newAge; } } // 创建对象 var X = new Person('Stupid', 13, new Date(2015, 01, 01)); var Q = new Person('Q', 12, new Date(2015, 01, 01)); X.changeName('X'); ``` ##### 创建构造器的方法(3 种) - `function ClassName() {...}` - `var Class = function() {...}` - `var Class = new Function()` NOTE: 并不是所有函数都可以被当成构造器,例如 `var o = new Math.min()`。通常自定义的函数均可当做构造器来使用。内置对象的构造器也可被当做构造器。 NOTE+:如果构造器有返还值并为对象类型,则对象将被直接返回。 ```javascript function Person(name, age, birthdate) { this.name = name; this.age = age; this.birthdate = birthdate; this.changeName = function(newAge) { this.age = newAge; } // !!! 注意这里 return {}; } var X = new Person('X', 13, new Date()); console.log(X.name); // undefined; ``` ### this `this` 在不同环境中指代的对象不同(`this` 指代的值可在函数运行过程中发生改变)。 |出现场景|所指代值| |--------|--------| |全局环境|全局对象(window 在浏览器环境中时)| |constructor|创建的新实例对象| |函数调用|函数的调用者| |`new Function()`|全局对象| |`eval()`|调用上下文中的 `this`| #### 全局环境中 全局环境中 `this` 指代全局对象,既 `window` 在浏览器环境中。 ```javascript // 以下的所有 this 均指代全局对象 var a = 10; alert(this.a); this.b = 20; alert(b); c = 30; alert(this.c); ``` #### 构造器中 构造器中的 `this` 指代的是即将被创建出的对象。 ```javascript // constructor function Person(name, age, birthdate) { // 下面的指代即将被创建的对象 this.name = name; this.age = age; this.birthdate = birthdate; this.changeName = function(newAge) { this.age = newAge; } } // 创建对象 var X = new Person('Stupid', 13, new Date(2015, 01, 01)); var Q = new Person('Q', 12, new Date(2015, 01, 01)); X.changeName('X'); ``` #### 函数中 函数中的 `this` 指代函数的调用者。 ```javascript // constructor function Person(name, age, birthdate) { // 下面的指代即将被创建的对象 this.name = name; this.age = age; this.birthdate = birthdate; this.changeName = function(newAge) { this.age = newAge; } this.gretting = function() { // !!! 下面这个 this 指代调用它的对象,既上面的 // 上面的 gretting 左边的 this,既为即将被创建的对象 console.log('Hi, I am ' + this.name) } } // 创建对象 var X = new Person('Stupid', 13, new Date(2015, 01, 01)); X.changeName('X'); X.gretting(); ``` NOTE: `new Function('console.log(this)')` 中的 `this` 均指代全局对象。`eval('console.log(this)` 则为调用上下文指代的 `this`。 ### this 实例 下面的例子使用 `apply` 与 `call`。通过这两个方法来将一个对象中 `this` 指代的目标进行改变。 ```javascript function Point(x, y) { this.x = x; this.y = y; this.move = function(x, y) { this.x += x; this.y += y; } } var point = new Point(0, 0); point.move(1, 1); var circle = {x: 0, y: 1, r: 1}; // 改变 point 中 move 方法 this 指代的对象至 circle point.move.apply(circle, [1, 1]); // 同样可以用类似的 call 方法,区别为参数需依次传入 point.move.call(circle, 1, 1); ``` ![](../img/T/this-sample.jpg) ### 原型继承 使用原型(prototype)可以解决重复定义实例对象拥有的完全一致的属性或方法(既共享原型中的属性或方法)。 ```javascript function Boss() { this.age = 0; this.birthdate = null; this.name = ''; this.tasks = []; this.title = 'Boss'; this.gretting = function() {console.log('I am a Boss!');}; } var X = new Boss(); var Q = new Boss(); // X 与 Q 中具有完全一致(不必唯一的属性或方法) // 并耗用内存的共享部分 // this.title 与 this.gretting ``` **改造后的构造器** ```javascript function Boss() { this.age = 0; this.birthdate = null; this.name = ''; this.tasks = []; } Boss.prototype = { title: 'Boss', gretting: function(){console.log('I am a Boss!');} } var X = new Boss(); var Q = new Boss(); // X 与 Q 中具有完全一致(不必唯一的属性或方法) // 并耗用内存的共享部分 // this.title 与 this.gretting var X = new Boss(); var Q = new Boss(); // X 与 Q 拥有相同的原型 Boss.prototype ``` ### 原型链 使用原型继承的方法会产生原型链。JavaScript 中对于对象的查找、修改和删除都是通过原型链来完成的。 **判断属性是否为对象本身** ```javascript objectName.hasOwnProperty('propertyName'); // 返回布尔值 true 或 false ``` #### 属性查找 对象的属性查找会更随原型链依次查找,如果在当前环境中无法找到需要的属性则会继续向下一层原型中继续寻找。 #### 属性修改 在 JavaScript 中对于对象属性的修改永远只修改对象自身的属性(不论是来源于对象本身还是对象的原型)。当创建当前对象不存在属性时(即便原型拥有此属性),也会为此对象增加改属性。 **修改原型上的属性** 修改原型属性会印象所有被创建出的对象现有的属性和方法。 ```javascript ClassName.prototype.propertyName = 'new value'; ClassName.prototype.methodName = function(){...}; ``` #### 属性删除 `delete objectName.propertyName` 只可删除对象自身的属性,无法删除对象的原型属性。 #### Object.create(proto[, propertiesObject]) 其为ECMAScript 5 中提出的新建立对象的方式。在 `X` 中使用隐式的原型对象指向 `boss` 对象,并将其设为 `X` 对象的原型。 ```javascript var boss = { title: 'Boss', gretting: function(){console.log('Hi, I am a Boss!');} }; var X = Object.create(boss); X.gretting(); // Hi, I am a Boss! ``` **低版本中实现 Object.create 功能** 此种方式仍需使用 `ClassName.prototype` 的方式来实现。 ```javascript var clone = (function(){ var F = function(){}; return function(proto) { F.prototype = proto; return new F(); } })(); ``` ### 面向对象的应用 #### 全局变量 全局变量可在程序任意位置进行访问和修改的变量。滥用全局变量会导致,命名冲突,导致程序不稳定。 **全局标量的三种定义方法:** - `var gloablVal = 'value';` 。 - `window.gloablVal = 'value';` 附加于 `window` 对象上 - `gloablVal = 'value';` 不使用 `var` 关键字,也附加于 `windwo` 对象 NOTE:`delete` 无法删除在代码最顶端定义的全局标量 `var globale` #### 封装 信息隐藏可以保证程序的稳定,将内部信息进行隐藏。其他语言中可词用访问权限来实现封装的概念,像 `private`、`public`。 **JavaScript 中的封装**可使用函数的方法(闭包)。 ```javascript // 模拟 private 的属性 function ClassName(){ var _property = ''; this.getProperty = function(){ return _property; }; } // 模拟 protected 属性,使用人为约束规则 var pro = ClassName.prototype; pro._protectedMethod = function(){...}; pro.publicMethod = function(){...}; ``` #### 继承 ##### 原型继承 原型继承的方式为 JavaScript 中固有的继承方式。 ```javascript var proto = { action1: function(){}, action2: function(){} } var obj = Object.create(proto); ``` 在不支持 EM5 中的实现方法: ```javascript var clone = (function(){ var F = function(){}; return function(proto) { F.prototype = proto; return new F(); } })(); ``` ##### 类继承 使用原型链继承的方式模拟其他语言类继承的特性。 ```javascript function ClassA() { ClassA.classMethod = function(){}; ClassA.prototype.api = function(){}; function ClassB() { ClassA.apply(this, argument); } ClassB.prototype = new ClassA(); ClassB.prototype.constructor = ClassB; ClassB.prototype.api = function(){ ClassA.prototype.api.apply(this, arguments); } } // ClassA 为父类 // ClassB 为子类 var b = new ClassB(); b.api(); ``` ![](../img/C/class-inherence.jpg) ================================================ FILE: 03-FEND_Note-master/chapter2/11_js_type_determin.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [类型识别](#%E7%B1%BB%E5%9E%8B%E8%AF%86%E5%88%AB) ### 类型识别 - `typeof` - `Object.prototype.toString` - `constructor` - `instanceof` **typeof**: - 可以是标准类型(Null 除外) - 不可识别具体的对象类型(Function 除外) **Object.prototype.toString**: - 可是识别标准类型及内置对象类型(例如,Object, Date, Array) - 不能识别自定义对象类型 **constructor**: - 可以识别标准类型(Undefined/Null 除外) - 可识别内置对象类型 - 可识别自定义对象类型 ```javascript function getConstructiorName(obj) { return obj && obj.constructor && obj.constructor.toString().match(/function\s*([^(]*)/)[1]; } getConstructiorName([]) === "Array"; // true ``` **instanceof**: - 不可判别原始类型 - 可判别内置对象类型 - 可判别自定义对象类型 JavaScript的数据类型可以分为:标准类型和对象类型。 `标准类型有`:undefined Null Boolean Date Number Object `对象类型(构造器类型)`:Boolean Date Number Object Array Date Error Function RegExp 用来判断数据类型的一般有四种方式,分别是: - `typeof ` - `Prototype.toString()` - `constructor` - `instanceof` **下面我们写一个HTML来检验一下:** ```html JavaScript类型判断
typeof toString constructor instanceof
undefined
Null
Boolean
Number
String
Object
-----------------------
Boolean Object
Number Object
String Object
Object Object
Array Object
Date Object
Error Object
Function Object
RegExp Object
-----------------------
Point Objct
``` 执行的结果如下: ![](../img/J/javascript-type-judge.png) 其中红色的单元格表示该判断方式不支持的类型。 ================================================ FILE: 03-FEND_Note-master/chapter2/12_reg_exp.md ================================================ ## 正则表达式 正则表达式为标书字符串规则的表达式。下面为两个例子为在 JavaScript 中的使用, - `/pattern/attrs` - `new RegExp(pattern, attrs)` ### 锚点 **锚点**用于匹配一个位置,下列为常用的锚点 - `^` 起始的位置 `/^http/` - `$` 结尾的位置 `/\.jpg$/` - `\b` 单词边界 ### 字符类 **字符类**用于匹配一类字符中的一个,下面为几个常用的例子, - `[abc]` 可用于匹配 `a` `b` 还有 `c` - `[0-9]` 可用于匹配一个数字 - `[^0-9]` 可用于匹配一个**非**数字 - `[a-z]` 可用于匹配一个字母 - `.` 任意字符但换行符除外 ### 元字符 **元字符**为具有特殊意义的字符。常见的有, - `^`, `$`, `\b` - `\d` 用于匹配数字 `[0-9]` - `\D` 用于匹配 `[^\d]` - `\s` 用于匹配空白符 - `\S` 用于匹配非空白符 `[^\s]` - `\w` 用于匹配任意单词字符(例如程序中的变量字符) `[A-Za-z0-9_]` - `\W` 用于匹配非单词字符 `[^\W]` ### 量词 量词用于表现字符出现的次数。可用的连词如下, - `{m,n}` 用于表示出现 `m` 到 `n` 次之间。 - `*` 用于表示出现 `0` 到无穷之间也就等同于 `{0,}` - `?` 用于表示出现 `0` 次到 `1` 次也等同于 `{0,1}` - `+` 用于表现出现 `1` 次以及一次以上也等同于 `{1,}` ### 转移符 **转义符**需要在匹配的字符是元字符的时候使用。使用 `\` 来进行转移即可。 ### 多选分支 **多选分支**用于表示*或*的概念。`/thi(c|n)k/` 其又等同于 `/thi[cn]k/`。其还可以用于匹配文件扩展名 `/\.(png|jpg|jpeg|gif)$/` 。 ### 常用方法 #### 测试:regxObj.test(str) 其用于测试正则表达式与指定字符串是否匹配。 ``` /123/.test(123); // true /123/.test(111); // false /123/.test('x123'); // true ``` #### 捕获 其用于保存所匹配到的字符串为后续开发所用。`()` 可用于捕获,正则表达式再运行时会将其保存下来,`(?:)` 则不予保存。 ``` // str.match(regexp) var url = 'http://www.google.com/query?test=li-xinyang#cool'; var reg = /(https?:)\/\/([^\/]+)(\/[^\?]*)?(\?[^#]*)?(#.*)?/; var arr = url.match(reg); var protocol = arr[1]; var host = arr[2]; var pathname = arr[3]; var search = arr[4]; var hash = arr[5]; ``` #### 搜索与替换 `regexpObj.exec(str)` 可以提供更强大的检索,它可以提供更详尽的结果 `index` 也可以提供过程状态 `lastIndex`。 `str.replace(regex/substr, replacement)` 可以使用正则表达式来对字符串进行替换。 ================================================ FILE: 03-FEND_Note-master/chapter3/00_intro.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [DOM 编程艺术](#dom-%E7%BC%96%E7%A8%8B%E8%89%BA%E6%9C%AF) # DOM 编程艺术 DOM 编程就是使用 W3C 定义的 API (Application Program Interface) 来操作 HTML 文档 (此处不局限于 HTML,亦可操作 XHTML、XML 等),使用户可以与进行页面交互。 你需要了解节点、属性、样式等基本 DOM 操作,DOM 事件模型,数据存储 (Cookie、Storage) 与数据通信 (Ajax) ,JavaScript 动画,音频、视频、Canvas 等 HTML5 特性,表单、列表操作。 ================================================ FILE: 03-FEND_Note-master/chapter3/01_dom_tree.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [文档树](#%E6%96%87%E6%A1%A3%E6%A0%91) - [HTML 转换 DOM 树](#html-%E8%BD%AC%E6%8D%A2-dom-%E6%A0%91) - [节点遍历](#%E8%8A%82%E7%82%B9%E9%81%8D%E5%8E%86) - [节点类型](#%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B) - [元素遍历](#%E5%85%83%E7%B4%A0%E9%81%8D%E5%8E%86) ## 文档树 Document Object Model (DOM) 为文档**对象**模型, 它使用对象的表示方式来表示对应的文档结构及其中的内容。 下面为一个样例 `p` 元素在文档中的对象所包含的所有属性。 ```html

Hello, World!

``` ``` p#targetaccessKey: "" align: "" attributes: Named NodeMapbaseURI: "" childElementCount: 0 childNodes: NodeList[1] children: HTMLCollection[0] classList: DOMTokenList[0] className: "" clientHeight: 0 clientLeft: 0 clientTop: 0 clientWidth: 0 contentEditable: "inherit" dataset: DOM StringMapdir: "" draggable: false firstChild: text firstElementChild: null hidden: false id: "target" innerHTML: "Hello, World!" innerText: "Hello, World!" isContentEditable: false lang: "" lastChild: text lastElementChild: null localName: "p" namespaceURI: "http://www.w3.org/1999/xhtml" nextElementSibling: null nextSibling: null nodeName: "P" nodeType: 1 nodeValue: null offsetHeight: 0 offsetLeft: 0 offsetParent: null offsetTop: 0 offsetWidth: 0 onabort: null onautocomplete: null onautocompleteerror: null onbeforecopy: null onbeforecut: null onbeforepaste: null onblur: null oncancel: null oncanplay: null oncanplaythrough: null onchange: null onclick: null onclose: null oncontextmenu: null oncopy: null oncuechange: null oncut: null ondblclick: null ondrag: null ondragend: null ondragenter: null ondragleave: null ondragover: null ondragstart: null ondrop: null ondurationchange: null onemptied: null onended: null onerror: null onfocus: null oninput: null oninvalid: null onkeydown: null onkeypress: null onkeyup: null onload: null onloadeddata: null onloadedmetadata: null onloadstart: null onmousedown: null onmouseenter: null onmouseleave: null onmousemove: null onmouseout: null onmouseover: null onmouseup: null onmousewheel: null onpaste: null onpause: null onplay: null onplaying: null onprogress: null onratechange: null onreset: null onresize: null onscroll: null onsearch: null onseeked: null onseeking: null onselect: null onselectstart: null onshow: null onstalled: null onsubmit: null onsuspend: null ontimeupdate: null ontoggle: null onvolumechange: null onwaiting: null onwebkitfullscreenchange: null onwebkitfullscreenerror: null onwheel: null outerHTML: "

Hello, World!

" outerText: "Hello, World!" ownerDocument: document parentElement: null parentNode: null prefix: null previousElementSibling: null previousSibling: null scrollHeight: 0 scrollLeft: 0 scrollTop: 0 scrollWidth: 0 shadowRoot: null spellcheck: true style: CSSStyle DeclarationtabIndex: -1 tagName: "P" textContent: "Hello, World!" title: "" translate: true webkitdropzone: "" __proto__: HTMLParagraphElement ``` 通过使用 DOM 提供的 API (Application Program Interface) 可以动态的修改节点(node),也就是对 DOM 树的直接操作。 浏览器中通过使用 JavaScript 来实现对于 DOM 树的改动。 **DOM 包含** - DOM Core - DOM HTML - DOM Style - DOM Event ### HTML 转换 DOM 树 ```html My title My Link

My header

``` ![](../img/D/dom-tree.gif) ### 节点遍历 在元素节点中提取自己所需的节点,并予以操作。 ```Javascript // Document.getElementsByTagName() // 更具标签名找到目标节点的集合,此例中为

My header

var node = document.getElementsByTagName('h1')[0]; // Node.parentNode; // 获得目标节点的父节点,此例中为 body 元素 node.parentNode; // Node.firstChild // 获得目标节点的第一个子节点,此例中为 "My header" node.firstChild; // Node.lastChild // 获得目标节点的最后一个子节点,此例中为 "My header" node.lastChild; // Node.previousSibling; // 获得目标节点的前一个相邻节点 node.previousSibling; // Node.nextSibling; // 获得目标节点的下一个相邻节点 node.nextSibling; ``` ### 节点类型 **常用节点类型** - ELEMENT_NODE 可使用 `Document.createElement('elementName');` 创建 - TEXT_NODE 可使用 `Document.createTextNode('Text Value');` 创建 **不常用节点类型** - COMMENT_NODE - DOCUMENT_TYPE_NODE **不同节点对应的NodeType类型** 此值可以通过 `Node.nodeType` 来获取。 |节点编号|节点名称| |--------|--------| |1|Element| |2|Attribute| |3|Text| |4|CDATA Section| |5|Entity Reference| |6|Entity| |7|Processing Instrucion| |8|Comment| |9|Document| |10|Document Type| |11|Document Fragment| |12|Notation| NOTE:此处需要清楚`节点`和`元素`的区别。我们平常说的`元素` 其实指的是节点中得`元素节点`,所以说`节点`包含`元素`,节点还包括文本节点、实体节点等。 ### 元素遍历 元素节点符合 HTML DOM 树规则,所以它与 DOM 中存在的节点相似。 ```

Hello, Xinyang! 回到 主页

``` ``` // 在选取元素节点后 p.firstElementChild; // Xinyang p.lastElementChild; // 主页 em.nextElementSibling; // 主页 em.previousElementSibling; // "Hello," ``` ================================================ FILE: 03-FEND_Note-master/chapter3/02_node_manipulation.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [节点操作](#%E8%8A%82%E7%82%B9%E6%93%8D%E4%BD%9C) - [获取节点](#%E8%8E%B7%E5%8F%96%E8%8A%82%E7%82%B9) - [接口获取节点](#%E6%8E%A5%E5%8F%A3%E8%8E%B7%E5%8F%96%E8%8A%82%E7%82%B9) - [getElementById](#getelementbyid) - [getElementsByTagName](#getelementsbytagname) - [getElementsByClassName](#getelementsbyclassname) - [querySelector / querySelectorAll](#queryselector--queryselectorall) - [创建节点](#%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B9) - [修改节点](#%E4%BF%AE%E6%94%B9%E8%8A%82%E7%82%B9) - [插入节点](#%E6%8F%92%E5%85%A5%E8%8A%82%E7%82%B9) - [appendChild](#appendchild) - [insertBefore](#insertbefore) - [删除节点](#%E5%88%A0%E9%99%A4%E8%8A%82%E7%82%B9) - [innerHTML](#innerhtml) ## 节点操作 因为 DOM 的存在,这使我们可以通过 JavaScript 来获取、创建、修改、或删除节点。 NOTE:下面提供的例子中的 `element` 均为元素节点。 ### 获取节点 **父子关系** - `element.parentNode` - `element.firstChild`/`element.lastChild` - `element.childNodes`/`element.children` **兄弟关系** - `element.previousSibling`/`element.nextSibling` - `element.previousElementSibling`/`element.nextElementSibling` 通过节点直接的关系获取节点会导致代码维护性大大降低(节点之间的关系变化会直接影响到获取节点),而通过接口则可以有效的解决此问题。 ```html ELEMENT_NODE & TEXT_NODE
  • First
  • Second
  • Third
  • Fourth

Hello

``` NTOE:细心地人会发现,在节点遍历的例子中,body、ul、li、p节点之间是没有空格的,因为如果有空格,那么空格就会被当做一个TEXT节点,从而用ulNode.previousSibling获取到得就是一个空的文本节点,而不是 `
  • First
  • ` 节点了。即节点遍历的几个属性会得到所有的节点类型,而元素遍历只会得到相对应的元素节点。一般情况下,用得比较多得还是元素节点的遍历属性。 **实现浏览器兼容版的element.children** 有一些低版本的浏览器并不支持 `element.children` 方法,但我们可以用下面的方式来实现兼容。 ```html Compatible Children Method
    123

    ppp

    h1

    ``` NOTE:此兼容方法为初稿,还未进行兼容性测试。 #### 接口获取元素节点 - `getElementById` - `getElementsByTagName` - `getElementsByClassName` - `querySelector` - `querySelectorAll` |API|只作用于 `document`|唯一返回值|live| |---|:-----------------:|:--------:|:--:| |getElementById|√|√|| |getElementsByTagName|||√| |getElementsByClassName|||√| |querySelectorAll|||| |querySelector||√||| ##### getElementById 获取文档中指定 `id` 的节点**对象**。 ```javascript var element = document.getElementById('id'); ``` ##### getElementsByTagName 动态的获取具有指定标签元素节点的集合(其返回值会被 DOM 的变化所影响,其值会发生变化)。此接口可直接通过元素而获取,不必直接作用于 `document` 之上。 ```javascript // 示例 var collection = element.getElementsByTagName('tagName'); // 获取指定元素的所有节点 var allNodes = document.getElementsByTagName('*'); // 获取所有 p 元素的节点 var elements = document.getElementsByTagName('p'); // 取出第一个 p 元素 var p = elements[0]; ``` ##### getElementsByClassName 获取指定元素中具有指定 `class` 的所有节点。多个 `class` 可的选择可使用空格分隔,与顺序无关。 ```javascript var elements = element.getElementsByClassName('className'); ``` NOTE:IE9 及一下版本不支持 `getElementsByClassName` **兼容方法** ```javascript function getElementsByClassName(root, className) { // 特性侦测 if (root.getElementsByClassName) { // 优先使用 W3C 规范接口 return root.getElementsByClassName(className); } else { // 获取所有后代节点 var elements = root.getElementsByTagName('*'); var result = []; var element = null; var classNameStr = null; var flag = null; className = className.split(' '); // 选择包含 class 的元素 for (var i = 0, element; element = elements[i]; i++) { classNameStr = ' ' + element.getAttribute('class') + ' '; flag = true; for (var j = 0, name; name = className[j]; j++) { if (classNameStr.indexOf(' ' + name + ' ') === -1) { flag = false; break; } } if (flag) { result.push(element); } } return result; } } ``` ##### querySelector / querySelectorAll 获取一个 `list` (其返回结果不会被之后 DOM 的修改所影响,获取后不会再变化)符合传入的 CSS 选择器的第一个元素或全部元素。 ```javascript var listElementNode = element.querySelector('selector'); var listElementsNodes = element.querySelectorAll('selector'); var sampleSingleNode = element.querySelector('#className'); var sampleAllNodes = element.querySelectorAll('#className'); ``` NOTE: IE9 一下不支持 `querySelector` 与 `querySelectorAll` ### 创建节点 创建节点 -> 设置属性 -> 插入节点 ```javascript var element = document.createElement('tagName'); ``` ### 修改节点 **textContent** 获取或设置节点以及其后代节点的文本内容(对于节点中的所有文本内容)。 ```javascript element.textContent; // 获取 element.textContent = 'New Content'; ``` NOTE:不支持 IE 9 及其一下版本。 **innerText** (不符合 W3C 规范) 获取或设置节点以及节点后代的文本内容。其作用于 `textContent` 几乎一致。 ```javascript element.innerText; ``` NOTE:不符合 W3C 规范,不支持 FireFox 浏览器。 **FireFox 兼容方案** ```javascript if (!('innerText' in document.body)) { HTMLElement.prototype.__defineGetter__('innerText', function(){ return this.textContent; }); HTMLElement.prototype.__defineSetter__('innerText', function(s) { return this.textContent = s; }); } ``` ### 插入节点 #### appendChild 在指定的元素**内**追加一个元素节点。 ```javascript var aChild = element.appendChild(aChild); ``` #### insertBefore 在指定元素的指定节点前插入指定的元素。 ```javascript var aChild = element.insertBefore(aChild, referenceChild); ``` ### 删除节点 删除指定的节点的子元素节点。 ```javascript var child = element.removeChild(child); ``` ### innerHTML 获取或设置指定节点之中所有的 HTML 内容。替换之前内部所有的内容并创建全新的一批节点(去除之前添加的**事件**和**样式**)。`innerHTML` 不检查内容,直接运行并替换原先的内容。 NOTE:只建议在创建全新的节点时使用。不可在用户可控的情况下使用。 ```javascript var elementsHTML = element.innerHTML; ``` **存在的问题** - 低版本 IE 存在内存泄露 - 安全问题(用户可以在名称中运行脚本代码) ================================================ FILE: 03-FEND_Note-master/chapter3/03_attribute.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [属性操作](#%E5%B1%9E%E6%80%A7%E6%93%8D%E4%BD%9C) - [HTML 属性与 DOM 属性的对应](#html-%E5%B1%9E%E6%80%A7%E4%B8%8E-dom-%E5%B1%9E%E6%80%A7%E7%9A%84%E5%AF%B9%E5%BA%94) - [属性操作方式](#%E5%B1%9E%E6%80%A7%E6%93%8D%E4%BD%9C%E6%96%B9%E5%BC%8F) - [Property Accessor](#property-accessor) - [getAttribute / setAttribute](#getattribute--setattribute) - [dataset](#dataset) ## 属性操作 ### HTML 属性与 DOM 属性的对应 每个 HTML 属性都会对应相应的 DOM 对象属性。 ```html
    ``` ```javascript input.id; // 'username' input.type; // 'text' input.className; // 'text' label.htmlFor; // 'username' ``` ### 属性操作方式 #### Property Accessor 通过属性方法符得到的属性为转换过的实例对象(并非全字符串)。 **特点** - X 通用行差(命名异常,使用不同的命名方式进行访问) - X 扩展性差 - √ 实用对象(取出后可直接使用) **读取属性** ```html
    ``` ```javascript input.className; // 'text' input[id]; // 'username' ``` **写入属性** 可增加新的属性或改写已有属性。 ```javascript input.value = 'new value'; input[id] = 'new-id'; ``` #### getAttribute / setAttribute **特点** - X 仅可获取字符串(使用时需转换) - √ 通用性强 **读取属性** 获取到的均为属性的**字符串**。 ```javascript var attribtue = element.getAttribute('attributeName'); ``` **写入属性** 可增加新的属性或改写已有属性。 ```javascript element.setAttribute('attributeName', value); ``` #### dataset 自定义属性,其为 `HTMLElement` 上的属性也是 `data-*` 的属性集。主要用于在元素上保存数据。获取的均为**属性字符串**。数据通常使用 AJAX 获取并存储在节点之上。 ```html
    ``` ```javascript div.dataset.id; // '1234' div.dataset.username; // 'x' div.dataset.email; // 'mail@gmail.com' ``` NOTE:`dataset` 在低版本 IE 不可使用,但可通过 `getAttribute` 与 `setAttribute` 来做兼容。 ================================================ FILE: 03-FEND_Note-master/chapter3/04_style_manipulation.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [样式操作](#%E6%A0%B7%E5%BC%8F%E6%93%8D%E4%BD%9C) - [CSS 对应 DOM 对象](#css-%E5%AF%B9%E5%BA%94-dom-%E5%AF%B9%E8%B1%A1) - [内部样式表](#%E5%86%85%E9%83%A8%E6%A0%B7%E5%BC%8F%E8%A1%A8) - [行内样式](#%E8%A1%8C%E5%86%85%E6%A0%B7%E5%BC%8F) - [更新样式](#%E6%9B%B4%E6%96%B0%E6%A0%B7%E5%BC%8F) - [element.style](#elementstyle) - [element.style.cssText](#elementstylecsstext) - [更新 class](#%E6%9B%B4%E6%96%B0-class) - [统一更新多个元素样式](#%E7%BB%9F%E4%B8%80%E6%9B%B4%E6%96%B0%E5%A4%9A%E4%B8%AA%E5%85%83%E7%B4%A0%E6%A0%B7%E5%BC%8F) - [获取样式](#%E8%8E%B7%E5%8F%96%E6%A0%B7%E5%BC%8F) - [element.style](#elementstyle-1) - [window.getComputedStyle()](#windowgetcomputedstyle) ## 样式操作 通过 JavaScript 动态修改页面样式。 ### CSS 对应 DOM 对象 ![](../img/C/css-dom-overview.jpg) ```html // var element = document.querySelector('link'); // 对应于 element.sheet // var element = document.querySelector('style'); // 对应于 element.sheet // 整个页面的全部样式(不包括行内样式) document.styleSheets

    Text Color

    // var element = document.querySelector('p'); // 对应于 element.style ``` #### 内部样式表 ```html // 1.对应所有样式的列表 // body{margin:30;} // p{color: #aaa; line-height:20px} element.sheet.cssRules; // 2.对应相应的 CSS 选择器 // p element.sheet.cssRules[1].selectorText; // 3.对应一个样式 // p{color: #aaa; line-height:20px} element.sheet.cssRules[1] // 4.对应所有样式的键值对 // color: #aaa; line-height:20px element.sheet.cssRules[1].style; // 5.对应的属性值 // #aaa element.sheet.cssRules[1].stlye.color; element.sheet.cssRules[1].lineHeight; ``` #### 行内样式 其对应于 `CSSStyleDeclaration` 的对象。 ```html element.style.color; // 获取行内样式的键值对 ``` ### 更新样式 #### element.style ```javascript element.style.color = 'red'; element.style.background = 'black'; ``` 增加样式后得到的结果 ```html
    ``` **缺点** - 每个属性的更新都需要一个命令 - 命名异常(以驼峰命名法命名属性) #### element.style.cssText 一次同时设置多个行内样式,其结果同 `element.style` 单独设置相同。 ```javascript element.style.cssText = 'color: red; background: black'; ``` 增加样式后得到的结果 ```html
    ``` **以上两种方式均将样式混合在逻辑当中。** #### 更新 class 首先需要创建对应样式的 CSS 样式。 ```html .angry { color: red; background: black; } ``` 然后再在 JavaScript 中,在对应的事件中给元素添加需要的类即可。 ```javascript element.className += ' angry'; ``` 增加样式后得到的结果 ```html
    ``` #### 统一更新多个元素样式 以上方法均不适合同时更新多个样式,通过更换样式表的方式则可同时更改多个页面中的样式。将需要的大量样式也在一个**皮肤样式**表中,通过 JavaScript 来直接更换样式表来进行样式改变。(此方法也可用于批量删除样式) ```html ``` ```javascript element.setAttribute('href', 'style2.css'); ``` ### 获取样式 #### element.style 其对应的为元素的**行内样式表**而不是实际样式表。 ```html ``` ```javascript element.style.color; // "" ``` line-height: 200px #### window.getComputedStyle() 将需要取出样式的目标元素传入 `window.getComputedStyle()` 函数中,即可得到对应元素的实际样式。注意的是这里获取到的样式值为**只读**属性不可修改! NOTE:获取的实际为 `CSSStyleDeclaration` 的实例对象。 NOTE+:此方法不支持 IE9 以下版本,IE9 中需使用 `element.currentStyle` 来做兼容。 ```javascript var style = window.getComputedStyle(element[, pseudoEle]); ``` ```html ``` ```javascript window.getComputedStyle(element).color; // 'rgb(0,0,0)' ``` ================================================ FILE: 03-FEND_Note-master/chapter3/05_events.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [DOM 事件](#dom-%E4%BA%8B%E4%BB%B6) - [事件流](#%E4%BA%8B%E4%BB%B6%E6%B5%81) - [事件注册](#%E4%BA%8B%E4%BB%B6%E6%B3%A8%E5%86%8C) - [注册事件](#%E6%B3%A8%E5%86%8C%E4%BA%8B%E4%BB%B6) - [取消事件](#%E5%8F%96%E6%B6%88%E4%BA%8B%E4%BB%B6) - [触发事件](#%E8%A7%A6%E5%8F%91%E4%BA%8B%E4%BB%B6) - [浏览器兼容型](#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E5%9E%8B) - [兼容低版本代码实现](#%E5%85%BC%E5%AE%B9%E4%BD%8E%E7%89%88%E6%9C%AC%E4%BB%A3%E7%A0%81%E5%AE%9E%E7%8E%B0) - [事件对象](#%E4%BA%8B%E4%BB%B6%E5%AF%B9%E8%B1%A1) - [属性和方法](#%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3%95) - [通用属性和方法](#%E9%80%9A%E7%94%A8%E5%B1%9E%E6%80%A7%E5%92%8C%E6%96%B9%E6%B3%95) - [阻止事件传播](#%E9%98%BB%E6%AD%A2%E4%BA%8B%E4%BB%B6%E4%BC%A0%E6%92%AD) - [阻止默认行为](#%E9%98%BB%E6%AD%A2%E9%BB%98%E8%AE%A4%E8%A1%8C%E4%B8%BA) - [事件分类](#%E4%BA%8B%E4%BB%B6%E5%88%86%E7%B1%BB) - [Event](#event) - [window](#window) - [image](#image) - [UIEvent](#uievent) - [MouseEvent](#mouseevent) - [属性](#%E5%B1%9E%E6%80%A7) - [MouseEvent 顺序](#mouseevent-%E9%A1%BA%E5%BA%8F) - [实例:拖动元素](#%E5%AE%9E%E4%BE%8B%EF%BC%9A%E6%8B%96%E5%8A%A8%E5%85%83%E7%B4%A0) - [滚轮事件(Wheel)](#%E6%BB%9A%E8%BD%AE%E4%BA%8B%E4%BB%B6%EF%BC%88wheel%EF%BC%89) - [FocusEvent](#focusevent) - [InputEvent](#inputevent) - [KeyboardEvent](#keyboardevent) - [事件代理](#%E4%BA%8B%E4%BB%B6%E4%BB%A3%E7%90%86) ## DOM 事件 何为 DOM 事件,HTML DOM 使JavaScript 有能力对 HTML 事件做出反应。(例如,点击 DOM 元素,键盘被按,输入框输入内容以及页面加载完毕等) ### 事件流 一个 DOM 事件可以分为`捕获过程`、`触发过程`、`冒泡过程`。 DOM 事件流为 DOM 事件的处理及执行的过程。下面以一个``元素被点击为例。 ![](../img/E/event_flow.jpg) 1. [红虚线]Capture Phase(事件捕获过程)当 DOM 事件发生时,它会从window节点一路跑下去直到触发事件元素的父节点为止,去捕获触发事件的元素。 2. [红绿实线]Target Phase(事件触发过程)当事件被捕获之后就开始执行事件绑定的代码 3. [绿虚线]Bubble Phase(冒泡过程)当事件代码执行完毕后,浏览器会从触发事件元素的父节点开始一直冒泡到window元素(**即元素的祖先元素也会触发这个元素所触发的事件**) **关于捕获过程的补充** 如果有一个支持三个阶段的事件,它一定在触发时遵循下面的顺序: ``` Capture -> Target -> Bubbling ``` ![](../img/E/event-phases.png) 使用下面的代码来举例: ```JavaScript // 添加Capture阶段事件 docuemnt.addEventListener('click',function(){ alert('capture:'+1); },true); tableNode.addEventListener('click',function(){ alert('capture:'+2); },true); tdNode.addEventListener('click',function(){ alert('capture:'+3); },true); // 添加Bubbling阶段事件 docuemnt.addEventListener('click',function(){ alert('bubble:'+1); }); tableNode.addEventListener('click',function(){ alert('bubble:'+2); }); tdNode.addEventListener('click',function(){ alert('bubble:'+3); }); ``` 输出结果为: ``` capture:1 capture:2 capture:3 bubble:3 bubble:2 bubble:1 ``` ![](../img/E/event-apply.png) ```javascript // 对document添加了三个bubbling阶段的事件 document.addEventListener('click',function(){ alert(1); }); document.addEventListener('click',function(){ alert(2); }); document.addEventListener('click',function(){ alert(3); }); ``` 如上面的代码所示,其为同一节点添加了同一阶段的多个事件,那执行顺序如何呢? 早期并没有规范定义,DOM 3 中规范已经明确规定 同一节点同一阶段的事件应按照注册函数的顺序执行。 > 在实际项目过程中,某些情况下比如若干的组件或者模块都需要监听某个节点的某个事件,但是组件或者模块的生成(即添加事件的时机)是不一定保证顺序的,所以这个情况下如果某个组件对这个节点的这个事件的优先级特别高(需要保证必须先触发这个组件里的这个事件)而这个平台又支持这个阶段事件的话可以添加 capture 阶段事件,用三阶段的顺序来保证,比如移动平台模拟手势的实现会添加 document 上 touchXXX 的 capture 阶段事件,以优先识别手势操作 > 当然实践过程中考虑到不同浏览器对三阶段支持的情况的差异,大部分情况下都采用的是 bubbling 阶段的事件 > > —— 蔡剑飞 网易前端工程师 NOTE:低版本 IE 中并未实现捕获过程。也不是所有事件均存在这三个完整的过程(例如 `load` 没有冒泡事件) NOTE+:在这三个阶段中无论将**事件捕获**和**事件处理**注册到任意一个父或祖父节点上都会被触发事件。 ### 事件注册 事件注册,取消以及触发其作用对象均为一个 DOM 元素。 #### 注册事件 ```javascript eventTarget.addEventListener(type, listener[,useCapture]) ``` - evenTarget 表示要绑定事件的DOM元素 - type 表示要绑定的事件,如:"click" - listener 表示要绑定的函数 - useCapture 可选参数,表示是否捕获过程 NOTE:`useCapture` 为设定是否为捕获过程,默认事件均为冒泡过程,只有 `useCapture` 为 `true` 时才会启用捕获过程。 ```javascript // 获取元素 var elem = document.getElemenyById('id'); // 事件处理函数 var clickHandler = function(event) { // statements }; // 注册事件 elem.addEventListener('click', clickHandler, false); // 第二种方式,不建议使用 elem.onclick = clickHandler; // 或者来弥补只可触发一个处理函数的缺陷 elem.onclick = function(){ clickHandler(); func(); // 其他处理函数 }; ``` #### 取消事件 ```javascript eventTarget.removeEventListener(type, listener[,useCapture]); ``` - evenTarget 表示要绑定事件的DOM元素 - type 表示要绑定的事件,如:"click" - listener 表示要绑定的函数 - useCapture 可选参数,表示是否捕获过程 ```javascript // 获取元素 var elem = document.getElemenyById('id'); // 取消事件 elem.removeEventListener('click', clickHandler, false); // 第二种方式。不建议使用 elem.onclick = null; ``` #### 触发事件 点击元素,按下按键均会触发 DOM 事件,当然也可以以通过代码来触发事件。 ```javascript eventTarget.dispatchEvent(type); // 获取元素 var elem = document.getElemenyById('id'); // 触发事件 elem.dispatchEvent('click'); ``` #### 浏览器兼容型 以上均为 **W3C**定义的标准定义,但早期浏览器 IE8 及其以下版本,均没有采用标准的实现方式。不过这些低版本浏览器也提供了对于 DOM 事件的注册、取消以及触发的实现。 **事件注册与取消**,`attchEvent/detachEvent`。**事件触发**,`fireEvent(e)`,其也**不存在**捕获阶段(Capture Phase)。 ##### 兼容低版本代码实现 **注册事件** ```javascript var addEvent = document.addEventListener ? function(elem, type, listener, useCapture) { elem.addEventListener(type, listener, useCapture); } : function(elem, type, listener, useCapture) { elem.attachEvent('on' + type, listener); } ``` **取消事件** ```javascript var addEvent = document.removeElementListener ? function(elem, type, listener, useCapture) { elem.removeElementListener(type, listener, useCapture); } : function(elem, type, listener, useCapture) { elem.detachEvent('on' + type, listener); } ``` ### 事件对象 调用事件处理函数时传入的信息对象,这个对象中含有关于这个事件的详细状态和信息,它就是事件对象 `event`。其中可能包含鼠标的位置,键盘信息等。 ```javascript // 获取元素 var elem = document.getElemenyById('id'); // 事件处理函数 var clickHandler = function(event) { // statements }; // 注册事件 elem.addEventListener('click', clickHandler, false); ``` NOTE:在低版本 IE 中事件对象是被注册在 `window` 之上而非目标对象上。使用下面的兼容代码既可解决。 ```javascript var elem = document.getElemenyById('id'); // 事件处理函数 var clickHandler = function(event) { event = event || window.event; // statements }; ``` #### 属性和方法 ##### 通用属性和方法 **属性** - type 事件类型 - target(srcElement IE 低版本) 事件触发节点 - currentTarget 处理事件的节点 **方法** - stopPropagation 阻止事件冒泡传播 - preventDefault 阻止默认行为 - stopImmediatePropagation 阻止冒泡传播 ###### 阻止事件传播 `event.stopPropagation()`(W3C规范方法),如果在当前节点已经处理了事件,则可以阻止事件被冒泡传播至 DOM 树最顶端即 `window` 对象。 `event.stopImmediatePropagation()` 此方法同上面的方法类似,除了阻止将事件冒泡传播值最高的 DOM 元素外,还会阻止在此事件后的事件的触发。 `event.cancelBubble=true` 为 IE 低版本中中对于阻止冒泡传播的实现。 ###### 阻止默认行为 默认行为是指浏览器定义的默认行为(点击一个链接的时候,链接默认就会打开。当我们双击文字的时候,文字就会被选中),比如单击链接可以打开新窗口。 `Event.preventDefault()` 为 W3C 规范方法,在 IE 中的实现方法为 `Event.returnValue=false`。 ### 事件分类 #### Event ![](../img/E/event_types.jpg) |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |load|NO|Window, Document, Element|None|window, image, iframe| |unload|NO|Window, Document, Element|None|window| |error|NO|Window, Element|None|window, image| |select|NO|Element|None|input, textarea| |abort|NO|Window, Element|None|window, image| ##### window - load 页面全部加载完毕 - unload 离开本页之前的卸载 - error 页面异常 - abort 取消加载 ##### image - load 图片加载完毕 - error 图标加载错误 - abort 取消图标加载 在目标图标不能正常载入时,载入备份替代图来提供用户体验。 ```javascript ``` #### UIEvent |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |resize|NO|Window, Element|None|window, iframe| |scroll|NO/YES|Document, Element|None|document, div| NOTE:`resize` 为改变浏览器或`iframe`的窗体大小时会触发事件,`scroll` 则会在滑动内容时触发,作用于 `Document` 则不会冒泡,作用于内部元素则会冒泡。 #### MouseEvent DOM 事件中最常见的事件之一。 |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |click|YES|Element|focus/activation|div| |dbclick|YES|Element|focus/activation/select|div| |mousedown|YES|Element|drag/scroll/text selection|div| |mosuemove|YES|Element|None|div| |mouseout|YES|Element|None|div| |mouseover|YES|Element|None|div| |mouseup|YES|Element|context menu|div| |mouseenter|NO|Element|None|div| |mouseleave|NO|Element|None|div| NOTE:`mouseenter` 与 `mouseover` 的区别为前者在鼠标在子元素直接移动不会触发事件,而后者会触发。 `mouseleave` 与 `mouseout` 同上相似。 ##### 属性 - clientX, clientX - screenX, screenY - ctrlKey, shiftKey, altKey, metaKey 如果被按下则为真(true) - button(0, 1, 2) 鼠标的间位 ![](../img/M/mouse_move_event.jpg) ##### MouseEvent 顺序 鼠标的移动过程中会产生很多事件。事件的监察频率又浏览器决定。 **例子:从元素 A 上方移动过** mousemove -> mouseover(A) -> mouseenter(A) -> mousemove(A) -> mouseout(A) -> mouseleave(A) **例子:点击元素** mousedown -> [mousemove] -> mouseup -> click ###### 实例:拖动元素 ```html
    ``` ```javascript var elem = document.getElemenyById('div0'); var clientX, clientY, isMoving; var mouseDownHandler = function(event) { event = event || window.event; clientX = event.clientX; clientY = event.clientY; isMoving = true; } var mouseMoveHandler = function(event) { if (!isMoving) return; event = event || window.event; var newClientX = event.clientX, newClientY = event.clientY; var left = parseInt(elem.style.left) || 0, top = parseInt(elem.style.top) || 0; elem.style.left = left + (newClientX - clientX) + 'px'; elem.style.top = top + (newClientY - clientY) + 'px'; clientX = newClientX; clientY = newClientY; } var mouseUpHandler = function() { isMoving = false; } addEvent(elem, 'mousedown', mouseDownHandler); addEvent(elem, 'mouseup', mouseUpHandler); addEvent(elem, 'mousemove', mouseMoveHandler); ``` #### 滚轮事件(Wheel) |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |wheel|YES|Element|scroll or zoom document|div| **属性** - deltaMode 鼠标滚轮偏移量的单位 - deltaX - deltaY - deltaZ #### FocusEvent 其用于处理元素获得或失去焦点的事件。(例如输入框的可输入状态则为获得焦点,点击外部则失去焦点) |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |blur|NO|Window, Element|None|window, input| |focus|NO|Window, Element|None|window, input| |focusin|NO|window, Element|None|window, input| |focusout|NO|window, Element|None|window, input| NOTE:`blur` 失去焦点时,`focus` 获得焦点时,`focusin` 即将获得焦点,`focusout`即将失去焦点。 **属性** 一个元素失去,既另一个元素获得焦点。这里的 `relatedTarget` 则为相对的那个元素。 - relatedTarget #### InputEvent 输入框输入内容则会触发输入事件。 |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |beforeInput|YES|Element|update DOM Element|input| |input|YES|Element|None|input| NOTE:`beforeInput` 为在按键按下后即将将输入字符显示之前生成的事件。 NOTE+:IE 并没有 `InputEvent` 则需使用 `onpropertychange(IE)` 来代替。 #### KeyboardEvent 其用于处理键盘事件。 |事件类型|是否冒泡|元素|默认事件|元素例子| |--------|--------|----|--------|--------| |keydown|YES|Element|beforeInput/input/focus/blur/activation|div, input| |keyup|YES|Element|None|div, input| **属性** - key 按下的键字符串 - code - ctrlKey, shiftKey, altKey, metaKey - repeat 代表按键不松开为 true - keyCode - charCode - which ### 事件代理 事件代理是指在父节点上(可为元素最近的父节点也可为上层的其他节点)处理子元素上触发的事件,其原理是通过事件流机制而完成的。可以通过**事件对象**中获取到触发事件的对象(如下所示)。 ```javascript var elem = document.getElemenyById('id'); elem.addEventListener('click', function(event) { var e = event || window.event; var target = e.target || e.srcElement; // statements }); ``` **优点** - 需要管理的事件处理函数更少 - 内存分配更少,更高效 - 增加与删除子节点可以不额外处理事件 **缺点** - 事件管理的逻辑变的复杂(因为冒泡机制) ================================================ FILE: 03-FEND_Note-master/chapter3/06_animation.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [JavaScript 动画](#javascript-%E5%8A%A8%E7%94%BB) - [实现方式](#%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F) - [JavaScript 动画三要素](#javascript-%E5%8A%A8%E7%94%BB%E4%B8%89%E8%A6%81%E7%B4%A0) - [定时器](#%E5%AE%9A%E6%97%B6%E5%99%A8) - [常见动画](#%E5%B8%B8%E8%A7%81%E5%8A%A8%E7%94%BB) - [动画函数](#%E5%8A%A8%E7%94%BB%E5%87%BD%E6%95%B0) ## JavaScript 动画 **帧**,为动画的最小单位,一个静态的图像。**帧频**,每秒播放的帧的数量。一个动画是由很多帧组成的,因为人眼的暂留特性,当图片交替的速度大于每秒 30 帧以上既有动画的感觉。 ### 实现方式 - gif 图像形式存储,容量大,需第三方制图工具制作。 - flash 需要第三方制作工具,不推荐。 - CSS3 实现动画具有局限性 - JavaScript 可实现大部分上面几类可实现的动画效果 ### JavaScript 动画三要素 ![](../img/J/javascript-animation.jpg) ### 定时器 **setInterval** - `func` 为执行改变属性的操作 - `delay` 为出发时间间隔(毫秒为单位) - `para1` 为执行时可传入改变属性函数的参数 ```javascript var intervalId = setInterval(func, delay[, param1, param2, ...]); clearInterval(intervalId); ``` NOTE:使用 `setInterval` 可以调用一次定时器既可实现连贯的动画。使用 `clearInterval` 即可清除动画效果。 **setTimeout** - `func` 为执行改变属性的操作 - `delay` 为出发时间间隔(毫秒为单位)默认为 0 - `para1` 为执行时可传入改变属性函数的参数 ```javascript var timeoutId = setTimeout(func[, delay, param1, param2, ...]); clearTimeout(timeoutId); ``` NOTE:使用 `setTimeout` 实现动画,则需要在动画每一帧结束时再次调用定时器。但它无需清除定时器。 **区别** - `setTimeout` 在延时后只执行一次,`setInterval` 则会每隔一个延时期间后会再执行。 **requestAnimationFrame** 类似于 `setTimeout` 但是无需设定时间间隔。此定时器为 HTML5 中的新标准,其间隔时间不由用户控制,而是由显示器的刷新频率决定。(市面上的显示器刷新频率为每秒刷新60次) **优势** - 无需设置间隔时间 - 动画流畅度高 ```javascript var requestId = requestAnimationFrame(func); cancelAnimationFrame(requestId); ``` NOTE:使用它来实现动画与 `setTimeout` 类似,需要每次每帧结束时再次调用。不可设置时间间隔(系统决定),时间间隔为16.67毫秒一帧。 ### 常见动画 大多的复杂动画都是有下列的简单动画所组成的。 - 形变,改变元素的宽高 - 位移,改变元素相对位置 - 旋转 - 透明度 - 其他... ### 动画函数 下面的例子为以 px 为单位的动画代码 ```javascript var animation = function(ele, attr, from, to) { var distance = Math.abs(to - from); var stepLength = distance/100; var sign = (to - from)/distance; var offset = 0; var step = function(){ var tmpOffset = offset + stepLength; if (tmpOffset < distance) { ele.style[attr] = from + tmpOffset * sign + 'px'; offset = tmpOffset; } else { ele.style[attr] = to + 'px'; clearInterval(intervalID); } } ele.style[attr] = from + 'px'; var intervalID = setInterval(step, 10); } ``` ================================================ FILE: 03-FEND_Note-master/chapter3/07_canvas.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [Canvas](#canvas) - [渲染上下文对象](#%E6%B8%B2%E6%9F%93%E4%B8%8A%E4%B8%8B%E6%96%87%E5%AF%B9%E8%B1%A1) - [绘图步骤](#%E7%BB%98%E5%9B%BE%E6%AD%A5%E9%AA%A4) ## Canvas **Mozilla** 官方 `` 教程在[这里](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial)。 画布 `` 的默认宽高为 300 与 150 ,但是同样可以使用 CSS 设定宽高。但因为 CSS 与 JavaScript 在渲染工程中有速度的差异,所以**不建议使用** CSS 对 `` 的宽高做设定。 ```html ``` ### 渲染上下文对象 下面的 `ctx` 即为渲染上下文对象。`globalCompositeOperation` 为对于 `canvas` 绘画时的渲染属性设置,具体表现如下图所示。 ```Javascript var canvas = document.getElementById('canvasId'); var ctx = canvas.getContext('2d'); // 绘画 canvas 的属性 ctx.globalCompositeOperation = 'destination-over'; ``` ![](../img/C/canvas-global-composite.png) ### 绘图步骤 ![](../img/C/canvas-drawing-steps.png) 一个周期就是完整的一帧的绘画过程。 ![](../img/C/canvas-animation.gif) ```javascript var sun = new Image(); var moon = new Image(); var earth = new Image(); function init() { sun.src = 'Canvas_sun.png'; moon.src = 'Canvas_moon.png'; earth.src = 'Canvas_earth.png'; window.requestAnimationFrame(draw); } function draw(){ var ctx = document.getElementById('canvas').getContext('2d'); ctx.globalCompositeOperation = 'destination-over'; // 清空画布内容 ctx.clearRect(0, 0, 300, 300); ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'; ctx.strokeStyle = 'rgba(0, 153, 255, 0.4)'; // 保存画布状态 ctx.save(); ctx.translate(150, 150); // 开始绘制图形 // 地球 var time = new Date(); ctx.rotate(((2*Math.PI)/60)* time.getSeconds() + ((2*Math.PI)/60000)*time.getMilliseconds()); ctx.translate(105, 0); // 阴影 ctx.fillRect(0, -12, 50, 24); ctx.drawImage(earth, -12, -12); // 月亮 ctx.rotate(((2*Math.PI)/6)*time.getSeconds() + ((2*Math.PI)/6000)*time.getMilliseconds()); ctx.translate(0, 28.5); ctx.drawInmage(moon, -3.5, -3.5); // 恢复画布状态 ctx.restore(); ctx.beginPath(); ctx.arc(150, 150, 105, 0, Math.PI*2, false); ctx.stroke(); ctx.drawImage(sun, 0, 0, 300, 300); window.requestAnimationFrame(draw); } init(); ``` ================================================ FILE: 03-FEND_Note-master/chapter3/08_multimedia.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [多媒体](#%E5%A4%9A%E5%AA%92%E4%BD%93) - [基本用法](#%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95) - [多媒体支持类型](#%E5%A4%9A%E5%AA%92%E4%BD%93%E6%94%AF%E6%8C%81%E7%B1%BB%E5%9E%8B) - [多媒体格式兼容](#%E5%A4%9A%E5%AA%92%E4%BD%93%E6%A0%BC%E5%BC%8F%E5%85%BC%E5%AE%B9) - [HTML 属性](#html-%E5%B1%9E%E6%80%A7) - [控制多媒体](#%E6%8E%A7%E5%88%B6%E5%A4%9A%E5%AA%92%E4%BD%93) - [多媒体相关事件](#%E5%A4%9A%E5%AA%92%E4%BD%93%E7%9B%B8%E5%85%B3%E4%BA%8B%E4%BB%B6) - [Web Audio API](#web-audio-api) ## 多媒体 HTML5 前的多媒体需要借助第三方插件,例如 Flash,但是 HTML5 将网页中的多媒体带入了新的一章。 ![](../img/M/mutimedia.jpg) ### 基本用法 ```html // 音频 // 指定资源类型可以帮助浏览器更快的定位解码 // 视频 ``` ### 多媒体支持类型 HTML5 支持音频[列表](http://en.wikipedia.org/wiki/HTML5_Audio#Supported_audio_coding_formats) HTML5 支持视频[列表](http://en.wikipedia.org/wiki/HTML5_video#Browser_support) ### 多媒体格式兼容 测试音频兼容性。 ```javascript var a = new Audio(); // 检测媒体类型返回 // 支持 - 'maybe' 或 'probably' // 不支持 - '' a.canPlayType('audio/nav'); ``` ### HTML 属性 视频与音频的大部分属性和方法几乎相同。 |属性|是否必须|默认值|备注| |----|--------|------|----| |src|是||音频文件地址 URL| |controls|否|false|显示控件| |autoplay|否|false|音频就绪后自动播放| |preload|否|none|可取值为 none、metadata、auto。音频在页面加载是进行加载,并预备播放。如果使用 autoplay 则忽略该属性(该属性失效)| |loop|否|false|是否循环播放| ### 控制多媒体 **方法** - `load()` 加载资源 - `play()` 播放 - `pause()` 暂停播放 **属性** - `playbackRate` 1为正常速度播放,大于1为快速播放最高20。 - `currentTime` 调准播放时间,以秒为单位。 - `volume` 取值范围0到1 - `muted` 真假值 - `paused` 布尔值暂停 - `seeking` 布尔值跳转 - `ended` 布尔值播放完成 - `duration` 媒体时长数值 - `initialTime` 媒体开始时间 ### 多媒体相关事件 - `loadstart` 开始请求媒体内容 - `loadmetadata` 媒体元数据以加载完成(时长,编码格式等) - `canplay` 加载一些内容但可播放 - `play` 调用`play()`或设置 `autoplay` - `waiting` 缓冲数据不够,暂停播放 - `playing` 正在进行播放 **全部事件列表** 事件[列表](http://www.w3.org/wiki/HTML/Elements/audio#Media_Events) ### Web Audio API 音频 W3C 官网定义在[这里](http://webaudio.github.io/web-audio-api/) Mozilla 官方音频教程在[这](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API),以及第三方[教程 1](http://www.html5rocks.com/en/tutorials/webaudio/intro/)和[教程 2](http://webaudioapi.com/)。 ================================================ FILE: 03-FEND_Note-master/chapter3/09_network.md ================================================ **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - [数据通信](#%E6%95%B0%E6%8D%AE%E9%80%9A%E4%BF%A1) - [HTTP](#http) - [HTTP 事务](#http-%E4%BA%8B%E5%8A%A1) - [HTTP 请求报文](#http-%E8%AF%B7%E6%B1%82%E6%8A%A5%E6%96%87) - [HTTP 回复报文](#http-%E5%9B%9E%E5%A4%8D%E6%8A%A5%E6%96%87) - [常用 HTTP 方法](#%E5%B8%B8%E7%94%A8-http-%E6%96%B9%E6%B3%95) - [URL 构成](#url-%E6%9E%84%E6%88%90) - [HTTP 版本](#http-%E7%89%88%E6%9C%AC) - [常见 HTTP 状态码](#%E5%B8%B8%E8%A7%81-http-%E7%8A%B6%E6%80%81%E7%A0%81) - [AJAX](#ajax) - [AJAX 调用](#ajax-%E8%B0%83%E7%94%A8) - [open](#open) - [setRequestHeader](#setrequestheader) - [send](#send) - [请求参数序列化](#%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E5%BA%8F%E5%88%97%E5%8C%96) - [同源策略](#%E5%90%8C%E6%BA%90%E7%AD%96%E7%95%A5) - [跨域资源访问](#%E8%B7%A8%E5%9F%9F%E8%B5%84%E6%BA%90%E8%AE%BF%E9%97%AE) - [其他跨域技术](#%E5%85%B6%E4%BB%96%E8%B7%A8%E5%9F%9F%E6%8A%80%E6%9C%AF) ## 数据通信 ### HTTP HTTP 为一个通信协议。HTTP 客户端发起请求并创建端口。HTTP 服务器在端口监听客户端的请求。 HTTP 服务器在收到请求后则返回状态和所请求的内容。 **网页浏览全过程** (粗浅流程) 1. 域名解析 1. 搜索浏览器自身 DNS 缓存 1. 搜索操作系统自身 DNS 缓存(如上一级未找到或已失效) 1. 读取本地 HOST 文件 (如上一级未找到或已失效,`/etc/hosts`) 1. 浏览器发起 DNS 系统调用请求 1. ISP 服务器查找自身缓存 1. ISP 服务器发起迭代(逐域寻找需要的地址)请求 1. 得到请求资源的 IP 地址 1. 发起 HTTP “三次握手”(下面为一个超级简化版) 1. 建立连接,等待服务器确认 1. 服务器接受请求,回复客户 1. 客户端与服务器连接成功(TCP/IP 连接成功) 1. 客户根据协议发送请求 1. 服务器更具请求返回客户需求资源 1. 客户获得资源 #### HTTP 事务 ![](../img/H/http-process.png) ##### HTTP 请求报文 ![](../img/H/http-request.jpg) 其中包括主机地址,HTTP 协议版本号。头部由键值对组成。因为此请求为 GET 方法所以请求体为空。 ##### HTTP 回复报文 ![](../img/H/http-response.png) 其中包括 HTTP 版本号,状态码及状态码描述。头部依然为键值对组成。主体则为 HTML 文件。 #### 常用 HTTP 方法 **常用方法** |方法|描述|是否包含主题| |----|----|------------| |GET|从服务器获取一份文档|否| |POST|向服务器发送需要处理的数据|是| |PUT|将请求的主题部分储存在服务器上|是| |DELETE|从服务器删除一份文档|否| **不常用方法** |方法|描述|是否包含主题| |----|----|------------| |HEAD|只从服务器获取头文档的首部|否| |TRACE|对可能经过代理服务器传送到服务器上的报文进行追踪|否| |OPTIONS|决定可以在服务器上执行的方法|否| #### URL 构成 ```html http://www.github.com:8080/index.html?user=li-xinyang&lang=zh-CN#home | | | | | | protocol | | | | | hostname port | | | \ / pathname search hash host ``` **可选部分包括**: - port - pathname - search - hash NOTE:上面提供的 URL 地址仅为参考所用。 #### HTTP 版本 - HTTP/0.9 1991年 HTTP 原型,存在设计缺陷 - HTTP/1.0 第一个广泛应用版本 - HTTP/1.0+ 添加持久的 keep-alive 链接,虚拟主机支持,代理连接支持,成为非官方的事实版本 - HTTP/1.1 校正结构性缺陷,明确语义,引入重要的新能优化措施,删除不好的特性(**当前使用版本**) NOTE:此文写于2015年6月。 #### 常见 HTTP 状态码 |状态码|描述|代码描述| |------|----|--------| |200|请求成功,一般用于 GET 和 POST 方法|OK| |301|资源移动。所请求资源自动到新的 URL,浏览器自动跳转至新的 URL | Moved Permanently| |304|未修改。所请求资源未修改,浏览器读取缓存数据|Not Modified| |400|请求语法错误,服务器无法解析|Bad Request| |404|未找到资源,可以设置个性“404页面”|Not Found| |500|服务器内部错误|Internal Server Error| ![](../img/H/http-state-code.jpg) ### AJAX AJAX(Asynchronous JavaScript and HTML)异步获取数据的概念,由 Jesse James Garrett 在2005年提出。 **AJAX 请求全过程** ![](../img/A/ajax-process.jpg) #### AJAX 调用 三部完成 AJAX 调用 1. 创建 XHR 对象 2. 处理返回数据及错误处理 3. 发送请求 ```javascript var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(callback) { if (xhr.readyState === 4) { if ((xhr.status >== 200 && xhr.status < 300) || xhr.status === 304) { callback(xhr.responseText); } else { console.error('Request was unsuccessful: ' + xhr.status); } } } xhr.open('get', 'exmaple.json', true); xhr.setRequestHeader('myHeader', 'myValue'); xhr.send(null); ``` NOTE:`xhr.onload` 只针对当 `readyState === 4` 和 `status === 200` 时的事件。 ##### open ```javascript xhr.open(method, url[, async = true]); ``` - `method` 为上面说过的 HTTP 方法(例如,GET、POST) - `url` 为资源地址 - `async` 默认为真,用于设置异步请求 ##### setRequestHeader ```javascript xhr.setRequestHeader('myHeader', 'myValue'); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); ``` 用于设置头部的键值对。 ##### send ```javascript xhr.send([data=null]); xhr.send() ``` 数据可包含字符串或表单数控,但需要提前为 RequestHeader 做设置。 #### 请求参数序列化 将查询参数使用字符串,跟入资源地址中。 ```javascript xhr.open('get', 'example.json?' + 'name=value&age=value', true); ``` **对象转换字符串的函数实现** ```javascript function serialize(data) { if (!data) return ''; var pairs = []; for (var name in data) { if (!data.hasOwnProperty(name)) continue; if (typeof data[name] === 'function') continue; var value = data[name].toString(); name = encodeURIComponent(name); value = encodeURIComponent(value); pairs.push(name + '=' + value); } return pairs.join('&'); } ``` **GET 请求** ```javascript var url = 'example.json?' + serialize(formData); xhr.open('get', url, true); xhr.send(null); ``` **POST 请求** 查询参数需要作为 `send()` 的存数传入。 ```javascript xhr.open('get', 'example.json', true); xhr.send(serialize(formData)); ``` #### 同源策略 两个页面拥有相同的协议(Protocol)、端口(Port)、和主机(host)那么这两个页面就是属于同一个源(Origin)。 ```html http://www.github.com:8080/index.html?user=li-xinyang&lang=zh-CN#home | | | | | | protocol | | | | | hostname port | | | \ / pathname search hash host |-----完全一致则同源------| ``` #### 跨域资源访问 不满足同源策略的资源访问均属于跨域资源访问,W3C 定义了 [**CORS**](http://www.w3.org/TR/cors/)。现代浏览器已经实现了 CORS 的支持。 **CORS 原理实现图** ![](../img/C/cors.jpg) ##### 其他跨域技术 - Frame 代理 - JSONP - Comet - Web Sockets - ... **Frame 代理** 关于 Frame 代理的更多内容在[这里](https://github.com/genify/nej/blob/master/doc/AJAX.md)。 ![](../img/F/frame_proxy.jpg) 优点: - 参照 CORS 标准 - 支持各种请求方法 GET POST PUT DELETE 缺点: - 需要在目标服务器防止代理文件(造成延时) - 低版本在大并发消息通信机制会产生延时 **JSONP** 全程为 JSON with Padding(填充式 JSON),它利用 `'.$extra; } // -------------------------------------------------------------------- // AJAX-Y STUFF - still a testbed // -------------------------------------------------------------------- /** * Update * * Outputs a javascript library slideDown event * * @param string - element * @param string - One of 'slow', 'normal', 'fast', or time in milliseconds * @param string - Javascript callback function * @return string */ public function update($element = 'this', $speed = '', $callback = '') { return $this->js->_updater($element, $speed, $callback); } // -------------------------------------------------------------------- /** * Generate JSON * * Can be passed a database result or associative array and returns a JSON formatted string * * @param mixed result set or array * @param bool match array types (defaults to objects) * @return string a json formatted string */ public function generate_json($result = NULL, $match_array_type = FALSE) { // JSON data can optionally be passed to this function // either as a database result object or an array, or a user supplied array if ($result !== NULL) { if (is_object($result)) { $json_result = is_callable(array($result, 'result_array')) ? $result->result_array() : (array) $result; } elseif (is_array($result)) { $json_result = $result; } else { return $this->_prep_args($result); } } else { return 'null'; } $json = array(); $_is_assoc = TRUE; if ( ! is_array($json_result) && empty($json_result)) { show_error('Generate JSON Failed - Illegal key, value pair.'); } elseif ($match_array_type) { $_is_assoc = $this->_is_associative_array($json_result); } foreach ($json_result as $k => $v) { if ($_is_assoc) { $json[] = $this->_prep_args($k, TRUE).':'.$this->generate_json($v, $match_array_type); } else { $json[] = $this->generate_json($v, $match_array_type); } } $json = implode(',', $json); return $_is_assoc ? '{'.$json.'}' : '['.$json.']'; } // -------------------------------------------------------------------- /** * Is associative array * * Checks for an associative array * * @param array * @return bool */ protected function _is_associative_array($arr) { foreach (array_keys($arr) as $key => $val) { if ($key !== $val) { return TRUE; } } return FALSE; } // -------------------------------------------------------------------- /** * Prep Args * * Ensures a standard json value and escapes values * * @param mixed $result * @param bool $is_key = FALSE * @return string */ protected function _prep_args($result, $is_key = FALSE) { if ($result === NULL) { return 'null'; } elseif (is_bool($result)) { return ($result === TRUE) ? 'true' : 'false'; } elseif (is_string($result) OR $is_key) { return '"'.str_replace(array('\\', "\t", "\n", "\r", '"', '/'), array('\\\\', '\\t', '\\n', "\\r", '\"', '\/'), $result).'"'; } elseif (is_scalar($result)) { return $result; } } } ================================================ FILE: 04-Front-end-tutorial-master/README/Migration.php ================================================ $val) { $this->{'_'.$key} = $val; } log_message('info', 'Migrations Class Initialized'); // Are they trying to use migrations while it is disabled? if ($this->_migration_enabled !== TRUE) { show_error('Migrations has been loaded but is disabled or set up incorrectly.'); } // If not set, set it $this->_migration_path !== '' OR $this->_migration_path = APPPATH.'migrations/'; // Add trailing slash if not set $this->_migration_path = rtrim($this->_migration_path, '/').'/'; // Load migration language $this->lang->load('migration'); // They'll probably be using dbforge $this->load->dbforge(); // Make sure the migration table name was set. if (empty($this->_migration_table)) { show_error('Migrations configuration file (migration.php) must have "migration_table" set.'); } // Migration basename regex $this->_migration_regex = ($this->_migration_type === 'timestamp') ? '/^\d{14}_(\w+)$/' : '/^\d{3}_(\w+)$/'; // Make sure a valid migration numbering type was set. if ( ! in_array($this->_migration_type, array('sequential', 'timestamp'))) { show_error('An invalid migration numbering type was specified: '.$this->_migration_type); } // If the migrations table is missing, make it if ( ! $this->db->table_exists($this->_migration_table)) { $this->dbforge->add_field(array( 'version' => array('type' => 'BIGINT', 'constraint' => 20), )); $this->dbforge->create_table($this->_migration_table, TRUE); $this->db->insert($this->_migration_table, array('version' => 0)); } // Do we auto migrate to the latest migration? if ($this->_migration_auto_latest === TRUE && ! $this->latest()) { show_error($this->error_string()); } } // -------------------------------------------------------------------- /** * Migrate to a schema version * * Calls each migration step required to get to the schema version of * choice * * @param string $target_version Target schema version * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function version($target_version) { // Note: We use strings, so that timestamp versions work on 32-bit systems $current_version = $this->_get_version(); if ($this->_migration_type === 'sequential') { $target_version = sprintf('%03d', $target_version); } else { $target_version = (string) $target_version; } $migrations = $this->find_migrations(); if ($target_version > 0 && ! isset($migrations[$target_version])) { $this->_error_string = sprintf($this->lang->line('migration_not_found'), $target_version); return FALSE; } if ($target_version > $current_version) { // Moving Up $method = 'up'; } else { // Moving Down, apply in reverse order $method = 'down'; krsort($migrations); } if (empty($migrations)) { return TRUE; } $previous = FALSE; // Validate all available migrations, and run the ones within our target range foreach ($migrations as $number => $file) { // Check for sequence gaps if ($this->_migration_type === 'sequential' && $previous !== FALSE && abs($number - $previous) > 1) { $this->_error_string = sprintf($this->lang->line('migration_sequence_gap'), $number); return FALSE; } include_once($file); $class = 'Migration_'.ucfirst(strtolower($this->_get_migration_name(basename($file, '.php')))); // Validate the migration file structure if ( ! class_exists($class, FALSE)) { $this->_error_string = sprintf($this->lang->line('migration_class_doesnt_exist'), $class); return FALSE; } $previous = $number; // Run migrations that are inside the target range if ( ($method === 'up' && $number > $current_version && $number <= $target_version) OR ($method === 'down' && $number <= $current_version && $number > $target_version) ) { $instance = new $class(); if ( ! is_callable(array($instance, $method))) { $this->_error_string = sprintf($this->lang->line('migration_missing_'.$method.'_method'), $class); return FALSE; } log_message('debug', 'Migrating '.$method.' from version '.$current_version.' to version '.$number); call_user_func(array($instance, $method)); $current_version = $number; $this->_update_version($current_version); } } // This is necessary when moving down, since the the last migration applied // will be the down() method for the next migration up from the target if ($current_version <> $target_version) { $current_version = $target_version; $this->_update_version($current_version); } log_message('debug', 'Finished migrating to '.$current_version); return $current_version; } // -------------------------------------------------------------------- /** * Sets the schema to the latest migration * * @return mixed Current version string on success, FALSE on failure */ public function latest() { $migrations = $this->find_migrations(); if (empty($migrations)) { $this->_error_string = $this->lang->line('migration_none_found'); return FALSE; } $last_migration = basename(end($migrations)); // Calculate the last migration step from existing migration // filenames and proceed to the standard version migration return $this->version($this->_get_migration_number($last_migration)); } // -------------------------------------------------------------------- /** * Sets the schema to the migration version set in config * * @return mixed TRUE if no migrations are found, current version string on success, FALSE on failure */ public function current() { return $this->version($this->_migration_version); } // -------------------------------------------------------------------- /** * Error string * * @return string Error message returned as a string */ public function error_string() { return $this->_error_string; } // -------------------------------------------------------------------- /** * Retrieves list of available migration scripts * * @return array list of migration file paths sorted by version */ public function find_migrations() { $migrations = array(); // Load all *_*.php files in the migrations path foreach (glob($this->_migration_path.'*_*.php') as $file) { $name = basename($file, '.php'); // Filter out non-migration files if (preg_match($this->_migration_regex, $name)) { $number = $this->_get_migration_number($name); // There cannot be duplicate migration numbers if (isset($migrations[$number])) { $this->_error_string = sprintf($this->lang->line('migration_multiple_version'), $number); show_error($this->_error_string); } $migrations[$number] = $file; } } ksort($migrations); return $migrations; } // -------------------------------------------------------------------- /** * Extracts the migration number from a filename * * @param string $migration * @return string Numeric portion of a migration filename */ protected function _get_migration_number($migration) { return sscanf($migration, '%[0-9]+', $number) ? $number : '0'; } // -------------------------------------------------------------------- /** * Extracts the migration class name from a filename * * @param string $migration * @return string text portion of a migration filename */ protected function _get_migration_name($migration) { $parts = explode('_', $migration); array_shift($parts); return implode('_', $parts); } // -------------------------------------------------------------------- /** * Retrieves current schema version * * @return string Current migration version */ protected function _get_version() { $row = $this->db->select('version')->get($this->_migration_table)->row(); return $row ? $row->version : '0'; } // -------------------------------------------------------------------- /** * Stores the current schema version * * @param string $migration Migration reached * @return void */ protected function _update_version($migration) { $this->db->update($this->_migration_table, array( 'version' => $migration )); } // -------------------------------------------------------------------- /** * Enable the use of CI super-global * * @param string $var * @return mixed */ public function __get($var) { return get_instance()->$var; } } ================================================ FILE: 04-Front-end-tutorial-master/README/Pagination.php ================================================ '; /** * Current tag close * * @var string */ protected $cur_tag_close = ''; /** * Next tag open * * @var string */ protected $next_tag_open = ''; /** * Next tag close * * @var string */ protected $next_tag_close = ''; /** * Previous tag open * * @var string */ protected $prev_tag_open = ''; /** * Previous tag close * * @var string */ protected $prev_tag_close = ''; /** * Number tag open * * @var string */ protected $num_tag_open = ''; /** * Number tag close * * @var string */ protected $num_tag_close = ''; /** * Page query string flag * * @var bool */ protected $page_query_string = FALSE; /** * Query string segment * * @var string */ protected $query_string_segment = 'per_page'; /** * Display pages flag * * @var bool */ protected $display_pages = TRUE; /** * Attributes * * @var string */ protected $_attributes = ''; /** * Link types * * "rel" attribute * * @see CI_Pagination::_attr_rel() * @var array */ protected $_link_types = array(); /** * Reuse query string flag * * @var bool */ protected $reuse_query_string = FALSE; /** * Use global URL suffix flag * * @var bool */ protected $use_global_url_suffix = FALSE; /** * Data page attribute * * @var string */ protected $data_page_attr = 'data-ci-pagination-page'; /** * CI Singleton * * @var object */ protected $CI; // -------------------------------------------------------------------- /** * Constructor * * @param array $params Initialization parameters * @return void */ public function __construct($params = array()) { $this->CI =& get_instance(); $this->CI->load->language('pagination'); foreach (array('first_link', 'next_link', 'prev_link', 'last_link') as $key) { if (($val = $this->CI->lang->line('pagination_'.$key)) !== FALSE) { $this->$key = $val; } } $this->initialize($params); log_message('info', 'Pagination Class Initialized'); } // -------------------------------------------------------------------- /** * Initialize Preferences * * @param array $params Initialization parameters * @return CI_Pagination */ public function initialize(array $params = array()) { isset($params['attributes']) OR $params['attributes'] = array(); if (is_array($params['attributes'])) { $this->_parse_attributes($params['attributes']); unset($params['attributes']); } // Deprecated legacy support for the anchor_class option // Should be removed in CI 3.1+ if (isset($params['anchor_class'])) { empty($params['anchor_class']) OR $attributes['class'] = $params['anchor_class']; unset($params['anchor_class']); } foreach ($params as $key => $val) { if (property_exists($this, $key)) { $this->$key = $val; } } if ($this->CI->config->item('enable_query_strings') === TRUE) { $this->page_query_string = TRUE; } if ($this->use_global_url_suffix === TRUE) { $this->suffix = $this->CI->config->item('url_suffix'); } return $this; } // -------------------------------------------------------------------- /** * Generate the pagination links * * @return string */ public function create_links() { // If our item count or per-page total is zero there is no need to continue. // Note: DO NOT change the operator to === here! if ($this->total_rows == 0 OR $this->per_page == 0) { return ''; } // Calculate the total number of pages $num_pages = (int) ceil($this->total_rows / $this->per_page); // Is there only one page? Hm... nothing more to do here then. if ($num_pages === 1) { return ''; } // Check the user defined number of links. $this->num_links = (int) $this->num_links; if ($this->num_links < 0) { show_error('Your number of links must be a non-negative number.'); } // Keep any existing query string items. // Note: Has nothing to do with any other query string option. if ($this->reuse_query_string === TRUE) { $get = $this->CI->input->get(); // Unset the controll, method, old-school routing options unset($get['c'], $get['m'], $get[$this->query_string_segment]); } else { $get = array(); } // Put together our base and first URLs. // Note: DO NOT append to the properties as that would break successive calls $base_url = trim($this->base_url); $first_url = $this->first_url; $query_string = ''; $query_string_sep = (strpos($base_url, '?') === FALSE) ? '?' : '&'; // Are we using query strings? if ($this->page_query_string === TRUE) { // If a custom first_url hasn't been specified, we'll create one from // the base_url, but without the page item. if ($first_url === '') { $first_url = $base_url; // If we saved any GET items earlier, make sure they're appended. if ( ! empty($get)) { $first_url .= $query_string_sep.http_build_query($get); } } // Add the page segment to the end of the query string, where the // page number will be appended. $base_url .= $query_string_sep.http_build_query(array_merge($get, array($this->query_string_segment => ''))); } else { // Standard segment mode. // Generate our saved query string to append later after the page number. if ( ! empty($get)) { $query_string = $query_string_sep.http_build_query($get); $this->suffix .= $query_string; } // Does the base_url have the query string in it? // If we're supposed to save it, remove it so we can append it later. if ($this->reuse_query_string === TRUE && ($base_query_pos = strpos($base_url, '?')) !== FALSE) { $base_url = substr($base_url, 0, $base_query_pos); } if ($first_url === '') { $first_url = $base_url.$query_string; } $base_url = rtrim($base_url, '/').'/'; } // Determine the current page number. $base_page = ($this->use_page_numbers) ? 1 : 0; // Are we using query strings? if ($this->page_query_string === TRUE) { $this->cur_page = $this->CI->input->get($this->query_string_segment); } else { // Default to the last segment number if one hasn't been defined. if ($this->uri_segment === 0) { $this->uri_segment = count($this->CI->uri->segment_array()); } $this->cur_page = $this->CI->uri->segment($this->uri_segment); // Remove any specified prefix/suffix from the segment. if ($this->prefix !== '' OR $this->suffix !== '') { $this->cur_page = str_replace(array($this->prefix, $this->suffix), '', $this->cur_page); } } // If something isn't quite right, back to the default base page. if ( ! ctype_digit($this->cur_page) OR ($this->use_page_numbers && (int) $this->cur_page === 0)) { $this->cur_page = $base_page; } else { // Make sure we're using integers for comparisons later. $this->cur_page = (int) $this->cur_page; } // Is the page number beyond the result range? // If so, we show the last page. if ($this->use_page_numbers) { if ($this->cur_page > $num_pages) { $this->cur_page = $num_pages; } } elseif ($this->cur_page > $this->total_rows) { $this->cur_page = ($num_pages - 1) * $this->per_page; } $uri_page_number = $this->cur_page; // If we're using offset instead of page numbers, convert it // to a page number, so we can generate the surrounding number links. if ( ! $this->use_page_numbers) { $this->cur_page = (int) floor(($this->cur_page/$this->per_page) + 1); } // Calculate the start and end numbers. These determine // which number to start and end the digit links with. $start = (($this->cur_page - $this->num_links) > 0) ? $this->cur_page - ($this->num_links - 1) : 1; $end = (($this->cur_page + $this->num_links) < $num_pages) ? $this->cur_page + $this->num_links : $num_pages; // And here we go... $output = ''; // Render the "First" link. if ($this->first_link !== FALSE && $this->cur_page > ($this->num_links + 1 + ! $this->num_links)) { // Take the general parameters, and squeeze this pagination-page attr in for JS frameworks. $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, 1); $output .= $this->first_tag_open.'
    _attr_rel('start').'>' .$this->first_link.''.$this->first_tag_close; } // Render the "Previous" link. if ($this->prev_link !== FALSE && $this->cur_page !== 1) { $i = ($this->use_page_numbers) ? $uri_page_number - 1 : $uri_page_number - $this->per_page; $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, ($this->cur_page - 1)); if ($i === $base_page) { // First page $output .= $this->prev_tag_open.'_attr_rel('prev').'>' .$this->prev_link.''.$this->prev_tag_close; } else { $append = $this->prefix.$i.$this->suffix; $output .= $this->prev_tag_open.'_attr_rel('prev').'>' .$this->prev_link.''.$this->prev_tag_close; } } // Render the pages if ($this->display_pages !== FALSE) { // Write the digit links for ($loop = $start - 1; $loop <= $end; $loop++) { $i = ($this->use_page_numbers) ? $loop : ($loop * $this->per_page) - $this->per_page; $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $loop); if ($i >= $base_page) { if ($this->cur_page === $loop) { // Current page $output .= $this->cur_tag_open.$loop.$this->cur_tag_close; } elseif ($i === $base_page) { // First page $output .= $this->num_tag_open.'_attr_rel('start').'>' .$loop.''.$this->num_tag_close; } else { $append = $this->prefix.$i.$this->suffix; $output .= $this->num_tag_open.'' .$loop.''.$this->num_tag_close; } } } } // Render the "next" link if ($this->next_link !== FALSE && $this->cur_page < $num_pages) { $i = ($this->use_page_numbers) ? $this->cur_page + 1 : $this->cur_page * $this->per_page; $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $this->cur_page + 1); $output .= $this->next_tag_open.'_attr_rel('next').'>'.$this->next_link.''.$this->next_tag_close; } // Render the "Last" link if ($this->last_link !== FALSE && ($this->cur_page + $this->num_links + ! $this->num_links) < $num_pages) { $i = ($this->use_page_numbers) ? $num_pages : ($num_pages * $this->per_page) - $this->per_page; $attributes = sprintf('%s %s="%d"', $this->_attributes, $this->data_page_attr, $num_pages); $output .= $this->last_tag_open.'' .$this->last_link.''.$this->last_tag_close; } // Kill double slashes. Note: Sometimes we can end up with a double slash // in the penultimate link so we'll kill all double slashes. $output = preg_replace('#([^:"])//+#', '\\1/', $output); // Add the wrapper HTML if exists return $this->full_tag_open.$output.$this->full_tag_close; } // -------------------------------------------------------------------- /** * Parse attributes * * @param array $attributes * @return void */ protected function _parse_attributes($attributes) { isset($attributes['rel']) OR $attributes['rel'] = TRUE; $this->_link_types = ($attributes['rel']) ? array('start' => 'start', 'prev' => 'prev', 'next' => 'next') : array(); unset($attributes['rel']); $this->_attributes = ''; foreach ($attributes as $key => $value) { $this->_attributes .= ' '.$key.'="'.$value.'"'; } } // -------------------------------------------------------------------- /** * Add "rel" attribute * * @link http://www.w3.org/TR/html5/links.html#linkTypes * @param string $type * @return string */ protected function _attr_rel($type) { if (isset($this->_link_types[$type])) { unset($this->_link_types[$type]); return ' rel="'.$type.'"'; } return ''; } } ================================================ FILE: 04-Front-end-tutorial-master/README/Parser.php ================================================ CI =& get_instance(); log_message('info', 'Parser Class Initialized'); } // -------------------------------------------------------------------- /** * Parse a template * * Parses pseudo-variables contained in the specified template view, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ public function parse($template, $data, $return = FALSE) { $template = $this->CI->load->view($template, $data, TRUE); return $this->_parse($template, $data, $return); } // -------------------------------------------------------------------- /** * Parse a String * * Parses pseudo-variables contained in the specified string, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ public function parse_string($template, $data, $return = FALSE) { return $this->_parse($template, $data, $return); } // -------------------------------------------------------------------- /** * Parse a template * * Parses pseudo-variables contained in the specified template, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ protected function _parse($template, $data, $return = FALSE) { if ($template === '') { return FALSE; } $replace = array(); foreach ($data as $key => $val) { $replace = array_merge( $replace, is_array($val) ? $this->_parse_pair($key, $val, $template) : $this->_parse_single($key, (string) $val, $template) ); } unset($data); $template = strtr($template, $replace); if ($return === FALSE) { $this->CI->output->append_output($template); } return $template; } // -------------------------------------------------------------------- /** * Set the left/right variable delimiters * * @param string * @param string * @return void */ public function set_delimiters($l = '{', $r = '}') { $this->l_delim = $l; $this->r_delim = $r; } // -------------------------------------------------------------------- /** * Parse a single key/value * * @param string * @param string * @param string * @return string */ protected function _parse_single($key, $val, $string) { return array($this->l_delim.$key.$this->r_delim => (string) $val); } // -------------------------------------------------------------------- /** * Parse a tag pair * * Parses tag pairs: {some_tag} string... {/some_tag} * * @param string * @param array * @param string * @return string */ protected function _parse_pair($variable, $data, $string) { $replace = array(); preg_match_all( '#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s', $string, $matches, PREG_SET_ORDER ); foreach ($matches as $match) { $str = ''; foreach ($data as $row) { $temp = array(); foreach ($row as $key => $val) { if (is_array($val)) { $pair = $this->_parse_pair($key, $val, $match[1]); if ( ! empty($pair)) { $temp = array_merge($temp, $pair); } continue; } $temp[$this->l_delim.$key.$this->r_delim] = $val; } $str .= strtr($match[1], $temp); } $replace[$match[0]] = $str; } return $replace; } } ================================================ FILE: 04-Front-end-tutorial-master/README/Profiler.php ================================================ CI =& get_instance(); $this->CI->load->language('profiler'); // default all sections to display foreach ($this->_available_sections as $section) { if ( ! isset($config[$section])) { $this->_compile_{$section} = TRUE; } } $this->set_sections($config); log_message('info', 'Profiler Class Initialized'); } // -------------------------------------------------------------------- /** * Set Sections * * Sets the private _compile_* properties to enable/disable Profiler sections * * @param mixed $config * @return void */ public function set_sections($config) { if (isset($config['query_toggle_count'])) { $this->_query_toggle_count = (int) $config['query_toggle_count']; unset($config['query_toggle_count']); } foreach ($config as $method => $enable) { if (in_array($method, $this->_available_sections)) { $this->_compile_{$method} = ($enable !== FALSE); } } } // -------------------------------------------------------------------- /** * Auto Profiler * * This function cycles through the entire array of mark points and * matches any two points that are named identically (ending in "_start" * and "_end" respectively). It then compiles the execution times for * all points and returns it as an array * * @return array */ protected function _compile_benchmarks() { $profile = array(); foreach ($this->CI->benchmark->marker as $key => $val) { // We match the "end" marker so that the list ends // up in the order that it was defined if (preg_match('/(.+?)_end$/i', $key, $match) && isset($this->CI->benchmark->marker[$match[1].'_end'], $this->CI->benchmark->marker[$match[1].'_start'])) { $profile[$match[1]] = $this->CI->benchmark->elapsed_time($match[1].'_start', $key); } } // Build a table containing the profile data. // Note: At some point we should turn this into a template that can // be modified. We also might want to make this data available to be logged $output = "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_benchmarks')."  " ."\n\n\n\n"; foreach ($profile as $key => $val) { $key = ucwords(str_replace(array('_', '-'), ' ', $key)); $output .= '\n"; } return $output."
    ' .$key.'  ' .$val."
    \n
    "; } // -------------------------------------------------------------------- /** * Compile Queries * * @return string */ protected function _compile_queries() { $dbs = array(); // Let's determine which databases are currently connected to foreach (get_object_vars($this->CI) as $name => $cobject) { if (is_object($cobject)) { if ($cobject instanceof CI_DB) { $dbs[get_class($this->CI).':$'.$name] = $cobject; } elseif ($cobject instanceof CI_Model) { foreach (get_object_vars($cobject) as $mname => $mobject) { if ($mobject instanceof CI_DB) { $dbs[get_class($cobject).':$'.$mname] = $mobject; } } } } } if (count($dbs) === 0) { return "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_queries').'  ' ."\n\n\n\n" .'\n
    ' .$this->CI->lang->line('profiler_no_db') ."
    \n
    "; } // Load the text helper so we can highlight the SQL $this->CI->load->helper('text'); // Key words we want bolded $highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'GROUP BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR ', 'HAVING', 'OFFSET', 'NOT IN', 'IN', 'LIKE', 'NOT LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')'); $output = "\n\n"; $count = 0; foreach ($dbs as $name => $db) { $hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : ''; $total_time = number_format(array_sum($db->query_times), 4).' '.$this->CI->lang->line('profiler_seconds'); $show_hide_js = '('.$this->CI->lang->line('profiler_section_hide').')'; if ($hide_queries !== '') { $show_hide_js = '('.$this->CI->lang->line('profiler_section_show').')'; } $output .= '
    ' ."\n" .'  '.$this->CI->lang->line('profiler_database') .':  '.$db->database.' ('.$name.')   '.$this->CI->lang->line('profiler_queries') .': '.count($db->queries).' ('.$total_time.')  '.$show_hide_js."\n\n\n" .'\n"; if (count($db->queries) === 0) { $output .= '\n"; } else { foreach ($db->queries as $key => $val) { $time = number_format($db->query_times[$key], 4); $val = highlight_code($val); foreach ($highlight as $bold) { $val = str_replace($bold, ''.$bold.'', $val); } $output .= '\n"; } } $output .= "
    ' .$this->CI->lang->line('profiler_no_queries')."
    ' .$time.'  ' .$val."
    \n
    "; $count++; } return $output; } // -------------------------------------------------------------------- /** * Compile $_GET Data * * @return string */ protected function _compile_get() { $output = "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_get_data')."  \n"; if (count($_GET) === 0) { $output .= '
    '.$this->CI->lang->line('profiler_no_get').'
    '; } else { $output .= "\n\n\n"; foreach ($_GET as $key => $val) { is_int($key) OR $key = "'".$key."'"; $output .= '\n"; } $output .= "
    $_GET[' .$key.']   ' .((is_array($val) OR is_object($val)) ? '
    '.htmlspecialchars(stripslashes(print_r($val, TRUE))).'
    ' : htmlspecialchars(stripslashes($val))) ."
    \n"; } return $output.'
    '; } // -------------------------------------------------------------------- /** * Compile $_POST Data * * @return string */ protected function _compile_post() { $output = "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_post_data')."  \n"; if (count($_POST) === 0 && count($_FILES) === 0) { $output .= '
    '.$this->CI->lang->line('profiler_no_post').'
    '; } else { $output .= "\n\n\n"; foreach ($_POST as $key => $val) { is_int($key) OR $key = "'".$key."'"; $output .= '\n"; } foreach ($_FILES as $key => $val) { is_int($key) OR $key = "'".$key."'"; $output .= '\n"; } $output .= "
    $_POST[' .$key.']   '; if (is_array($val) OR is_object($val)) { $output .= '
    '.htmlspecialchars(stripslashes(print_r($val, TRUE))).'
    '; } else { $output .= htmlspecialchars(stripslashes($val)); } $output .= "
    $_FILES[' .$key.']   '; if (is_array($val) OR is_object($val)) { $output .= '
    '.htmlspecialchars(stripslashes(print_r($val, TRUE))).'
    '; } $output .= "
    \n"; } return $output.'
    '; } // -------------------------------------------------------------------- /** * Show query string * * @return string */ protected function _compile_uri_string() { return "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_uri_string')."  \n" .'
    ' .($this->CI->uri->uri_string === '' ? $this->CI->lang->line('profiler_no_uri') : $this->CI->uri->uri_string) .'
    '; } // -------------------------------------------------------------------- /** * Show the controller and function that were called * * @return string */ protected function _compile_controller_info() { return "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_controller_info')."  \n" .'
    '.$this->CI->router->class.'/'.$this->CI->router->method .'
    '; } // -------------------------------------------------------------------- /** * Compile memory usage * * Display total used memory * * @return string */ protected function _compile_memory_usage() { return "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_memory_usage')."  \n" .'
    ' .(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory')) .'
    '; } // -------------------------------------------------------------------- /** * Compile header information * * Lists HTTP headers * * @return string */ protected function _compile_http_headers() { $output = "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_headers') .'  ('.$this->CI->lang->line('profiler_section_show').")\n\n\n" .''."\n"; foreach (array('HTTP_ACCEPT', 'HTTP_USER_AGENT', 'HTTP_CONNECTION', 'SERVER_PORT', 'SERVER_NAME', 'REMOTE_ADDR', 'SERVER_SOFTWARE', 'HTTP_ACCEPT_LANGUAGE', 'SCRIPT_NAME', 'REQUEST_METHOD',' HTTP_HOST', 'REMOTE_HOST', 'CONTENT_TYPE', 'SERVER_PROTOCOL', 'QUERY_STRING', 'HTTP_ACCEPT_ENCODING', 'HTTP_X_FORWARDED_FOR', 'HTTP_DNT') as $header) { $val = isset($_SERVER[$header]) ? $_SERVER[$header] : ''; $output .= '\n"; } return $output."\n
    "; } // -------------------------------------------------------------------- /** * Compile config information * * Lists developer config variables * * @return string */ protected function _compile_config() { $output = "\n\n" .'
    ' ."\n" .'  '.$this->CI->lang->line('profiler_config').'  ('.$this->CI->lang->line('profiler_section_show').")\n\n\n" .''."\n"; foreach ($this->CI->config->config as $config => $val) { if (is_array($val) OR is_object($val)) { $val = print_r($val, TRUE); } $output .= '\n"; } return $output."\n
    "; } // -------------------------------------------------------------------- /** * Compile session userdata * * @return string */ protected function _compile_session_data() { if ( ! isset($this->CI->session)) { return; } $output = '
    ' .'  '.$this->CI->lang->line('profiler_session_data').'  ('.$this->CI->lang->line('profiler_section_show').')' .''; foreach ($this->CI->session->userdata() as $key => $val) { if (is_array($val) OR is_object($val)) { $val = print_r($val, TRUE); } $output .= '\n"; } return $output."\n
    "; } // -------------------------------------------------------------------- /** * Run the Profiler * * @return string */ public function run() { $output = '
    '; $fields_displayed = 0; foreach ($this->_available_sections as $section) { if ($this->_compile_{$section} !== FALSE) { $func = '_compile_'.$section; $output .= $this->{$func}(); $fields_displayed++; } } if ($fields_displayed === 0) { $output .= '

    ' .$this->CI->lang->line('profiler_no_profiles').'

    '; } return $output.'
    '; } } ================================================ FILE: 04-Front-end-tutorial-master/README/README.php ================================================ li ) 6. descendant selectors (li a) 7. wildcard selector (*) 8. attribute selector (a [rel = "external"]) 9. pseudo-class selectors (a: hover, li: nth-child) Inheritable style: font-size font-family color, text-indent; Non Inherited Styles: border padding margin width height; Priority under the principle of proximity, with weights whichever the case recently defined style; Loading last loaded positioning styles to prevail; Priority: ! Important> ID> class> tag Important priority higher than inline, inline but higher than the id Added CSS3 pseudo-class examples: p: first-of-type select each

    element belongs to the first element of its parent

    element. p: last-of-type select each

    element belongs to its parent last

    element. p: only-of-type select each

    element belongs to its parent element only

    elements. p: only-child selects each

    element belongs to its parent element only child element. p: nth-child (2) Select each

    element belongs to the parent element of the second sub-element. : Enabled: disabled control form control disabled. : Checked radio button or checkbox is selected. position values, relative and absolute are positioned with respect to whom? absolute generate absolute element positioned relative to the recent level is not static positioned parent element to be positioned. fixed (old IE does not support) to generate absolute element positioned relative to the browser window positioning. relative generating element relative positioning, relative to its position in the general stream positioning. static default. No positioning element occurs in the normal stream CSS3 What's new? CSS3 rounded corners (border-radius), shadow (box-shadow), add special effects to the text (text-shadow,), a linear gradient (gradient), rotation (transform) transform: Rotate (9deg) Scale (0.85,0.90) translate (0px, -30px) skew (-9deg, 0deg); // rotation, scaling, positioning, tilt adds more CSS selectors more background rgba unique introduced in CSS3 pseudo-elements :: selection. Media queries , multi-column layout border-image XML and JSON difference? (1) The data volume terms. XML JSON relative terms, the volume of data is small, the speed of delivery faster. (2) aspects of data exchange. JSON and JavaScript interaction more convenient and easier to parse processing, better data interaction. (3) The data described aspects. JSON description of data than XML poor. (4) The transmission speed. JSON's speed is much faster than XML. BFC norms of understanding? BFC, block-level formatting context, a BFC created a new box is an independent layout, style, child elements inside the box will not affect the outside elements. In the same BFC two adjoining block-level box in the vertical direction (the direction of a relationship and layout) of margin will collapse from happening. (A concept W3C CSS 2.1 specification, which determines how the elements in the layout of its contents, and its relationship with other elements and interactions.) Under explain CSS sprites, and how you want to use it in a page or site. CSS Sprites in fact, the number of pages in the background image into an image file, and then use the CSS "background-image", "background- repeat", "background-position" a combination of background positioning, background-position can be used Digital can accurately locate the position of the background image. This can reduce the cost of many pictures request because the request takes a relatively long; although requests can be complicated, but there are limits, are six general browser. For the future, there is no need to do so, and because of `http2`. html part Talk about your understanding of the semantic? 1, removed or lost when allowing page style showing clear structure 2, is conducive to SEO: search engines and to establish good communication helps crawlers to crawl more useful information: reptiles rely on labels to determine the context and the individual right keyword weight; 3, easy to resolve other devices (such as a screen reader, blind readers, mobile devices) in meaningful ways to render web pages; 4, easy to team development and maintenance, more readable semantic, is under Important Trends step right of the page, follow the W3C standards team follow this standard, the difference can be reduced. Doctype role? Strict mode and mixed mode how to distinguish? They mean? (1), declaration in the document in the front, in the tag before. Tells the browser to render the document in which mode. (2) strict mode layout and JS mode of operation is based on the highest standards of the browser supports running. (3), in promiscuous mode, page backward compatible with liberal display. Simulate the behavior of older browsers do not work to prevent the site. (4), DOCTYPE does not exist or incorrectly formatted document will lead to promiscuous mode rendering. Do you know how many document types Doctype? The label can be declared DTD three types, respectively strict version, interim version and an HTML-based documentation framework. HTML 4.01 specifies three document types: Strict, Transitional, and Frameset. XHTML 1.0 specifies three XML document types: Strict, Transitional, and Frameset. Standards (standard) mode (that is, in strict rendering mode) complies with the latest standards for rendering Web pages, while Quirks (inclusive) mode (that is, loose rendering mode or compatibility mode) is used to render conventional web browser designed. HTML and XHTML-- What is the difference between the two Difference: 1. All tags must have a corresponding end tag element and attribute names 2. All tags must be lowercase 3. All the XML tags must be properly nested 4. All attributes must be quoted. " "enclosed 5. Put all the ` * top and bottom margin overlap problem ie and ff are present, adjacent both margin-left and margin-right does not coincide div, but margin-top and margin-bottom but it will coincide occur. Solution, to develop good coding habits, while using margin-top or while using margin-bottom. * Ie6 png image format support is not good for (quote a script processing) Floating and explain how it works? Clear float tips Floating elements from the document flow, does not occupy space. Floats across the border to stay its borders contain or floating elements. 1. Clear float with an empty label. This method is to add an empty label definition css clear in all floating behind the label: both the disadvantages is to increase the meaningless labels. 2. Use the overflow. To a parent tag contains floating elements add css property overflow: auto; zoom: 1; zoom: 1 for compatibility with IE6. 3. Use after pseudo remove floating objects. This method applies only to non-IE browsers. Specific wording may refer to the following examples. Use should pay attention to the following points. First, the method must be set to clear the height of the floating element's pseudo-objects: 0, otherwise the element will be much higher than the actual number of pixels; Problems caused by floating elements and solutions? Problems caused by the floating elements: (1) height of the parent element can not be softened, peer influence and elements of the parent element (2) and non-floating elements floating element siblings (inline elements) will follow thereafter (3) if not the first element of float, then that element is also required before the float, otherwise it will affect the structure of the page displayed Workaround: use CSS in Clear: Both ; attribute to clear floating elements 2,3 resolve the problem, the problem 1, add the following styles to parent element Add clearfix style: .clearfix: after {content:; display ".": Block; height: 0; Clear: Both; visibility: hidden;} .clearfix {display: inline-Block;} / * for IE / Mac * / Clear float several ways: 1, additional labeling,

    (disadvantage: But this approach would cause additional HTML tag structure looks simple enough) 2, use after pseudo-class #parent: after { content:; "." height: 0; visibility: hidden; display: Block; Clear: Both; } 3, floating external element 4, is set to `hidden`` overflow` or auto IE 8 the following versions of the browser box model What is the difference The following box model IE8 browser's width and height defined elements does not include padding and border DOM manipulation - how to add, remove, move, copy, create, and find nodes. (1) Create a new node createDocumentFragment () // Create a DOM fragment createElement () // create a specific element createTextNode () // Create a text node (2) to add, remove, replace, insert appendChild () removeChild ( ) replaceChild () insertBefore () // before the existing child node into a new child node (3) Find getElementsByTagName () // by tag name getElementsByName () // by the value of the Name property of the element (IE fault tolerance Strong, will be an array, including name id equal value) getElementById () // by elemental Id, uniqueness What's new html5, removing those elements? How to deal with a new label HTML5 browser compatibility problems? How to distinguish between HTML and HTML5? * HTML5 now is not a subset of SGML, mainly to increase with respect to the image, location, storage, multi-tasking and other features. * Drag release (Drag and drop) API semantic content better label (header, nav, footer, aside, article, section) audio, video, API (audio, video) canvas (Canvas) API geography (Geolocation) API Local localStorage offline storage for long term storage of data, the browser is closed after data is not lost; sessionStorage of data after the browser is closed automatically delete form controls, calendar, date, time, email, url, search WebWorker new technologies, websocket, Geolocation * Remove The elements of the element of pure performance: basefont, big, center, font, s, strike, tt, u; adversely affect the availability of the elements of: frame, frameset, noframes; support HTML5 new label: * IE8 / IE7 / IE6 support through document.createElement method of producing labels, can use this feature to allow these browsers support HTML5 new label, after the browser to support the new label, you also need to add tags default style: * Of course, the best way is the direct use of mature framework, use the most is html5shim framework <-! [if lt IE 9]> ``` Note that this package is not in CommonJS format, so doing `require('angular')` will return `undefined`. If you're using [Browserify](https://github.com/substack/node-browserify), you can use [exposify](https://github.com/thlorenz/exposify) to have `require('angular')` return the `angular` global. ### bower ```shell bower install angular ``` Then add a ` ``` ## Documentation Documentation is available on the [AngularJS docs site](http://docs.angularjs.org/). ## License The MIT License Copyright (c) 2010-2012 Google, Inc. http://angularjs.org 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: 05-fks-master/fks_chart/bower_components/angular/angular-csp.css ================================================ /* Include this file in your html if you are using the CSP mode. */ @charset "UTF-8"; [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak, .ng-hide { display: none !important; } ng\:form { display: block; } .ng-animate-block-transitions { transition:0s all!important; -webkit-transition:0s all!important; } /* show the element during a show/hide animation when the * animation is ongoing, but the .ng-hide class is active */ .ng-hide-add-active, .ng-hide-remove { display: block!important; } ================================================ FILE: 05-fks-master/fks_chart/bower_components/angular/angular.js ================================================ /** * @license AngularJS v1.2.28 * (c) 2010-2014 Google, Inc. http://angularjs.org * License: MIT */ (function(window, document, undefined) {'use strict'; /** * @description * * This object provides a utility for producing rich Error messages within * Angular. It can be called as follows: * * var exampleMinErr = minErr('example'); * throw exampleMinErr('one', 'This {0} is {1}', foo, bar); * * The above creates an instance of minErr in the example namespace. The * resulting error will have a namespaced error code of example.one. The * resulting error will replace {0} with the value of foo, and {1} with the * value of bar. The object is not restricted in the number of arguments it can * take. * * If fewer arguments are specified than necessary for interpolation, the extra * interpolation markers will be preserved in the final string. * * Since data will be parsed statically during a build step, some restrictions * are applied with respect to how minErr instances are created and called. * Instances should have names of the form namespaceMinErr for a minErr created * using minErr('namespace') . Error codes, namespaces and template strings * should all be static strings, not variables or general expressions. * * @param {string} module The namespace to use for the new minErr instance. * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance */ function minErr(module) { return function () { var code = arguments[0], prefix = '[' + (module ? module + ':' : '') + code + '] ', template = arguments[1], templateArgs = arguments, stringify = function (obj) { if (typeof obj === 'function') { return obj.toString().replace(/ \{[\s\S]*$/, ''); } else if (typeof obj === 'undefined') { return 'undefined'; } else if (typeof obj !== 'string') { return JSON.stringify(obj); } return obj; }, message, i; message = prefix + template.replace(/\{\d+\}/g, function (match) { var index = +match.slice(1, -1), arg; if (index + 2 < templateArgs.length) { arg = templateArgs[index + 2]; if (typeof arg === 'function') { return arg.toString().replace(/ ?\{[\s\S]*$/, ''); } else if (typeof arg === 'undefined') { return 'undefined'; } else if (typeof arg !== 'string') { return toJson(arg); } return arg; } return match; }); message = message + '\nhttp://errors.angularjs.org/1.2.28/' + (module ? module + '/' : '') + code; for (i = 2; i < arguments.length; i++) { message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' + encodeURIComponent(stringify(arguments[i])); } return new Error(message); }; } /* We need to tell jshint what variables are being exported */ /* global angular: true, msie: true, jqLite: true, jQuery: true, slice: true, push: true, toString: true, ngMinErr: true, angularModule: true, nodeName_: true, uid: true, VALIDITY_STATE_PROPERTY: true, lowercase: true, uppercase: true, manualLowercase: true, manualUppercase: true, nodeName_: true, isArrayLike: true, forEach: true, sortedKeys: true, forEachSorted: true, reverseParams: true, nextUid: true, setHashKey: true, extend: true, int: true, inherit: true, noop: true, identity: true, valueFn: true, isUndefined: true, isDefined: true, isObject: true, isString: true, isNumber: true, isDate: true, isArray: true, isFunction: true, isRegExp: true, isWindow: true, isScope: true, isFile: true, isBlob: true, isBoolean: true, isPromiseLike: true, trim: true, isElement: true, makeMap: true, map: true, size: true, includes: true, indexOf: true, arrayRemove: true, isLeafNode: true, copy: true, shallowCopy: true, equals: true, csp: true, concat: true, sliceArgs: true, bind: true, toJsonReplacer: true, toJson: true, fromJson: true, toBoolean: true, startingTag: true, tryDecodeURIComponent: true, parseKeyValue: true, toKeyValue: true, encodeUriSegment: true, encodeUriQuery: true, angularInit: true, bootstrap: true, snake_case: true, bindJQuery: true, assertArg: true, assertArgFn: true, assertNotHasOwnProperty: true, getter: true, getBlockElements: true, hasOwnProperty: true, */ //////////////////////////////////// /** * @ngdoc module * @name ng * @module ng * @description * * # ng (core module) * The ng module is loaded by default when an AngularJS application is started. The module itself * contains the essential components for an AngularJS application to function. The table below * lists a high level breakdown of each of the services/factories, filters, directives and testing * components available within this core module. * *
    */ // The name of a form control's ValidityState property. // This is used so that it's possible for internal tests to create mock ValidityStates. var VALIDITY_STATE_PROPERTY = 'validity'; /** * @ngdoc function * @name angular.lowercase * @module ng * @kind function * * @description Converts the specified string to lowercase. * @param {string} string String to be converted to lowercase. * @returns {string} Lowercased string. */ var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; var hasOwnProperty = Object.prototype.hasOwnProperty; /** * @ngdoc function * @name angular.uppercase * @module ng * @kind function * * @description Converts the specified string to uppercase. * @param {string} string String to be converted to uppercase. * @returns {string} Uppercased string. */ var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; var manualLowercase = function(s) { /* jshint bitwise: false */ return isString(s) ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);}) : s; }; var manualUppercase = function(s) { /* jshint bitwise: false */ return isString(s) ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);}) : s; }; // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods // with correct but slower alternatives. if ('i' !== 'I'.toLowerCase()) { lowercase = manualLowercase; uppercase = manualUppercase; } var msie, // holds major version number for IE, or NaN if UA is not IE. jqLite, // delay binding since jQuery could be loaded after us. jQuery, // delay binding slice = [].slice, push = [].push, toString = Object.prototype.toString, ngMinErr = minErr('ng'), /** @name angular */ angular = window.angular || (window.angular = {}), angularModule, nodeName_, uid = ['0', '0', '0']; /** * IE 11 changed the format of the UserAgent string. * See http://msdn.microsoft.com/en-us/library/ms537503.aspx */ msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); if (isNaN(msie)) { msie = int((/trident\/.*; rv:(\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]); } /** * @private * @param {*} obj * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments, * String ...) */ function isArrayLike(obj) { if (obj == null || isWindow(obj)) { return false; } var length = obj.length; if (obj.nodeType === 1 && length) { return true; } return isString(obj) || isArray(obj) || length === 0 || typeof length === 'number' && length > 0 && (length - 1) in obj; } /** * @ngdoc function * @name angular.forEach * @module ng * @kind function * * @description * Invokes the `iterator` function once for each item in `obj` collection, which can be either an * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` * is the value of an object property or an array element and `key` is the object property key or * array element index. Specifying a `context` for the function is optional. * * It is worth noting that `.forEach` does not iterate over inherited properties because it filters * using the `hasOwnProperty` method. * ```js var values = {name: 'misko', gender: 'male'}; var log = []; angular.forEach(values, function(value, key) { this.push(key + ': ' + value); }, log); expect(log).toEqual(['name: misko', 'gender: male']); ``` * * @param {Object|Array} obj Object to iterate over. * @param {Function} iterator Iterator function. * @param {Object=} context Object to become context (`this`) for the iterator function. * @returns {Object|Array} Reference to `obj`. */ function forEach(obj, iterator, context) { var key; if (obj) { if (isFunction(obj)) { for (key in obj) { // Need to check if hasOwnProperty exists, // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) { iterator.call(context, obj[key], key); } } } else if (isArray(obj) || isArrayLike(obj)) { for (key = 0; key < obj.length; key++) { iterator.call(context, obj[key], key); } } else if (obj.forEach && obj.forEach !== forEach) { obj.forEach(iterator, context); } else { for (key in obj) { if (obj.hasOwnProperty(key)) { iterator.call(context, obj[key], key); } } } } return obj; } function sortedKeys(obj) { var keys = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { keys.push(key); } } return keys.sort(); } function forEachSorted(obj, iterator, context) { var keys = sortedKeys(obj); for ( var i = 0; i < keys.length; i++) { iterator.call(context, obj[keys[i]], keys[i]); } return keys; } /** * when using forEach the params are value, key, but it is often useful to have key, value. * @param {function(string, *)} iteratorFn * @returns {function(*, string)} */ function reverseParams(iteratorFn) { return function(value, key) { iteratorFn(key, value); }; } /** * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric * characters such as '012ABC'. The reason why we are not using simply a number counter is that * the number string gets longer over time, and it can also overflow, where as the nextId * will grow much slower, it is a string, and it will never overflow. * * @returns {string} an unique alpha-numeric string */ function nextUid() { var index = uid.length; var digit; while(index) { index--; digit = uid[index].charCodeAt(0); if (digit == 57 /*'9'*/) { uid[index] = 'A'; return uid.join(''); } if (digit == 90 /*'Z'*/) { uid[index] = '0'; } else { uid[index] = String.fromCharCode(digit + 1); return uid.join(''); } } uid.unshift('0'); return uid.join(''); } /** * Set or clear the hashkey for an object. * @param obj object * @param h the hashkey (!truthy to delete the hashkey) */ function setHashKey(obj, h) { if (h) { obj.$$hashKey = h; } else { delete obj.$$hashKey; } } /** * @ngdoc function * @name angular.extend * @module ng * @kind function * * @description * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s) * to `dst`. You can specify multiple `src` objects. * * @param {Object} dst Destination object. * @param {...Object} src Source object(s). * @returns {Object} Reference to `dst`. */ function extend(dst) { var h = dst.$$hashKey; forEach(arguments, function(obj) { if (obj !== dst) { forEach(obj, function(value, key) { dst[key] = value; }); } }); setHashKey(dst,h); return dst; } function int(str) { return parseInt(str, 10); } function inherit(parent, extra) { return extend(new (extend(function() {}, {prototype:parent}))(), extra); } /** * @ngdoc function * @name angular.noop * @module ng * @kind function * * @description * A function that performs no operations. This function can be useful when writing code in the * functional style. ```js function foo(callback) { var result = calculateResult(); (callback || angular.noop)(result); } ``` */ function noop() {} noop.$inject = []; /** * @ngdoc function * @name angular.identity * @module ng * @kind function * * @description * A function that returns its first argument. This function is useful when writing code in the * functional style. * ```js function transformer(transformationFn, value) { return (transformationFn || angular.identity)(value); }; ``` */ function identity($) {return $;} identity.$inject = []; function valueFn(value) {return function() {return value;};} /** * @ngdoc function * @name angular.isUndefined * @module ng * @kind function * * @description * Determines if a reference is undefined. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is undefined. */ function isUndefined(value){return typeof value === 'undefined';} /** * @ngdoc function * @name angular.isDefined * @module ng * @kind function * * @description * Determines if a reference is defined. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is defined. */ function isDefined(value){return typeof value !== 'undefined';} /** * @ngdoc function * @name angular.isObject * @module ng * @kind function * * @description * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not * considered to be objects. Note that JavaScript arrays are objects. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is an `Object` but not `null`. */ function isObject(value){return value != null && typeof value === 'object';} /** * @ngdoc function * @name angular.isString * @module ng * @kind function * * @description * Determines if a reference is a `String`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `String`. */ function isString(value){return typeof value === 'string';} /** * @ngdoc function * @name angular.isNumber * @module ng * @kind function * * @description * Determines if a reference is a `Number`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Number`. */ function isNumber(value){return typeof value === 'number';} /** * @ngdoc function * @name angular.isDate * @module ng * @kind function * * @description * Determines if a value is a date. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Date`. */ function isDate(value) { return toString.call(value) === '[object Date]'; } /** * @ngdoc function * @name angular.isArray * @module ng * @kind function * * @description * Determines if a reference is an `Array`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is an `Array`. */ var isArray = (function() { if (!isFunction(Array.isArray)) { return function(value) { return toString.call(value) === '[object Array]'; }; } return Array.isArray; })(); /** * @ngdoc function * @name angular.isFunction * @module ng * @kind function * * @description * Determines if a reference is a `Function`. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `Function`. */ function isFunction(value){return typeof value === 'function';} /** * Determines if a value is a regular expression object. * * @private * @param {*} value Reference to check. * @returns {boolean} True if `value` is a `RegExp`. */ function isRegExp(value) { return toString.call(value) === '[object RegExp]'; } /** * Checks if `obj` is a window object. * * @private * @param {*} obj Object to check * @returns {boolean} True if `obj` is a window obj. */ function isWindow(obj) { return obj && obj.document && obj.location && obj.alert && obj.setInterval; } function isScope(obj) { return obj && obj.$evalAsync && obj.$watch; } function isFile(obj) { return toString.call(obj) === '[object File]'; } function isBlob(obj) { return toString.call(obj) === '[object Blob]'; } function isBoolean(value) { return typeof value === 'boolean'; } function isPromiseLike(obj) { return obj && isFunction(obj.then); } var trim = (function() { // native trim is way faster: http://jsperf.com/angular-trim-test // but IE doesn't have it... :-( // TODO: we should move this into IE/ES5 polyfill if (!String.prototype.trim) { return function(value) { return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value; }; } return function(value) { return isString(value) ? value.trim() : value; }; })(); /** * @ngdoc function * @name angular.isElement * @module ng * @kind function * * @description * Determines if a reference is a DOM element (or wrapped jQuery element). * * @param {*} value Reference to check. * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). */ function isElement(node) { return !!(node && (node.nodeName // we are a direct element || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API } /** * @param str 'key1,key2,...' * @returns {object} in the form of {key1:true, key2:true, ...} */ function makeMap(str) { var obj = {}, items = str.split(","), i; for ( i = 0; i < items.length; i++ ) obj[ items[i] ] = true; return obj; } if (msie < 9) { nodeName_ = function(element) { element = element.nodeName ? element : element[0]; return (element.scopeName && element.scopeName != 'HTML') ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; }; } else { nodeName_ = function(element) { return element.nodeName ? element.nodeName : element[0].nodeName; }; } function map(obj, iterator, context) { var results = []; forEach(obj, function(value, index, list) { results.push(iterator.call(context, value, index, list)); }); return results; } /** * @description * Determines the number of elements in an array, the number of properties an object has, or * the length of a string. * * Note: This function is used to augment the Object type in Angular expressions. See * {@link angular.Object} for more information about Angular arrays. * * @param {Object|Array|string} obj Object, array, or string to inspect. * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. */ function size(obj, ownPropsOnly) { var count = 0, key; if (isArray(obj) || isString(obj)) { return obj.length; } else if (isObject(obj)) { for (key in obj) if (!ownPropsOnly || obj.hasOwnProperty(key)) count++; } return count; } function includes(array, obj) { return indexOf(array, obj) != -1; } function indexOf(array, obj) { if (array.indexOf) return array.indexOf(obj); for (var i = 0; i < array.length; i++) { if (obj === array[i]) return i; } return -1; } function arrayRemove(array, value) { var index = indexOf(array, value); if (index >=0) array.splice(index, 1); return value; } function isLeafNode (node) { if (node) { switch (node.nodeName) { case "OPTION": case "PRE": case "TITLE": return true; } } return false; } /** * @ngdoc function * @name angular.copy * @module ng * @kind function * * @description * Creates a deep copy of `source`, which should be an object or an array. * * * If no destination is supplied, a copy of the object or array is created. * * If a destination is provided, all of its elements (for array) or properties (for objects) * are deleted and then all elements/properties from the source are copied to it. * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned. * * If `source` is identical to 'destination' an exception will be thrown. * * @param {*} source The source that will be used to make a copy. * Can be any type, including primitives, `null`, and `undefined`. * @param {(Object|Array)=} destination Destination into which the source is copied. If * provided, must be of the same type as `source`. * @returns {*} The copy or updated `destination`, if `destination` was specified. * * @example
    Name:
    E-mail:
    Gender: male female
    form = {{user | json}}
    master = {{master | json}}
    */ function copy(source, destination, stackSource, stackDest) { if (isWindow(source) || isScope(source)) { throw ngMinErr('cpws', "Can't copy! Making copies of Window or Scope instances is not supported."); } if (!destination) { destination = source; if (source) { if (isArray(source)) { destination = copy(source, [], stackSource, stackDest); } else if (isDate(source)) { destination = new Date(source.getTime()); } else if (isRegExp(source)) { destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]); destination.lastIndex = source.lastIndex; } else if (isObject(source)) { destination = copy(source, {}, stackSource, stackDest); } } } else { if (source === destination) throw ngMinErr('cpi', "Can't copy! Source and destination are identical."); stackSource = stackSource || []; stackDest = stackDest || []; if (isObject(source)) { var index = indexOf(stackSource, source); if (index !== -1) return stackDest[index]; stackSource.push(source); stackDest.push(destination); } var result; if (isArray(source)) { destination.length = 0; for ( var i = 0; i < source.length; i++) { result = copy(source[i], null, stackSource, stackDest); if (isObject(source[i])) { stackSource.push(source[i]); stackDest.push(result); } destination.push(result); } } else { var h = destination.$$hashKey; if (isArray(destination)) { destination.length = 0; } else { forEach(destination, function(value, key) { delete destination[key]; }); } for ( var key in source) { result = copy(source[key], null, stackSource, stackDest); if (isObject(source[key])) { stackSource.push(source[key]); stackDest.push(result); } destination[key] = result; } setHashKey(destination,h); } } return destination; } /** * Creates a shallow copy of an object, an array or a primitive */ function shallowCopy(src, dst) { if (isArray(src)) { dst = dst || []; for ( var i = 0; i < src.length; i++) { dst[i] = src[i]; } } else if (isObject(src)) { dst = dst || {}; for (var key in src) { if (hasOwnProperty.call(src, key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) { dst[key] = src[key]; } } } return dst || src; } /** * @ngdoc function * @name angular.equals * @module ng * @kind function * * @description * Determines if two objects or two values are equivalent. Supports value types, regular * expressions, arrays and objects. * * Two objects or values are considered equivalent if at least one of the following is true: * * * Both objects or values pass `===` comparison. * * Both objects or values are of the same type and all of their properties are equal by * comparing them with `angular.equals`. * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal) * * Both values represent the same regular expression (In JavaScript, * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual * representation matches). * * During a property comparison, properties of `function` type and properties with names * that begin with `$` are ignored. * * Scope and DOMWindow objects are being compared only by identify (`===`). * * @param {*} o1 Object or value to compare. * @param {*} o2 Object or value to compare. * @returns {boolean} True if arguments are equal. */ function equals(o1, o2) { if (o1 === o2) return true; if (o1 === null || o2 === null) return false; if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN var t1 = typeof o1, t2 = typeof o2, length, key, keySet; if (t1 == t2) { if (t1 == 'object') { if (isArray(o1)) { if (!isArray(o2)) return false; if ((length = o1.length) == o2.length) { for(key=0; key 2 ? sliceArgs(arguments, 2) : []; if (isFunction(fn) && !(fn instanceof RegExp)) { return curryArgs.length ? function() { return arguments.length ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) : fn.apply(self, curryArgs); } : function() { return arguments.length ? fn.apply(self, arguments) : fn.call(self); }; } else { // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) return fn; } } function toJsonReplacer(key, value) { var val = value; if (typeof key === 'string' && key.charAt(0) === '$') { val = undefined; } else if (isWindow(value)) { val = '$WINDOW'; } else if (value && document === value) { val = '$DOCUMENT'; } else if (isScope(value)) { val = '$SCOPE'; } return val; } /** * @ngdoc function * @name angular.toJson * @module ng * @kind function * * @description * Serializes input into a JSON-formatted string. Properties with leading $ characters will be * stripped since angular uses this notation internally. * * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. * @returns {string|undefined} JSON-ified string representing `obj`. */ function toJson(obj, pretty) { if (typeof obj === 'undefined') return undefined; return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); } /** * @ngdoc function * @name angular.fromJson * @module ng * @kind function * * @description * Deserializes a JSON string. * * @param {string} json JSON string to deserialize. * @returns {Object|Array|string|number} Deserialized thingy. */ function fromJson(json) { return isString(json) ? JSON.parse(json) : json; } function toBoolean(value) { if (typeof value === 'function') { value = true; } else if (value && value.length !== 0) { var v = lowercase("" + value); value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); } else { value = false; } return value; } /** * @returns {string} Returns the string representation of the element. */ function startingTag(element) { element = jqLite(element).clone(); try { // turns out IE does not let you set .html() on elements which // are not allowed to have children. So we just ignore it. element.empty(); } catch(e) {} // As Per DOM Standards var TEXT_NODE = 3; var elemHtml = jqLite('
    ').append(element).html(); try { return element[0].nodeType === TEXT_NODE ? lowercase(elemHtml) : elemHtml. match(/^(<[^>]+>)/)[1]. replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); } catch(e) { return lowercase(elemHtml); } } ///////////////////////////////////////////////// /** * Tries to decode the URI component without throwing an exception. * * @private * @param str value potential URI component to check. * @returns {boolean} True if `value` can be decoded * with the decodeURIComponent function. */ function tryDecodeURIComponent(value) { try { return decodeURIComponent(value); } catch(e) { // Ignore any invalid uri component } } /** * Parses an escaped url query string into key-value pairs. * @returns {Object.} */ function parseKeyValue(/**string*/keyValue) { var obj = {}, key_value, key; forEach((keyValue || "").split('&'), function(keyValue) { if ( keyValue ) { key_value = keyValue.replace(/\+/g,'%20').split('='); key = tryDecodeURIComponent(key_value[0]); if ( isDefined(key) ) { var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true; if (!hasOwnProperty.call(obj, key)) { obj[key] = val; } else if(isArray(obj[key])) { obj[key].push(val); } else { obj[key] = [obj[key],val]; } } } }); return obj; } function toKeyValue(obj) { var parts = []; forEach(obj, function(value, key) { if (isArray(value)) { forEach(value, function(arrayValue) { parts.push(encodeUriQuery(key, true) + (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true))); }); } else { parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); } }); return parts.length ? parts.join('&') : ''; } /** * We need our custom method because encodeURIComponent is too aggressive and doesn't follow * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path * segments: * segment = *pchar * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * pct-encoded = "%" HEXDIG HEXDIG * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" */ function encodeUriSegment(val) { return encodeUriQuery(val, true). replace(/%26/gi, '&'). replace(/%3D/gi, '='). replace(/%2B/gi, '+'); } /** * This method is intended for encoding *key* or *value* parts of query component. We need a custom * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be * encoded per http://tools.ietf.org/html/rfc3986: * query = *( pchar / "/" / "?" ) * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" * pct-encoded = "%" HEXDIG HEXDIG * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" * / "*" / "+" / "," / ";" / "=" */ function encodeUriQuery(val, pctEncodeSpaces) { return encodeURIComponent(val). replace(/%40/gi, '@'). replace(/%3A/gi, ':'). replace(/%24/g, '$'). replace(/%2C/gi, ','). replace(/%20/g, (pctEncodeSpaces ? '%20' : '+')); } /** * @ngdoc directive * @name ngApp * @module ng * * @element ANY * @param {angular.Module} ngApp an optional application * {@link angular.module module} name to load. * * @description * * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive * designates the **root element** of the application and is typically placed near the root element * of the page - e.g. on the `` or `` tags. * * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp` * found in the document will be used to define the root element to auto-bootstrap as an * application. To run multiple applications in an HTML document you must manually bootstrap them using * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other. * * You can specify an **AngularJS module** to be used as the root module for the application. This * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and * should contain the application code needed or have dependencies on other modules that will * contain the code. See {@link angular.module} for more information. * * In the example below if the `ngApp` directive were not placed on the `html` element then the * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}` * would not be resolved to `3`. * * `ngApp` is the easiest, and most common, way to bootstrap an application. *
    I can add: {{a}} + {{b}} = {{ a+b }}
    angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) { $scope.a = 1; $scope.b = 2; });
    * */ function angularInit(element, bootstrap) { var elements = [element], appElement, module, names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; function append(element) { element && elements.push(element); } forEach(names, function(name) { names[name] = true; append(document.getElementById(name)); name = name.replace(':', '\\:'); if (element.querySelectorAll) { forEach(element.querySelectorAll('.' + name), append); forEach(element.querySelectorAll('.' + name + '\\:'), append); forEach(element.querySelectorAll('[' + name + ']'), append); } }); forEach(elements, function(element) { if (!appElement) { var className = ' ' + element.className + ' '; var match = NG_APP_CLASS_REGEXP.exec(className); if (match) { appElement = element; module = (match[2] || '').replace(/\s+/g, ','); } else { forEach(element.attributes, function(attr) { if (!appElement && names[attr.name]) { appElement = element; module = attr.value; } }); } } }); if (appElement) { bootstrap(appElement, module ? [module] : []); } } /** * @ngdoc function * @name angular.bootstrap * @module ng * @description * Use this function to manually start up angular application. * * See: {@link guide/bootstrap Bootstrap} * * Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually. * They must use {@link ng.directive:ngApp ngApp}. * * Angular will detect if it has been loaded into the browser more than once and only allow the * first loaded script to be bootstrapped and will report a warning to the browser console for * each of the subsequent scripts. This prevents strange results in applications, where otherwise * multiple instances of Angular try to work on the DOM. * * * * *
    * * * * * * * *
    {{heading}}
    {{fill}}
    *
    *
    * * var app = angular.module('multi-bootstrap', []) * * .controller('BrokenTable', function($scope) { * $scope.headings = ['One', 'Two', 'Three']; * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]]; * }); * * * it('should only insert one table cell for each item in $scope.fillings', function() { * expect(element.all(by.css('td')).count()) * .toBe(9); * }); * *
    * * @param {DOMElement} element DOM element which is the root of angular application. * @param {Array=} modules an array of modules to load into the application. * Each item in the array should be the name of a predefined module or a (DI annotated) * function that will be invoked by the injector as a run block. * See: {@link angular.module modules} * @returns {auto.$injector} Returns the newly created injector for this app. */ function bootstrap(element, modules) { var doBootstrap = function() { element = jqLite(element); if (element.injector()) { var tag = (element[0] === document) ? 'document' : startingTag(element); //Encode angle brackets to prevent input from being sanitized to empty string #8683 throw ngMinErr( 'btstrpd', "App Already Bootstrapped with this Element '{0}'", tag.replace(//,'>')); } modules = modules || []; modules.unshift(['$provide', function($provide) { $provide.value('$rootElement', element); }]); modules.unshift('ng'); var injector = createInjector(modules); injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector', '$animate', function(scope, element, compile, injector, animate) { scope.$apply(function() { element.data('$injector', injector); compile(element)(scope); }); }] ); return injector; }; var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/; if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) { return doBootstrap(); } window.name = window.name.replace(NG_DEFER_BOOTSTRAP, ''); angular.resumeBootstrap = function(extraModules) { forEach(extraModules, function(module) { modules.push(module); }); doBootstrap(); }; } var SNAKE_CASE_REGEXP = /[A-Z]/g; function snake_case(name, separator) { separator = separator || '_'; return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { return (pos ? separator : '') + letter.toLowerCase(); }); } function bindJQuery() { // bind to jQuery if present; jQuery = window.jQuery; // Use jQuery if it exists with proper functionality, otherwise default to us. // Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support. if (jQuery && jQuery.fn.on) { jqLite = jQuery; extend(jQuery.fn, { scope: JQLitePrototype.scope, isolateScope: JQLitePrototype.isolateScope, controller: JQLitePrototype.controller, injector: JQLitePrototype.injector, inheritedData: JQLitePrototype.inheritedData }); // Method signature: // jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) jqLitePatchJQueryRemove('remove', true, true, false); jqLitePatchJQueryRemove('empty', false, false, false); jqLitePatchJQueryRemove('html', false, false, true); } else { jqLite = JQLite; } angular.element = jqLite; } /** * throw error if the argument is falsy. */ function assertArg(arg, name, reason) { if (!arg) { throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required")); } return arg; } function assertArgFn(arg, name, acceptArrayAnnotation) { if (acceptArrayAnnotation && isArray(arg)) { arg = arg[arg.length - 1]; } assertArg(isFunction(arg), name, 'not a function, got ' + (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg)); return arg; } /** * throw error if the name given is hasOwnProperty * @param {String} name the name to test * @param {String} context the context in which the name is used, such as module or directive */ function assertNotHasOwnProperty(name, context) { if (name === 'hasOwnProperty') { throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context); } } /** * Return the value accessible from the object by path. Any undefined traversals are ignored * @param {Object} obj starting object * @param {String} path path to traverse * @param {boolean} [bindFnToScope=true] * @returns {Object} value as accessible by path */ //TODO(misko): this function needs to be removed function getter(obj, path, bindFnToScope) { if (!path) return obj; var keys = path.split('.'); var key; var lastInstance = obj; var len = keys.length; for (var i = 0; i < len; i++) { key = keys[i]; if (obj) { obj = (lastInstance = obj)[key]; } } if (!bindFnToScope && isFunction(obj)) { return bind(lastInstance, obj); } return obj; } /** * Return the DOM siblings between the first and last node in the given array. * @param {Array} array like object * @returns {DOMElement} object containing the elements */ function getBlockElements(nodes) { var startNode = nodes[0], endNode = nodes[nodes.length - 1]; if (startNode === endNode) { return jqLite(startNode); } var element = startNode; var elements = [element]; do { element = element.nextSibling; if (!element) break; elements.push(element); } while (element !== endNode); return jqLite(elements); } /** * @ngdoc type * @name angular.Module * @module ng * @description * * Interface for configuring angular {@link angular.module modules}. */ function setupModuleLoader(window) { var $injectorMinErr = minErr('$injector'); var ngMinErr = minErr('ng'); function ensure(obj, name, factory) { return obj[name] || (obj[name] = factory()); } var angular = ensure(window, 'angular', Object); // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap angular.$$minErr = angular.$$minErr || minErr; return ensure(angular, 'module', function() { /** @type {Object.} */ var modules = {}; /** * @ngdoc function * @name angular.module * @module ng * @description * * The `angular.module` is a global place for creating, registering and retrieving Angular * modules. * All modules (angular core or 3rd party) that should be available to an application must be * registered using this mechanism. * * When passed two or more arguments, a new module is created. If passed only one argument, an * existing module (the name passed as the first argument to `module`) is retrieved. * * * # Module * * A module is a collection of services, directives, controllers, filters, and configuration information. * `angular.module` is used to configure the {@link auto.$injector $injector}. * * ```js * // Create a new module * var myModule = angular.module('myModule', []); * * // register a new service * myModule.value('appName', 'MyCoolApp'); * * // configure existing services inside initialization blocks. * myModule.config(['$locationProvider', function($locationProvider) { * // Configure existing providers * $locationProvider.hashPrefix('!'); * }]); * ``` * * Then you can create an injector and load your modules like this: * * ```js * var injector = angular.injector(['ng', 'myModule']) * ``` * * However it's more likely that you'll just use * {@link ng.directive:ngApp ngApp} or * {@link angular.bootstrap} to simplify this process for you. * * @param {!string} name The name of the module to create or retrieve. * @param {!Array.=} requires If specified then new module is being created. If * unspecified then the module is being retrieved for further configuration. * @param {Function=} configFn Optional configuration function for the module. Same as * {@link angular.Module#config Module#config()}. * @returns {module} new module with the {@link angular.Module} api. */ return function module(name, requires, configFn) { var assertNotHasOwnProperty = function(name, context) { if (name === 'hasOwnProperty') { throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context); } }; assertNotHasOwnProperty(name, 'module'); if (requires && modules.hasOwnProperty(name)) { modules[name] = null; } return ensure(modules, name, function() { if (!requires) { throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " + "the module name or forgot to load it. If registering a module ensure that you " + "specify the dependencies as the second argument.", name); } /** @type {!Array.>} */ var invokeQueue = []; /** @type {!Array.} */ var runBlocks = []; var config = invokeLater('$injector', 'invoke'); /** @type {angular.Module} */ var moduleInstance = { // Private state _invokeQueue: invokeQueue, _runBlocks: runBlocks, /** * @ngdoc property * @name angular.Module#requires * @module ng * * @description * Holds the list of modules which the injector will load before the current module is * loaded. */ requires: requires, /** * @ngdoc property * @name angular.Module#name * @module ng * * @description * Name of the module. */ name: name, /** * @ngdoc method * @name angular.Module#provider * @module ng * @param {string} name service name * @param {Function} providerType Construction function for creating new instance of the * service. * @description * See {@link auto.$provide#provider $provide.provider()}. */ provider: invokeLater('$provide', 'provider'), /** * @ngdoc method * @name angular.Module#factory * @module ng * @param {string} name service name * @param {Function} providerFunction Function for creating new instance of the service. * @description * See {@link auto.$provide#factory $provide.factory()}. */ factory: invokeLater('$provide', 'factory'), /** * @ngdoc method * @name angular.Module#service * @module ng * @param {string} name service name * @param {Function} constructor A constructor function that will be instantiated. * @description * See {@link auto.$provide#service $provide.service()}. */ service: invokeLater('$provide', 'service'), /** * @ngdoc method * @name angular.Module#value * @module ng * @param {string} name service name * @param {*} object Service instance object. * @description * See {@link auto.$provide#value $provide.value()}. */ value: invokeLater('$provide', 'value'), /** * @ngdoc method * @name angular.Module#constant * @module ng * @param {string} name constant name * @param {*} object Constant value. * @description * Because the constant are fixed, they get applied before other provide methods. * See {@link auto.$provide#constant $provide.constant()}. */ constant: invokeLater('$provide', 'constant', 'unshift'), /** * @ngdoc method * @name angular.Module#animation * @module ng * @param {string} name animation name * @param {Function} animationFactory Factory function for creating new instance of an * animation. * @description * * **NOTE**: animations take effect only if the **ngAnimate** module is loaded. * * * Defines an animation hook that can be later used with * {@link ngAnimate.$animate $animate} service and directives that use this service. * * ```js * module.animation('.animation-name', function($inject1, $inject2) { * return { * eventName : function(element, done) { * //code to run the animation * //once complete, then run done() * return function cancellationFunction(element) { * //code to cancel the animation * } * } * } * }) * ``` * * See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and * {@link ngAnimate ngAnimate module} for more information. */ animation: invokeLater('$animateProvider', 'register'), /** * @ngdoc method * @name angular.Module#filter * @module ng * @param {string} name Filter name. * @param {Function} filterFactory Factory function for creating new instance of filter. * @description * See {@link ng.$filterProvider#register $filterProvider.register()}. */ filter: invokeLater('$filterProvider', 'register'), /** * @ngdoc method * @name angular.Module#controller * @module ng * @param {string|Object} name Controller name, or an object map of controllers where the * keys are the names and the values are the constructors. * @param {Function} constructor Controller constructor function. * @description * See {@link ng.$controllerProvider#register $controllerProvider.register()}. */ controller: invokeLater('$controllerProvider', 'register'), /** * @ngdoc method * @name angular.Module#directive * @module ng * @param {string|Object} name Directive name, or an object map of directives where the * keys are the names and the values are the factories. * @param {Function} directiveFactory Factory function for creating new instance of * directives. * @description * See {@link ng.$compileProvider#directive $compileProvider.directive()}. */ directive: invokeLater('$compileProvider', 'directive'), /** * @ngdoc method * @name angular.Module#config * @module ng * @param {Function} configFn Execute this function on module load. Useful for service * configuration. * @description * Use this method to register work which needs to be performed on module loading. * For more about how to configure services, see * {@link providers#providers_provider-recipe Provider Recipe}. */ config: config, /** * @ngdoc method * @name angular.Module#run * @module ng * @param {Function} initializationFn Execute this function after injector creation. * Useful for application initialization. * @description * Use this method to register work which should be performed when the injector is done * loading all modules. */ run: function(block) { runBlocks.push(block); return this; } }; if (configFn) { config(configFn); } return moduleInstance; /** * @param {string} provider * @param {string} method * @param {String=} insertMethod * @returns {angular.Module} */ function invokeLater(provider, method, insertMethod) { return function() { invokeQueue[insertMethod || 'push']([provider, method, arguments]); return moduleInstance; }; } }); }; }); } /* global angularModule: true, version: true, $LocaleProvider, $CompileProvider, htmlAnchorDirective, inputDirective, inputDirective, formDirective, scriptDirective, selectDirective, styleDirective, optionDirective, ngBindDirective, ngBindHtmlDirective, ngBindTemplateDirective, ngClassDirective, ngClassEvenDirective, ngClassOddDirective, ngCspDirective, ngCloakDirective, ngControllerDirective, ngFormDirective, ngHideDirective, ngIfDirective, ngIncludeDirective, ngIncludeFillContentDirective, ngInitDirective, ngNonBindableDirective, ngPluralizeDirective, ngRepeatDirective, ngShowDirective, ngStyleDirective, ngSwitchDirective, ngSwitchWhenDirective, ngSwitchDefaultDirective, ngOptionsDirective, ngTranscludeDirective, ngModelDirective, ngListDirective, ngChangeDirective, requiredDirective, requiredDirective, ngValueDirective, ngAttributeAliasDirectives, ngEventDirectives, $AnchorScrollProvider, $AnimateProvider, $BrowserProvider, $CacheFactoryProvider, $ControllerProvider, $DocumentProvider, $ExceptionHandlerProvider, $FilterProvider, $InterpolateProvider, $IntervalProvider, $HttpProvider, $HttpBackendProvider, $LocationProvider, $LogProvider, $ParseProvider, $RootScopeProvider, $QProvider, $$SanitizeUriProvider, $SceProvider, $SceDelegateProvider, $SnifferProvider, $TemplateCacheProvider, $TimeoutProvider, $$RAFProvider, $$AsyncCallbackProvider, $WindowProvider */ /** * @ngdoc object * @name angular.version * @module ng * @description * An object that contains information about the current AngularJS version. This object has the * following properties: * * - `full` – `{string}` – Full version string, such as "0.9.18". * - `major` – `{number}` – Major version number, such as "0". * - `minor` – `{number}` – Minor version number, such as "9". * - `dot` – `{number}` – Dot version number, such as "18". * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { full: '1.2.28', // all of these placeholder strings will be replaced by grunt's major: 1, // package task minor: 2, dot: 28, codeName: 'finnish-disembarkation' }; function publishExternalAPI(angular){ extend(angular, { 'bootstrap': bootstrap, 'copy': copy, 'extend': extend, 'equals': equals, 'element': jqLite, 'forEach': forEach, 'injector': createInjector, 'noop': noop, 'bind': bind, 'toJson': toJson, 'fromJson': fromJson, 'identity': identity, 'isUndefined': isUndefined, 'isDefined': isDefined, 'isString': isString, 'isFunction': isFunction, 'isObject': isObject, 'isNumber': isNumber, 'isElement': isElement, 'isArray': isArray, 'version': version, 'isDate': isDate, 'lowercase': lowercase, 'uppercase': uppercase, 'callbacks': {counter: 0}, '$$minErr': minErr, '$$csp': csp }); angularModule = setupModuleLoader(window); try { angularModule('ngLocale'); } catch (e) { angularModule('ngLocale', []).provider('$locale', $LocaleProvider); } angularModule('ng', ['ngLocale'], ['$provide', function ngModule($provide) { // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it. $provide.provider({ $$sanitizeUri: $$SanitizeUriProvider }); $provide.provider('$compile', $CompileProvider). directive({ a: htmlAnchorDirective, input: inputDirective, textarea: inputDirective, form: formDirective, script: scriptDirective, select: selectDirective, style: styleDirective, option: optionDirective, ngBind: ngBindDirective, ngBindHtml: ngBindHtmlDirective, ngBindTemplate: ngBindTemplateDirective, ngClass: ngClassDirective, ngClassEven: ngClassEvenDirective, ngClassOdd: ngClassOddDirective, ngCloak: ngCloakDirective, ngController: ngControllerDirective, ngForm: ngFormDirective, ngHide: ngHideDirective, ngIf: ngIfDirective, ngInclude: ngIncludeDirective, ngInit: ngInitDirective, ngNonBindable: ngNonBindableDirective, ngPluralize: ngPluralizeDirective, ngRepeat: ngRepeatDirective, ngShow: ngShowDirective, ngStyle: ngStyleDirective, ngSwitch: ngSwitchDirective, ngSwitchWhen: ngSwitchWhenDirective, ngSwitchDefault: ngSwitchDefaultDirective, ngOptions: ngOptionsDirective, ngTransclude: ngTranscludeDirective, ngModel: ngModelDirective, ngList: ngListDirective, ngChange: ngChangeDirective, required: requiredDirective, ngRequired: requiredDirective, ngValue: ngValueDirective }). directive({ ngInclude: ngIncludeFillContentDirective }). directive(ngAttributeAliasDirectives). directive(ngEventDirectives); $provide.provider({ $anchorScroll: $AnchorScrollProvider, $animate: $AnimateProvider, $browser: $BrowserProvider, $cacheFactory: $CacheFactoryProvider, $controller: $ControllerProvider, $document: $DocumentProvider, $exceptionHandler: $ExceptionHandlerProvider, $filter: $FilterProvider, $interpolate: $InterpolateProvider, $interval: $IntervalProvider, $http: $HttpProvider, $httpBackend: $HttpBackendProvider, $location: $LocationProvider, $log: $LogProvider, $parse: $ParseProvider, $rootScope: $RootScopeProvider, $q: $QProvider, $sce: $SceProvider, $sceDelegate: $SceDelegateProvider, $sniffer: $SnifferProvider, $templateCache: $TemplateCacheProvider, $timeout: $TimeoutProvider, $window: $WindowProvider, $$rAF: $$RAFProvider, $$asyncCallback : $$AsyncCallbackProvider }); } ]); } /* global JQLitePrototype: true, addEventListenerFn: true, removeEventListenerFn: true, BOOLEAN_ATTR: true */ ////////////////////////////////// //JQLite ////////////////////////////////// /** * @ngdoc function * @name angular.element * @module ng * @kind function * * @description * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. * * If jQuery is available, `angular.element` is an alias for the * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element` * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite." * *
    jqLite is a tiny, API-compatible subset of jQuery that allows * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most * commonly needed functionality with the goal of having a very small footprint.
    * * To use jQuery, simply load it before `DOMContentLoaded` event fired. * *
    **Note:** all element references in Angular are always wrapped with jQuery or * jqLite; they are never raw DOM references.
    * * ## Angular's jqLite * jqLite provides only the following jQuery methods: * * - [`addClass()`](http://api.jquery.com/addClass/) * - [`after()`](http://api.jquery.com/after/) * - [`append()`](http://api.jquery.com/append/) * - [`attr()`](http://api.jquery.com/attr/) * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData * - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`clone()`](http://api.jquery.com/clone/) * - [`contents()`](http://api.jquery.com/contents/) * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyles()` * - [`data()`](http://api.jquery.com/data/) * - [`empty()`](http://api.jquery.com/empty/) * - [`eq()`](http://api.jquery.com/eq/) * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name * - [`hasClass()`](http://api.jquery.com/hasClass/) * - [`html()`](http://api.jquery.com/html/) * - [`next()`](http://api.jquery.com/next/) - Does not support selectors * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors * - [`prepend()`](http://api.jquery.com/prepend/) * - [`prop()`](http://api.jquery.com/prop/) * - [`ready()`](http://api.jquery.com/ready/) * - [`remove()`](http://api.jquery.com/remove/) * - [`removeAttr()`](http://api.jquery.com/removeAttr/) * - [`removeClass()`](http://api.jquery.com/removeClass/) * - [`removeData()`](http://api.jquery.com/removeData/) * - [`replaceWith()`](http://api.jquery.com/replaceWith/) * - [`text()`](http://api.jquery.com/text/) * - [`toggleClass()`](http://api.jquery.com/toggleClass/) * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers. * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces * - [`val()`](http://api.jquery.com/val/) * - [`wrap()`](http://api.jquery.com/wrap/) * * ## jQuery/jqLite Extras * Angular also provides the following additional methods and events to both jQuery and jqLite: * * ### Events * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM * element before it is removed. * * ### Methods * - `controller(name)` - retrieves the controller of the current element or its parent. By default * retrieves controller associated with the `ngController` directive. If `name` is provided as * camelCase directive name, then the controller for this directive will be retrieved (e.g. * `'ngModel'`). * - `injector()` - retrieves the injector of the current element or its parent. * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current * element or its parent. * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the * current element. This getter should be used only on elements that contain a directive which starts a new isolate * scope. Calling `scope()` on this element always returns the original non-isolate scope. * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top * parent element is reached. * * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. * @returns {Object} jQuery object. */ JQLite.expando = 'ng339'; var jqCache = JQLite.cache = {}, jqId = 1, addEventListenerFn = (window.document.addEventListener ? function(element, type, fn) {element.addEventListener(type, fn, false);} : function(element, type, fn) {element.attachEvent('on' + type, fn);}), removeEventListenerFn = (window.document.removeEventListener ? function(element, type, fn) {element.removeEventListener(type, fn, false); } : function(element, type, fn) {element.detachEvent('on' + type, fn); }); /* * !!! This is an undocumented "private" function !!! */ var jqData = JQLite._data = function(node) { //jQuery always returns an object on cache miss return this.cache[node[this.expando]] || {}; }; function jqNextId() { return ++jqId; } var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; var MOZ_HACK_REGEXP = /^moz([A-Z])/; var jqLiteMinErr = minErr('jqLite'); /** * Converts snake_case to camelCase. * Also there is special case for Moz prefix starting with upper case letter. * @param name Name to normalize */ function camelCase(name) { return name. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { return offset ? letter.toUpperCase() : letter; }). replace(MOZ_HACK_REGEXP, 'Moz$1'); } ///////////////////////////////////////////// // jQuery mutation patch // // In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a // $destroy event on all DOM nodes being removed. // ///////////////////////////////////////////// function jqLitePatchJQueryRemove(name, dispatchThis, filterElems, getterIfNoArguments) { var originalJqFn = jQuery.fn[name]; originalJqFn = originalJqFn.$original || originalJqFn; removePatch.$original = originalJqFn; jQuery.fn[name] = removePatch; function removePatch(param) { // jshint -W040 var list = filterElems && param ? [this.filter(param)] : [this], fireEvent = dispatchThis, set, setIndex, setLength, element, childIndex, childLength, children; if (!getterIfNoArguments || param != null) { while(list.length) { set = list.shift(); for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { element = jqLite(set[setIndex]); if (fireEvent) { element.triggerHandler('$destroy'); } else { fireEvent = !fireEvent; } for(childIndex = 0, childLength = (children = element.children()).length; childIndex < childLength; childIndex++) { list.push(jQuery(children[childIndex])); } } } } return originalJqFn.apply(this, arguments); } } var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/; var HTML_REGEXP = /<|&#?\w+;/; var TAG_NAME_REGEXP = /<([\w:]+)/; var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi; var wrapMap = { 'option': [1, ''], 'thead': [1, '', '
    '], 'col': [2, '', '
    '], 'tr': [2, '', '
    '], 'td': [3, '', '
    '], '_default': [0, "", ""] }; wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; function jqLiteIsTextNode(html) { return !HTML_REGEXP.test(html); } function jqLiteBuildFragment(html, context) { var elem, tmp, tag, wrap, fragment = context.createDocumentFragment(), nodes = [], i, j, jj; if (jqLiteIsTextNode(html)) { // Convert non-html into a text node nodes.push(context.createTextNode(html)); } else { tmp = fragment.appendChild(context.createElement('div')); // Convert html into DOM nodes tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase(); wrap = wrapMap[tag] || wrapMap._default; tmp.innerHTML = '
     
    ' + wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1>") + wrap[2]; tmp.removeChild(tmp.firstChild); // Descend through wrappers to the right content i = wrap[0]; while (i--) { tmp = tmp.lastChild; } for (j=0, jj=tmp.childNodes.length; j -1); } function jqLiteRemoveClass(element, cssClasses) { if (cssClasses && element.setAttribute) { forEach(cssClasses.split(' '), function(cssClass) { element.setAttribute('class', trim( (" " + (element.getAttribute('class') || '') + " ") .replace(/[\n\t]/g, " ") .replace(" " + trim(cssClass) + " ", " ")) ); }); } } function jqLiteAddClass(element, cssClasses) { if (cssClasses && element.setAttribute) { var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ') .replace(/[\n\t]/g, " "); forEach(cssClasses.split(' '), function(cssClass) { cssClass = trim(cssClass); if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { existingClasses += cssClass + ' '; } }); element.setAttribute('class', trim(existingClasses)); } } function jqLiteAddNodes(root, elements) { if (elements) { elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) ? elements : [ elements ]; for(var i=0; i < elements.length; i++) { root.push(elements[i]); } } } function jqLiteController(element, name) { return jqLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); } function jqLiteInheritedData(element, name, value) { // if element is the document object work with the html element instead // this makes $(document).scope() possible if(element.nodeType == 9) { element = element.documentElement; } var names = isArray(name) ? name : [name]; while (element) { for (var i = 0, ii = names.length; i < ii; i++) { if ((value = jqLite.data(element, names[i])) !== undefined) return value; } // If dealing with a document fragment node with a host element, and no parent, use the host // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM // to lookup parent controllers. element = element.parentNode || (element.nodeType === 11 && element.host); } } function jqLiteEmpty(element) { for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { jqLiteDealoc(childNodes[i]); } while (element.firstChild) { element.removeChild(element.firstChild); } } ////////////////////////////////////////// // Functions which are declared directly. ////////////////////////////////////////// var JQLitePrototype = JQLite.prototype = { ready: function(fn) { var fired = false; function trigger() { if (fired) return; fired = true; fn(); } // check if document already is loaded if (document.readyState === 'complete'){ setTimeout(trigger); } else { this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 // we can not use jqLite since we are not done loading and jQuery could be loaded later. // jshint -W064 JQLite(window).on('load', trigger); // fallback to window.onload for others // jshint +W064 } }, toString: function() { var value = []; forEach(this, function(e){ value.push('' + e);}); return '[' + value.join(', ') + ']'; }, eq: function(index) { return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); }, length: 0, push: push, sort: [].sort, splice: [].splice }; ////////////////////////////////////////// // Functions iterating getter/setters. // these functions return self on setter and // value on get. ////////////////////////////////////////// var BOOLEAN_ATTR = {}; forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) { BOOLEAN_ATTR[lowercase(value)] = value; }); var BOOLEAN_ELEMENTS = {}; forEach('input,select,option,textarea,button,form,details'.split(','), function(value) { BOOLEAN_ELEMENTS[uppercase(value)] = true; }); function getBooleanAttrName(element, name) { // check dom last since we will most likely fail on name var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; // booleanAttr is here twice to minimize DOM access return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; } forEach({ data: jqLiteData, removeData: jqLiteRemoveData }, function(fn, name) { JQLite[name] = fn; }); forEach({ data: jqLiteData, inheritedData: jqLiteInheritedData, scope: function(element) { // Can't use jqLiteData here directly so we stay compatible with jQuery! return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']); }, isolateScope: function(element) { // Can't use jqLiteData here directly so we stay compatible with jQuery! return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate'); }, controller: jqLiteController, injector: function(element) { return jqLiteInheritedData(element, '$injector'); }, removeAttr: function(element,name) { element.removeAttribute(name); }, hasClass: jqLiteHasClass, css: function(element, name, value) { name = camelCase(name); if (isDefined(value)) { element.style[name] = value; } else { var val; if (msie <= 8) { // this is some IE specific weirdness that jQuery 1.6.4 does not sure why val = element.currentStyle && element.currentStyle[name]; if (val === '') val = 'auto'; } val = val || element.style[name]; if (msie <= 8) { // jquery weirdness :-/ val = (val === '') ? undefined : val; } return val; } }, attr: function(element, name, value){ var lowercasedName = lowercase(name); if (BOOLEAN_ATTR[lowercasedName]) { if (isDefined(value)) { if (!!value) { element[name] = true; element.setAttribute(name, lowercasedName); } else { element[name] = false; element.removeAttribute(lowercasedName); } } else { return (element[name] || (element.attributes.getNamedItem(name)|| noop).specified) ? lowercasedName : undefined; } } else if (isDefined(value)) { element.setAttribute(name, value); } else if (element.getAttribute) { // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code // some elements (e.g. Document) don't have get attribute, so return undefined var ret = element.getAttribute(name, 2); // normalize non-existing attributes to undefined (as jQuery) return ret === null ? undefined : ret; } }, prop: function(element, name, value) { if (isDefined(value)) { element[name] = value; } else { return element[name]; } }, text: (function() { var NODE_TYPE_TEXT_PROPERTY = []; if (msie < 9) { NODE_TYPE_TEXT_PROPERTY[1] = 'innerText'; /** Element **/ NODE_TYPE_TEXT_PROPERTY[3] = 'nodeValue'; /** Text **/ } else { NODE_TYPE_TEXT_PROPERTY[1] = /** Element **/ NODE_TYPE_TEXT_PROPERTY[3] = 'textContent'; /** Text **/ } getText.$dv = ''; return getText; function getText(element, value) { var textProp = NODE_TYPE_TEXT_PROPERTY[element.nodeType]; if (isUndefined(value)) { return textProp ? element[textProp] : ''; } element[textProp] = value; } })(), val: function(element, value) { if (isUndefined(value)) { if (nodeName_(element) === 'SELECT' && element.multiple) { var result = []; forEach(element.options, function (option) { if (option.selected) { result.push(option.value || option.text); } }); return result.length === 0 ? null : result; } return element.value; } element.value = value; }, html: function(element, value) { if (isUndefined(value)) { return element.innerHTML; } for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { jqLiteDealoc(childNodes[i]); } element.innerHTML = value; }, empty: jqLiteEmpty }, function(fn, name){ /** * Properties: writes return selection, reads return first value */ JQLite.prototype[name] = function(arg1, arg2) { var i, key; var nodeCount = this.length; // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it // in a way that survives minification. // jqLiteEmpty takes no arguments but is a setter. if (fn !== jqLiteEmpty && (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) { if (isObject(arg1)) { // we are a write, but the object properties are the key/values for (i = 0; i < nodeCount; i++) { if (fn === jqLiteData) { // data() takes the whole object in jQuery fn(this[i], arg1); } else { for (key in arg1) { fn(this[i], key, arg1[key]); } } } // return self for chaining return this; } else { // we are a read, so read the first child. // TODO: do we still need this? var value = fn.$dv; // Only if we have $dv do we iterate over all, otherwise it is just the first element. var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount; for (var j = 0; j < jj; j++) { var nodeValue = fn(this[j], arg1, arg2); value = value ? value + nodeValue : nodeValue; } return value; } } else { // we are a write, so apply to all children for (i = 0; i < nodeCount; i++) { fn(this[i], arg1, arg2); } // return self for chaining return this; } }; }); function createEventHandler(element, events) { var eventHandler = function (event, type) { if (!event.preventDefault) { event.preventDefault = function() { event.returnValue = false; //ie }; } if (!event.stopPropagation) { event.stopPropagation = function() { event.cancelBubble = true; //ie }; } if (!event.target) { event.target = event.srcElement || document; } if (isUndefined(event.defaultPrevented)) { var prevent = event.preventDefault; event.preventDefault = function() { event.defaultPrevented = true; prevent.call(event); }; event.defaultPrevented = false; } event.isDefaultPrevented = function() { return event.defaultPrevented || event.returnValue === false; }; // Copy event handlers in case event handlers array is modified during execution. var eventHandlersCopy = shallowCopy(events[type || event.type] || []); forEach(eventHandlersCopy, function(fn) { fn.call(element, event); }); // Remove monkey-patched methods (IE), // as they would cause memory leaks in IE8. if (msie <= 8) { // IE7/8 does not allow to delete property on native object event.preventDefault = null; event.stopPropagation = null; event.isDefaultPrevented = null; } else { // It shouldn't affect normal browsers (native methods are defined on prototype). delete event.preventDefault; delete event.stopPropagation; delete event.isDefaultPrevented; } }; eventHandler.elem = element; return eventHandler; } ////////////////////////////////////////// // Functions iterating traversal. // These functions chain results into a single // selector. ////////////////////////////////////////// forEach({ removeData: jqLiteRemoveData, dealoc: jqLiteDealoc, on: function onFn(element, type, fn, unsupported){ if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters'); var events = jqLiteExpandoStore(element, 'events'), handle = jqLiteExpandoStore(element, 'handle'); if (!events) jqLiteExpandoStore(element, 'events', events = {}); if (!handle) jqLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); forEach(type.split(' '), function(type){ var eventFns = events[type]; if (!eventFns) { if (type == 'mouseenter' || type == 'mouseleave') { var contains = document.body.contains || document.body.compareDocumentPosition ? function( a, b ) { // jshint bitwise: false var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; return a === bup || !!( bup && bup.nodeType === 1 && ( adown.contains ? adown.contains( bup ) : a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 )); } : function( a, b ) { if ( b ) { while ( (b = b.parentNode) ) { if ( b === a ) { return true; } } } return false; }; events[type] = []; // Refer to jQuery's implementation of mouseenter & mouseleave // Read about mouseenter and mouseleave: // http://www.quirksmode.org/js/events_mouse.html#link8 var eventmap = { mouseleave : "mouseout", mouseenter : "mouseover"}; onFn(element, eventmap[type], function(event) { var target = this, related = event.relatedTarget; // For mousenter/leave call the handler if related is outside the target. // NB: No relatedTarget if the mouse left/entered the browser window if ( !related || (related !== target && !contains(target, related)) ){ handle(event, type); } }); } else { addEventListenerFn(element, type, handle); events[type] = []; } eventFns = events[type]; } eventFns.push(fn); }); }, off: jqLiteOff, one: function(element, type, fn) { element = jqLite(element); //add the listener twice so that when it is called //you can remove the original function and still be //able to call element.off(ev, fn) normally element.on(type, function onFn() { element.off(type, fn); element.off(type, onFn); }); element.on(type, fn); }, replaceWith: function(element, replaceNode) { var index, parent = element.parentNode; jqLiteDealoc(element); forEach(new JQLite(replaceNode), function(node){ if (index) { parent.insertBefore(node, index.nextSibling); } else { parent.replaceChild(node, element); } index = node; }); }, children: function(element) { var children = []; forEach(element.childNodes, function(element){ if (element.nodeType === 1) children.push(element); }); return children; }, contents: function(element) { return element.contentDocument || element.childNodes || []; }, append: function(element, node) { forEach(new JQLite(node), function(child){ if (element.nodeType === 1 || element.nodeType === 11) { element.appendChild(child); } }); }, prepend: function(element, node) { if (element.nodeType === 1) { var index = element.firstChild; forEach(new JQLite(node), function(child){ element.insertBefore(child, index); }); } }, wrap: function(element, wrapNode) { wrapNode = jqLite(wrapNode)[0]; var parent = element.parentNode; if (parent) { parent.replaceChild(wrapNode, element); } wrapNode.appendChild(element); }, remove: function(element) { jqLiteDealoc(element); var parent = element.parentNode; if (parent) parent.removeChild(element); }, after: function(element, newElement) { var index = element, parent = element.parentNode; forEach(new JQLite(newElement), function(node){ parent.insertBefore(node, index.nextSibling); index = node; }); }, addClass: jqLiteAddClass, removeClass: jqLiteRemoveClass, toggleClass: function(element, selector, condition) { if (selector) { forEach(selector.split(' '), function(className){ var classCondition = condition; if (isUndefined(classCondition)) { classCondition = !jqLiteHasClass(element, className); } (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className); }); } }, parent: function(element) { var parent = element.parentNode; return parent && parent.nodeType !== 11 ? parent : null; }, next: function(element) { if (element.nextElementSibling) { return element.nextElementSibling; } // IE8 doesn't have nextElementSibling var elm = element.nextSibling; while (elm != null && elm.nodeType !== 1) { elm = elm.nextSibling; } return elm; }, find: function(element, selector) { if (element.getElementsByTagName) { return element.getElementsByTagName(selector); } else { return []; } }, clone: jqLiteClone, triggerHandler: function(element, event, extraParameters) { var dummyEvent, eventFnsCopy, handlerArgs; var eventName = event.type || event; var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName]; if (eventFns) { // Create a dummy event to pass to the handlers dummyEvent = { preventDefault: function() { this.defaultPrevented = true; }, isDefaultPrevented: function() { return this.defaultPrevented === true; }, stopPropagation: noop, type: eventName, target: element }; // If a custom event was provided then extend our dummy event with it if (event.type) { dummyEvent = extend(dummyEvent, event); } // Copy event handlers in case event handlers array is modified during execution. eventFnsCopy = shallowCopy(eventFns); handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent]; forEach(eventFnsCopy, function(fn) { fn.apply(element, handlerArgs); }); } } }, function(fn, name){ /** * chaining functions */ JQLite.prototype[name] = function(arg1, arg2, arg3) { var value; for(var i=0; i < this.length; i++) { if (isUndefined(value)) { value = fn(this[i], arg1, arg2, arg3); if (isDefined(value)) { // any function which returns a value needs to be wrapped value = jqLite(value); } } else { jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3)); } } return isDefined(value) ? value : this; }; // bind legacy bind/unbind to on/off JQLite.prototype.bind = JQLite.prototype.on; JQLite.prototype.unbind = JQLite.prototype.off; }); /** * Computes a hash of an 'obj'. * Hash of a: * string is string * number is number as string * object is either result of calling $$hashKey function on the object or uniquely generated id, * that is also assigned to the $$hashKey property of the object. * * @param obj * @returns {string} hash string such that the same input will have the same hash string. * The resulting string key is in 'type:hashKey' format. */ function hashKey(obj, nextUidFn) { var objType = typeof obj, key; if (objType == 'function' || (objType == 'object' && obj !== null)) { if (typeof (key = obj.$$hashKey) == 'function') { // must invoke on object to keep the right this key = obj.$$hashKey(); } else if (key === undefined) { key = obj.$$hashKey = (nextUidFn || nextUid)(); } } else { key = obj; } return objType + ':' + key; } /** * HashMap which can use objects as keys */ function HashMap(array, isolatedUid) { if (isolatedUid) { var uid = 0; this.nextUid = function() { return ++uid; }; } forEach(array, this.put, this); } HashMap.prototype = { /** * Store key value pair * @param key key to store can be any type * @param value value to store can be any type */ put: function(key, value) { this[hashKey(key, this.nextUid)] = value; }, /** * @param key * @returns {Object} the value for the key */ get: function(key) { return this[hashKey(key, this.nextUid)]; }, /** * Remove the key/value pair * @param key */ remove: function(key) { var value = this[key = hashKey(key, this.nextUid)]; delete this[key]; return value; } }; /** * @ngdoc function * @module ng * @name angular.injector * @kind function * * @description * Creates an injector object that can be used for retrieving services as well as for * dependency injection (see {@link guide/di dependency injection}). * * @param {Array.} modules A list of module functions or their aliases. See * {@link angular.module}. The `ng` module must be explicitly added. * @returns {injector} Injector object. See {@link auto.$injector $injector}. * * @example * Typical usage * ```js * // create an injector * var $injector = angular.injector(['ng']); * * // use the injector to kick off your application * // use the type inference to auto inject arguments, or use implicit injection * $injector.invoke(function($rootScope, $compile, $document){ * $compile($document)($rootScope); * $rootScope.$digest(); * }); * ``` * * Sometimes you want to get access to the injector of a currently running Angular app * from outside Angular. Perhaps, you want to inject and compile some markup after the * application has been bootstrapped. You can do this using the extra `injector()` added * to JQuery/jqLite elements. See {@link angular.element}. * * *This is fairly rare but could be the case if a third party library is injecting the * markup.* * * In the following example a new block of HTML containing a `ng-controller` * directive is added to the end of the document body by JQuery. We then compile and link * it into the current AngularJS scope. * * ```js * var $div = $('
    {{content.label}}
    '); * $(document.body).append($div); * * angular.element(document).injector().invoke(function($compile) { * var scope = angular.element($div).scope(); * $compile($div)(scope); * }); * ``` */ /** * @ngdoc module * @name auto * @description * * Implicit module which gets automatically added to each {@link auto.$injector $injector}. */ var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; var FN_ARG_SPLIT = /,/; var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; var $injectorMinErr = minErr('$injector'); function annotate(fn) { var $inject, fnText, argDecl, last; if (typeof fn === 'function') { if (!($inject = fn.$inject)) { $inject = []; if (fn.length) { fnText = fn.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS); forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ arg.replace(FN_ARG, function(all, underscore, name){ $inject.push(name); }); }); } fn.$inject = $inject; } } else if (isArray(fn)) { last = fn.length - 1; assertArgFn(fn[last], 'fn'); $inject = fn.slice(0, last); } else { assertArgFn(fn, 'fn', true); } return $inject; } /////////////////////////////////////// /** * @ngdoc service * @name $injector * * @description * * `$injector` is used to retrieve object instances as defined by * {@link auto.$provide provider}, instantiate types, invoke methods, * and load modules. * * The following always holds true: * * ```js * var $injector = angular.injector(); * expect($injector.get('$injector')).toBe($injector); * expect($injector.invoke(function($injector){ * return $injector; * })).toBe($injector); * ``` * * # Injection Function Annotation * * JavaScript does not have annotations, and annotations are needed for dependency injection. The * following are all valid ways of annotating function with injection arguments and are equivalent. * * ```js * // inferred (only works if code not minified/obfuscated) * $injector.invoke(function(serviceA){}); * * // annotated * function explicit(serviceA) {}; * explicit.$inject = ['serviceA']; * $injector.invoke(explicit); * * // inline * $injector.invoke(['serviceA', function(serviceA){}]); * ``` * * ## Inference * * In JavaScript calling `toString()` on a function returns the function definition. The definition * can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with * minification, and obfuscation tools since these tools change the argument names. * * ## `$inject` Annotation * By adding an `$inject` property onto a function the injection parameters can be specified. * * ## Inline * As an array of injection names, where the last item in the array is the function to call. */ /** * @ngdoc method * @name $injector#get * * @description * Return an instance of the service. * * @param {string} name The name of the instance to retrieve. * @return {*} The instance. */ /** * @ngdoc method * @name $injector#invoke * * @description * Invoke the method and supply the method arguments from the `$injector`. * * @param {!Function} fn The function to invoke. Function parameters are injected according to the * {@link guide/di $inject Annotation} rules. * @param {Object=} self The `this` for the invoked method. * @param {Object=} locals Optional object. If preset then any argument names are read from this * object first, before the `$injector` is consulted. * @returns {*} the value returned by the invoked `fn` function. */ /** * @ngdoc method * @name $injector#has * * @description * Allows the user to query if the particular service exists. * * @param {string} name Name of the service to query. * @returns {boolean} `true` if injector has given service. */ /** * @ngdoc method * @name $injector#instantiate * @description * Create a new instance of JS type. The method takes a constructor function, invokes the new * operator, and supplies all of the arguments to the constructor function as specified by the * constructor annotation. * * @param {Function} Type Annotated constructor function. * @param {Object=} locals Optional object. If preset then any argument names are read from this * object first, before the `$injector` is consulted. * @returns {Object} new instance of `Type`. */ /** * @ngdoc method * @name $injector#annotate * * @description * Returns an array of service names which the function is requesting for injection. This API is * used by the injector to determine which services need to be injected into the function when the * function is invoked. There are three ways in which the function can be annotated with the needed * dependencies. * * # Argument names * * The simplest form is to extract the dependencies from the arguments of the function. This is done * by converting the function into a string using `toString()` method and extracting the argument * names. * ```js * // Given * function MyController($scope, $route) { * // ... * } * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * ``` * * This method does not work with code minification / obfuscation. For this reason the following * annotation strategies are supported. * * # The `$inject` property * * If a function has an `$inject` property and its value is an array of strings, then the strings * represent names of services to be injected into the function. * ```js * // Given * var MyController = function(obfuscatedScope, obfuscatedRoute) { * // ... * } * // Define function dependencies * MyController['$inject'] = ['$scope', '$route']; * * // Then * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); * ``` * * # The array notation * * It is often desirable to inline Injected functions and that's when setting the `$inject` property * is very inconvenient. In these situations using the array notation to specify the dependencies in * a way that survives minification is a better choice: * * ```js * // We wish to write this (not minification / obfuscation safe) * injector.invoke(function($compile, $rootScope) { * // ... * }); * * // We are forced to write break inlining * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { * // ... * }; * tmpFn.$inject = ['$compile', '$rootScope']; * injector.invoke(tmpFn); * * // To better support inline function the inline annotation is supported * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { * // ... * }]); * * // Therefore * expect(injector.annotate( * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) * ).toEqual(['$compile', '$rootScope']); * ``` * * @param {Function|Array.} fn Function for which dependent service names need to * be retrieved as described above. * * @returns {Array.} The names of the services which the function requires. */ /** * @ngdoc service * @name $provide * * @description * * The {@link auto.$provide $provide} service has a number of methods for registering components * with the {@link auto.$injector $injector}. Many of these functions are also exposed on * {@link angular.Module}. * * An Angular **service** is a singleton object created by a **service factory**. These **service * factories** are functions which, in turn, are created by a **service provider**. * The **service providers** are constructor functions. When instantiated they must contain a * property called `$get`, which holds the **service factory** function. * * When you request a service, the {@link auto.$injector $injector} is responsible for finding the * correct **service provider**, instantiating it and then calling its `$get` **service factory** * function to get the instance of the **service**. * * Often services have no configuration options and there is no need to add methods to the service * provider. The provider will be no more than a constructor function with a `$get` property. For * these cases the {@link auto.$provide $provide} service has additional helper methods to register * services without specifying a provider. * * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the * {@link auto.$injector $injector} * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by * providers and services. * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by * services, not providers. * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`, * that will be wrapped in a **service provider** object, whose `$get` property will contain the * given factory function. * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class` * that will be wrapped in a **service provider** object, whose `$get` property will instantiate * a new object using the given constructor function. * * See the individual methods for more information and examples. */ /** * @ngdoc method * @name $provide#provider * @description * * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions * are constructor functions, whose instances are responsible for "providing" a factory for a * service. * * Service provider names start with the name of the service they provide followed by `Provider`. * For example, the {@link ng.$log $log} service has a provider called * {@link ng.$logProvider $logProvider}. * * Service provider objects can have additional methods which allow configuration of the provider * and its service. Importantly, you can configure what kind of service is created by the `$get` * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a * method {@link ng.$logProvider#debugEnabled debugEnabled} * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the * console or not. * * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. * @param {(Object|function())} provider If the provider is: * * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created. * - `Constructor`: a new instance of the provider will be created using * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`. * * @returns {Object} registered provider instance * @example * * The following example shows how to create a simple event tracking service and register it using * {@link auto.$provide#provider $provide.provider()}. * * ```js * // Define the eventTracker provider * function EventTrackerProvider() { * var trackingUrl = '/track'; * * // A provider method for configuring where the tracked events should been saved * this.setTrackingUrl = function(url) { * trackingUrl = url; * }; * * // The service factory function * this.$get = ['$http', function($http) { * var trackedEvents = {}; * return { * // Call this to track an event * event: function(event) { * var count = trackedEvents[event] || 0; * count += 1; * trackedEvents[event] = count; * return count; * }, * // Call this to save the tracked events to the trackingUrl * save: function() { * $http.post(trackingUrl, trackedEvents); * } * }; * }]; * } * * describe('eventTracker', function() { * var postSpy; * * beforeEach(module(function($provide) { * // Register the eventTracker provider * $provide.provider('eventTracker', EventTrackerProvider); * })); * * beforeEach(module(function(eventTrackerProvider) { * // Configure eventTracker provider * eventTrackerProvider.setTrackingUrl('/custom-track'); * })); * * it('tracks events', inject(function(eventTracker) { * expect(eventTracker.event('login')).toEqual(1); * expect(eventTracker.event('login')).toEqual(2); * })); * * it('saves to the tracking url', inject(function(eventTracker, $http) { * postSpy = spyOn($http, 'post'); * eventTracker.event('login'); * eventTracker.save(); * expect(postSpy).toHaveBeenCalled(); * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track'); * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track'); * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 }); * })); * }); * ``` */ /** * @ngdoc method * @name $provide#factory * @description * * Register a **service factory**, which will be called to return the service instance. * This is short for registering a service where its provider consists of only a `$get` property, * which is the given service factory function. * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to * configure your service in a provider. * * @param {string} name The name of the instance. * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand * for `$provide.provider(name, {$get: $getFn})`. * @returns {Object} registered provider instance * * @example * Here is an example of registering a service * ```js * $provide.factory('ping', ['$http', function($http) { * return function ping() { * return $http.send('/ping'); * }; * }]); * ``` * You would then inject and use this service like this: * ```js * someModule.controller('Ctrl', ['ping', function(ping) { * ping(); * }]); * ``` */ /** * @ngdoc method * @name $provide#service * @description * * Register a **service constructor**, which will be invoked with `new` to create the service * instance. * This is short for registering a service where its provider's `$get` property is the service * constructor function that will be used to instantiate the service instance. * * You should use {@link auto.$provide#service $provide.service(class)} if you define your service * as a type/class. * * @param {string} name The name of the instance. * @param {Function} constructor A class (constructor function) that will be instantiated. * @returns {Object} registered provider instance * * @example * Here is an example of registering a service using * {@link auto.$provide#service $provide.service(class)}. * ```js * var Ping = function($http) { * this.$http = $http; * }; * * Ping.$inject = ['$http']; * * Ping.prototype.send = function() { * return this.$http.get('/ping'); * }; * $provide.service('ping', Ping); * ``` * You would then inject and use this service like this: * ```js * someModule.controller('Ctrl', ['ping', function(ping) { * ping.send(); * }]); * ``` */ /** * @ngdoc method * @name $provide#value * @description * * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a * number, an array, an object or a function. This is short for registering a service where its * provider's `$get` property is a factory function that takes no arguments and returns the **value * service**. * * Value services are similar to constant services, except that they cannot be injected into a * module configuration function (see {@link angular.Module#config}) but they can be overridden by * an Angular * {@link auto.$provide#decorator decorator}. * * @param {string} name The name of the instance. * @param {*} value The value. * @returns {Object} registered provider instance * * @example * Here are some examples of creating value services. * ```js * $provide.value('ADMIN_USER', 'admin'); * * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 }); * * $provide.value('halfOf', function(value) { * return value / 2; * }); * ``` */ /** * @ngdoc method * @name $provide#constant * @description * * Register a **constant service**, such as a string, a number, an array, an object or a function, * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be * injected into a module configuration function (see {@link angular.Module#config}) and it cannot * be overridden by an Angular {@link auto.$provide#decorator decorator}. * * @param {string} name The name of the constant. * @param {*} value The constant value. * @returns {Object} registered instance * * @example * Here a some examples of creating constants: * ```js * $provide.constant('SHARD_HEIGHT', 306); * * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']); * * $provide.constant('double', function(value) { * return value * 2; * }); * ``` */ /** * @ngdoc method * @name $provide#decorator * @description * * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator * intercepts the creation of a service, allowing it to override or modify the behaviour of the * service. The object returned by the decorator may be the original service, or a new service * object which replaces or wraps and delegates to the original service. * * @param {string} name The name of the service to decorate. * @param {function()} decorator This function will be invoked when the service needs to be * instantiated and should return the decorated service instance. The function is called using * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable. * Local injection arguments: * * * `$delegate` - The original service instance, which can be monkey patched, configured, * decorated or delegated to. * * @example * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting * calls to {@link ng.$log#error $log.warn()}. * ```js * $provide.decorator('$log', ['$delegate', function($delegate) { * $delegate.warn = $delegate.error; * return $delegate; * }]); * ``` */ function createInjector(modulesToLoad) { var INSTANTIATING = {}, providerSuffix = 'Provider', path = [], loadedModules = new HashMap([], true), providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, providerInjector = (providerCache.$injector = createInternalInjector(providerCache, function() { throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- ')); })), instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(servicename) { var provider = providerInjector.get(servicename + providerSuffix); return instanceInjector.invoke(provider.$get, provider); })); forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); return instanceInjector; //////////////////////////////////// // $provider //////////////////////////////////// function supportObject(delegate) { return function(key, value) { if (isObject(key)) { forEach(key, reverseParams(delegate)); } else { return delegate(key, value); } }; } function provider(name, provider_) { assertNotHasOwnProperty(name, 'service'); if (isFunction(provider_) || isArray(provider_)) { provider_ = providerInjector.instantiate(provider_); } if (!provider_.$get) { throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name); } return providerCache[name + providerSuffix] = provider_; } function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } function service(name, constructor) { return factory(name, ['$injector', function($injector) { return $injector.instantiate(constructor); }]); } function value(name, val) { return factory(name, valueFn(val)); } function constant(name, value) { assertNotHasOwnProperty(name, 'constant'); providerCache[name] = value; instanceCache[name] = value; } function decorator(serviceName, decorFn) { var origProvider = providerInjector.get(serviceName + providerSuffix), orig$get = origProvider.$get; origProvider.$get = function() { var origInstance = instanceInjector.invoke(orig$get, origProvider); return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); }; } //////////////////////////////////// // Module Loading //////////////////////////////////// function loadModules(modulesToLoad){ var runBlocks = [], moduleFn, invokeQueue, i, ii; forEach(modulesToLoad, function(module) { if (loadedModules.get(module)) return; loadedModules.put(module, true); try { if (isString(module)) { moduleFn = angularModule(module); runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); for(invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { var invokeArgs = invokeQueue[i], provider = providerInjector.get(invokeArgs[0]); provider[invokeArgs[1]].apply(provider, invokeArgs[2]); } } else if (isFunction(module)) { runBlocks.push(providerInjector.invoke(module)); } else if (isArray(module)) { runBlocks.push(providerInjector.invoke(module)); } else { assertArgFn(module, 'module'); } } catch (e) { if (isArray(module)) { module = module[module.length - 1]; } if (e.message && e.stack && e.stack.indexOf(e.message) == -1) { // Safari & FF's stack traces don't contain error.message content // unlike those of Chrome and IE // So if stack doesn't contain message, we create a new string that contains both. // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here. /* jshint -W022 */ e = e.message + '\n' + e.stack; } throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e); } }); return runBlocks; } //////////////////////////////////// // internal Injector //////////////////////////////////// function createInternalInjector(cache, factory) { function getService(serviceName) { if (cache.hasOwnProperty(serviceName)) { if (cache[serviceName] === INSTANTIATING) { throw $injectorMinErr('cdep', 'Circular dependency found: {0}', serviceName + ' <- ' + path.join(' <- ')); } return cache[serviceName]; } else { try { path.unshift(serviceName); cache[serviceName] = INSTANTIATING; return cache[serviceName] = factory(serviceName); } catch (err) { if (cache[serviceName] === INSTANTIATING) { delete cache[serviceName]; } throw err; } finally { path.shift(); } } } function invoke(fn, self, locals){ var args = [], $inject = annotate(fn), length, i, key; for(i = 0, length = $inject.length; i < length; i++) { key = $inject[i]; if (typeof key !== 'string') { throw $injectorMinErr('itkn', 'Incorrect injection token! Expected service name as string, got {0}', key); } args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key) ); } if (isArray(fn)) { fn = fn[length]; } // http://jsperf.com/angularjs-invoke-apply-vs-switch // #5388 return fn.apply(self, args); } function instantiate(Type, locals) { var Constructor = function() {}, instance, returnedValue; // Check if Type is annotated and use just the given function at n-1 as parameter // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]); Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; instance = new Constructor(); returnedValue = invoke(Type, instance, locals); return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance; } return { invoke: invoke, instantiate: instantiate, get: getService, annotate: annotate, has: function(name) { return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name); } }; } } /** * @ngdoc service * @name $anchorScroll * @kind function * @requires $window * @requires $location * @requires $rootScope * * @description * When called, it checks current value of `$location.hash()` and scrolls to the related element, * according to rules specified in * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document). * * It also watches the `$location.hash()` and scrolls whenever it changes to match any anchor. * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. * * @example
    Go to bottom You're at the bottom!
    function ScrollCtrl($scope, $location, $anchorScroll) { $scope.gotoBottom = function (){ // set the location.hash to the id of // the element you wish to scroll to. $location.hash('bottom'); // call $anchorScroll() $anchorScroll(); }; } #scrollArea { height: 350px; overflow: auto; } #bottom { display: block; margin-top: 2000px; }
    */ function $AnchorScrollProvider() { var autoScrollingEnabled = true; /** * @ngdoc method * @name $anchorScrollProvider#disableAutoScrolling * * @description * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.
    * Use this method to disable automatic scrolling. * * If automatic scrolling is disabled, one must explicitly call * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the * current hash. */ this.disableAutoScrolling = function() { autoScrollingEnabled = false; }; this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { var document = $window.document; // helper function to get first anchor from a NodeList // can't use filter.filter, as it accepts only instances of Array // and IE can't convert NodeList to an array using [].slice // TODO(vojta): use filter if we change it to accept lists as well function getFirstAnchor(list) { var result = null; forEach(list, function(element) { if (!result && lowercase(element.nodeName) === 'a') result = element; }); return result; } function scroll() { var hash = $location.hash(), elm; // empty hash, scroll to the top of the page if (!hash) $window.scrollTo(0, 0); // element with given id else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); // first anchor with given name :-D else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); // no element and hash == 'top', scroll to the top of the page else if (hash === 'top') $window.scrollTo(0, 0); } // does not scroll when user clicks on anchor link that is currently on // (no url change, no $location.hash() change), browser native does scroll if (autoScrollingEnabled) { $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, function autoScrollWatchAction() { $rootScope.$evalAsync(scroll); }); } return scroll; }]; } var $animateMinErr = minErr('$animate'); /** * @ngdoc provider * @name $animateProvider * * @description * Default implementation of $animate that doesn't perform any animations, instead just * synchronously performs DOM * updates and calls done() callbacks. * * In order to enable animations the ngAnimate module has to be loaded. * * To see the functional implementation check out src/ngAnimate/animate.js */ var $AnimateProvider = ['$provide', function($provide) { this.$$selectors = {}; /** * @ngdoc method * @name $animateProvider#register * * @description * Registers a new injectable animation factory function. The factory function produces the * animation object which contains callback functions for each event that is expected to be * animated. * * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction` * must be called once the element animation is complete. If a function is returned then the * animation service will use this function to cancel the animation whenever a cancel event is * triggered. * * * ```js * return { * eventFn : function(element, done) { * //code to run the animation * //once complete, then run done() * return function cancellationFunction() { * //code to cancel the animation * } * } * } * ``` * * @param {string} name The name of the animation. * @param {Function} factory The factory function that will be executed to return the animation * object. */ this.register = function(name, factory) { var key = name + '-animation'; if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name); this.$$selectors[name.substr(1)] = key; $provide.factory(key, factory); }; /** * @ngdoc method * @name $animateProvider#classNameFilter * * @description * Sets and/or returns the CSS class regular expression that is checked when performing * an animation. Upon bootstrap the classNameFilter value is not set at all and will * therefore enable $animate to attempt to perform an animation on any element. * When setting the classNameFilter value, animations will only be performed on elements * that successfully match the filter expression. This in turn can boost performance * for low-powered devices as well as applications containing a lot of structural operations. * @param {RegExp=} expression The className expression which will be checked against all animations * @return {RegExp} The current CSS className expression value. If null then there is no expression value */ this.classNameFilter = function(expression) { if(arguments.length === 1) { this.$$classNameFilter = (expression instanceof RegExp) ? expression : null; } return this.$$classNameFilter; }; this.$get = ['$timeout', '$$asyncCallback', function($timeout, $$asyncCallback) { function async(fn) { fn && $$asyncCallback(fn); } /** * * @ngdoc service * @name $animate * @description The $animate service provides rudimentary DOM manipulation functions to * insert, remove and move elements within the DOM, as well as adding and removing classes. * This service is the core service used by the ngAnimate $animator service which provides * high-level animation hooks for CSS and JavaScript. * * $animate is available in the AngularJS core, however, the ngAnimate module must be included * to enable full out animation support. Otherwise, $animate will only perform simple DOM * manipulation operations. * * To learn more about enabling animation support, click here to visit the {@link ngAnimate * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service * page}. */ return { /** * * @ngdoc method * @name $animate#enter * @kind function * @description Inserts the element into the DOM either after the `after` element or within * the `parent` element. Once complete, the done() callback will be fired (if provided). * @param {DOMElement} element the element which will be inserted into the DOM * @param {DOMElement} parent the parent element which will append the element as * a child (if the after element is not present) * @param {DOMElement} after the sibling element which will append the element * after itself * @param {Function=} done callback function that will be called after the element has been * inserted into the DOM */ enter : function(element, parent, after, done) { if (after) { after.after(element); } else { if (!parent || !parent[0]) { parent = after.parent(); } parent.append(element); } async(done); }, /** * * @ngdoc method * @name $animate#leave * @kind function * @description Removes the element from the DOM. Once complete, the done() callback will be * fired (if provided). * @param {DOMElement} element the element which will be removed from the DOM * @param {Function=} done callback function that will be called after the element has been * removed from the DOM */ leave : function(element, done) { element.remove(); async(done); }, /** * * @ngdoc method * @name $animate#move * @kind function * @description Moves the position of the provided element within the DOM to be placed * either after the `after` element or inside of the `parent` element. Once complete, the * done() callback will be fired (if provided). * * @param {DOMElement} element the element which will be moved around within the * DOM * @param {DOMElement} parent the parent element where the element will be * inserted into (if the after element is not present) * @param {DOMElement} after the sibling element where the element will be * positioned next to * @param {Function=} done the callback function (if provided) that will be fired after the * element has been moved to its new position */ move : function(element, parent, after, done) { // Do not remove element before insert. Removing will cause data associated with the // element to be dropped. Insert will implicitly do the remove. this.enter(element, parent, after, done); }, /** * * @ngdoc method * @name $animate#addClass * @kind function * @description Adds the provided className CSS class value to the provided element. Once * complete, the done() callback will be fired (if provided). * @param {DOMElement} element the element which will have the className value * added to it * @param {string} className the CSS class which will be added to the element * @param {Function=} done the callback function (if provided) that will be fired after the * className value has been added to the element */ addClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { jqLiteAddClass(element, className); }); async(done); }, /** * * @ngdoc method * @name $animate#removeClass * @kind function * @description Removes the provided className CSS class value from the provided element. * Once complete, the done() callback will be fired (if provided). * @param {DOMElement} element the element which will have the className value * removed from it * @param {string} className the CSS class which will be removed from the element * @param {Function=} done the callback function (if provided) that will be fired after the * className value has been removed from the element */ removeClass : function(element, className, done) { className = isString(className) ? className : isArray(className) ? className.join(' ') : ''; forEach(element, function (element) { jqLiteRemoveClass(element, className); }); async(done); }, /** * * @ngdoc method * @name $animate#setClass * @kind function * @description Adds and/or removes the given CSS classes to and from the element. * Once complete, the done() callback will be fired (if provided). * @param {DOMElement} element the element which will have its CSS classes changed * removed from it * @param {string} add the CSS classes which will be added to the element * @param {string} remove the CSS class which will be removed from the element * @param {Function=} done the callback function (if provided) that will be fired after the * CSS classes have been set on the element */ setClass : function(element, add, remove, done) { forEach(element, function (element) { jqLiteAddClass(element, add); jqLiteRemoveClass(element, remove); }); async(done); }, enabled : noop }; }]; }]; function $$AsyncCallbackProvider(){ this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) { return $$rAF.supported ? function(fn) { return $$rAF(fn); } : function(fn) { return $timeout(fn, 0, false); }; }]; } /* global stripHash: true */ /** * ! This is a private undocumented service ! * * @name $browser * @requires $log * @description * This object has two goals: * * - hide all the global state in the browser caused by the window object * - abstract away all the browser specific features and inconsistencies * * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` * service, which can be used for convenient testing of the application without the interaction with * the real browser apis. */ /** * @param {object} window The global window object. * @param {object} document jQuery wrapped document. * @param {function()} XHR XMLHttpRequest constructor. * @param {object} $log console.log or an object with the same interface. * @param {object} $sniffer $sniffer service */ function Browser(window, document, $log, $sniffer) { var self = this, rawDocument = document[0], location = window.location, history = window.history, setTimeout = window.setTimeout, clearTimeout = window.clearTimeout, pendingDeferIds = {}; self.isMock = false; var outstandingRequestCount = 0; var outstandingRequestCallbacks = []; // TODO(vojta): remove this temporary api self.$$completeOutstandingRequest = completeOutstandingRequest; self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; /** * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. */ function completeOutstandingRequest(fn) { try { fn.apply(null, sliceArgs(arguments, 1)); } finally { outstandingRequestCount--; if (outstandingRequestCount === 0) { while(outstandingRequestCallbacks.length) { try { outstandingRequestCallbacks.pop()(); } catch (e) { $log.error(e); } } } } } /** * @private * Note: this method is used only by scenario runner * TODO(vojta): prefix this method with $$ ? * @param {function()} callback Function that will be called when no outstanding request */ self.notifyWhenNoOutstandingRequests = function(callback) { // force browser to execute all pollFns - this is needed so that cookies and other pollers fire // at some deterministic time in respect to the test runner's actions. Leaving things up to the // regular poller would result in flaky tests. forEach(pollFns, function(pollFn){ pollFn(); }); if (outstandingRequestCount === 0) { callback(); } else { outstandingRequestCallbacks.push(callback); } }; ////////////////////////////////////////////////////////////// // Poll Watcher API ////////////////////////////////////////////////////////////// var pollFns = [], pollTimeout; /** * @name $browser#addPollFn * * @param {function()} fn Poll function to add * * @description * Adds a function to the list of functions that poller periodically executes, * and starts polling if not started yet. * * @returns {function()} the added function */ self.addPollFn = function(fn) { if (isUndefined(pollTimeout)) startPoller(100, setTimeout); pollFns.push(fn); return fn; }; /** * @param {number} interval How often should browser call poll functions (ms) * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. * * @description * Configures the poller to run in the specified intervals, using the specified * setTimeout fn and kicks it off. */ function startPoller(interval, setTimeout) { (function check() { forEach(pollFns, function(pollFn){ pollFn(); }); pollTimeout = setTimeout(check, interval); })(); } ////////////////////////////////////////////////////////////// // URL API ////////////////////////////////////////////////////////////// var lastBrowserUrl = location.href, baseElement = document.find('base'), reloadLocation = null; /** * @name $browser#url * * @description * GETTER: * Without any argument, this method just returns current value of location.href. * * SETTER: * With at least one argument, this method sets url to new value. * If html5 history api supported, pushState/replaceState is used, otherwise * location.href/location.replace is used. * Returns its own instance to allow chaining * * NOTE: this api is intended for use only by the $location service. Please use the * {@link ng.$location $location service} to change url. * * @param {string} url New url (when used as setter) * @param {boolean=} replace Should new url replace current history record ? */ self.url = function(url, replace) { // Android Browser BFCache causes location, history reference to become stale. if (location !== window.location) location = window.location; if (history !== window.history) history = window.history; // setter if (url) { if (lastBrowserUrl == url) return; var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url); lastBrowserUrl = url; // Don't use history API if only the hash changed // due to a bug in IE10/IE11 which leads // to not firing a `hashchange` nor `popstate` event // in some cases (see #9143). if (!sameBase && $sniffer.history) { if (replace) history.replaceState(null, '', url); else { history.pushState(null, '', url); // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 baseElement.attr('href', baseElement.attr('href')); } } else { if (!sameBase) { reloadLocation = url; } if (replace) { location.replace(url); } else { location.href = url; } } return self; // getter } else { // - reloadLocation is needed as browsers don't allow to read out // the new location.href if a reload happened. // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 return reloadLocation || location.href.replace(/%27/g,"'"); } }; var urlChangeListeners = [], urlChangeInit = false; function fireUrlChange() { if (lastBrowserUrl == self.url()) return; lastBrowserUrl = self.url(); forEach(urlChangeListeners, function(listener) { listener(self.url()); }); } /** * @name $browser#onUrlChange * * @description * Register callback function that will be called, when url changes. * * It's only called when the url is changed from outside of angular: * - user types different url into address bar * - user clicks on history (forward/back) button * - user clicks on a link * * It's not called when url is changed by $browser.url() method * * The listener gets called with new url as parameter. * * NOTE: this api is intended for use only by the $location service. Please use the * {@link ng.$location $location service} to monitor url changes in angular apps. * * @param {function(string)} listener Listener function to be called when url changes. * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. */ self.onUrlChange = function(callback) { // TODO(vojta): refactor to use node's syntax for events if (!urlChangeInit) { // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) // don't fire popstate when user change the address bar and don't fire hashchange when url // changed by push/replaceState // html5 history api - popstate event if ($sniffer.history) jqLite(window).on('popstate', fireUrlChange); // hashchange event if ($sniffer.hashchange) jqLite(window).on('hashchange', fireUrlChange); // polling else self.addPollFn(fireUrlChange); urlChangeInit = true; } urlChangeListeners.push(callback); return callback; }; /** * Checks whether the url has changed outside of Angular. * Needs to be exported to be able to check for changes that have been done in sync, * as hashchange/popstate events fire in async. */ self.$$checkUrlChange = fireUrlChange; ////////////////////////////////////////////////////////////// // Misc API ////////////////////////////////////////////////////////////// /** * @name $browser#baseHref * * @description * Returns current * (always relative - without domain) * * @returns {string} The current base href */ self.baseHref = function() { var href = baseElement.attr('href'); return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : ''; }; ////////////////////////////////////////////////////////////// // Cookies API ////////////////////////////////////////////////////////////// var lastCookies = {}; var lastCookieString = ''; var cookiePath = self.baseHref(); /** * @name $browser#cookies * * @param {string=} name Cookie name * @param {string=} value Cookie value * * @description * The cookies method provides a 'private' low level access to browser cookies. * It is not meant to be used directly, use the $cookie service instead. * * The return values vary depending on the arguments that the method was called with as follows: * * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify * it * - cookies(name, value) -> set name to value, if value is undefined delete the cookie * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that * way) * * @returns {Object} Hash of all cookies (if called without any parameter) */ self.cookies = function(name, value) { /* global escape: false, unescape: false */ var cookieLength, cookieArray, cookie, i, index; if (name) { if (value === undefined) { rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; } else { if (isString(value)) { cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum: // - 300 cookies // - 20 cookies per unique domain // - 4096 bytes per cookie if (cookieLength > 4096) { $log.warn("Cookie '"+ name + "' possibly not set or overflowed because it was too large ("+ cookieLength + " > 4096 bytes)!"); } } } } else { if (rawDocument.cookie !== lastCookieString) { lastCookieString = rawDocument.cookie; cookieArray = lastCookieString.split("; "); lastCookies = {}; for (i = 0; i < cookieArray.length; i++) { cookie = cookieArray[i]; index = cookie.indexOf('='); if (index > 0) { //ignore nameless cookies name = unescape(cookie.substring(0, index)); // the first value that is seen for a cookie is the most // specific one. values for the same cookie name that // follow are for less specific paths. if (lastCookies[name] === undefined) { lastCookies[name] = unescape(cookie.substring(index + 1)); } } } } return lastCookies; } }; /** * @name $browser#defer * @param {function()} fn A function, who's execution should be deferred. * @param {number=} [delay=0] of milliseconds to defer the function execution. * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. * * @description * Executes a fn asynchronously via `setTimeout(fn, delay)`. * * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed * via `$browser.defer.flush()`. * */ self.defer = function(fn, delay) { var timeoutId; outstandingRequestCount++; timeoutId = setTimeout(function() { delete pendingDeferIds[timeoutId]; completeOutstandingRequest(fn); }, delay || 0); pendingDeferIds[timeoutId] = true; return timeoutId; }; /** * @name $browser#defer.cancel * * @description * Cancels a deferred task identified with `deferId`. * * @param {*} deferId Token returned by the `$browser.defer` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */ self.defer.cancel = function(deferId) { if (pendingDeferIds[deferId]) { delete pendingDeferIds[deferId]; clearTimeout(deferId); completeOutstandingRequest(noop); return true; } return false; }; } function $BrowserProvider(){ this.$get = ['$window', '$log', '$sniffer', '$document', function( $window, $log, $sniffer, $document){ return new Browser($window, $document, $log, $sniffer); }]; } /** * @ngdoc service * @name $cacheFactory * * @description * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to * them. * * ```js * * var cache = $cacheFactory('cacheId'); * expect($cacheFactory.get('cacheId')).toBe(cache); * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined(); * * cache.put("key", "value"); * cache.put("another key", "another value"); * * // We've specified no options on creation * expect(cache.info()).toEqual({id: 'cacheId', size: 2}); * * ``` * * * @param {string} cacheId Name or id of the newly created cache. * @param {object=} options Options object that specifies the cache behavior. Properties: * * - `{number=}` `capacity` — turns the cache into LRU cache. * * @returns {object} Newly created cache object with the following set of methods: * * - `{object}` `info()` — Returns id, size, and options of cache. * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns * it. * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. * - `{void}` `removeAll()` — Removes all cached values. * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. * * @example

    Cached Values

    :

    Cache Info

    :
    angular.module('cacheExampleApp', []). controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) { $scope.keys = []; $scope.cache = $cacheFactory('cacheId'); $scope.put = function(key, value) { if ($scope.cache.get(key) === undefined) { $scope.keys.push(key); } $scope.cache.put(key, value === undefined ? null : value); }; }]); p { margin: 10px 0 3px; }
    */ function $CacheFactoryProvider() { this.$get = function() { var caches = {}; function cacheFactory(cacheId, options) { if (cacheId in caches) { throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId); } var size = 0, stats = extend({}, options, {id: cacheId}), data = {}, capacity = (options && options.capacity) || Number.MAX_VALUE, lruHash = {}, freshEnd = null, staleEnd = null; /** * @ngdoc type * @name $cacheFactory.Cache * * @description * A cache object used to store and retrieve data, primarily used by * {@link $http $http} and the {@link ng.directive:script script} directive to cache * templates and other data. * * ```js * angular.module('superCache') * .factory('superCache', ['$cacheFactory', function($cacheFactory) { * return $cacheFactory('super-cache'); * }]); * ``` * * Example test: * * ```js * it('should behave like a cache', inject(function(superCache) { * superCache.put('key', 'value'); * superCache.put('another key', 'another value'); * * expect(superCache.info()).toEqual({ * id: 'super-cache', * size: 2 * }); * * superCache.remove('another key'); * expect(superCache.get('another key')).toBeUndefined(); * * superCache.removeAll(); * expect(superCache.info()).toEqual({ * id: 'super-cache', * size: 0 * }); * })); * ``` */ return caches[cacheId] = { /** * @ngdoc method * @name $cacheFactory.Cache#put * @kind function * * @description * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be * retrieved later, and incrementing the size of the cache if the key was not already * present in the cache. If behaving like an LRU cache, it will also remove stale * entries from the set. * * It will not insert undefined values into the cache. * * @param {string} key the key under which the cached data is stored. * @param {*} value the value to store alongside the key. If it is undefined, the key * will not be stored. * @returns {*} the value stored. */ put: function(key, value) { if (capacity < Number.MAX_VALUE) { var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); refresh(lruEntry); } if (isUndefined(value)) return; if (!(key in data)) size++; data[key] = value; if (size > capacity) { this.remove(staleEnd.key); } return value; }, /** * @ngdoc method * @name $cacheFactory.Cache#get * @kind function * * @description * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object. * * @param {string} key the key of the data to be retrieved * @returns {*} the value stored. */ get: function(key) { if (capacity < Number.MAX_VALUE) { var lruEntry = lruHash[key]; if (!lruEntry) return; refresh(lruEntry); } return data[key]; }, /** * @ngdoc method * @name $cacheFactory.Cache#remove * @kind function * * @description * Removes an entry from the {@link $cacheFactory.Cache Cache} object. * * @param {string} key the key of the entry to be removed */ remove: function(key) { if (capacity < Number.MAX_VALUE) { var lruEntry = lruHash[key]; if (!lruEntry) return; if (lruEntry == freshEnd) freshEnd = lruEntry.p; if (lruEntry == staleEnd) staleEnd = lruEntry.n; link(lruEntry.n,lruEntry.p); delete lruHash[key]; } delete data[key]; size--; }, /** * @ngdoc method * @name $cacheFactory.Cache#removeAll * @kind function * * @description * Clears the cache object of any entries. */ removeAll: function() { data = {}; size = 0; lruHash = {}; freshEnd = staleEnd = null; }, /** * @ngdoc method * @name $cacheFactory.Cache#destroy * @kind function * * @description * Destroys the {@link $cacheFactory.Cache Cache} object entirely, * removing it from the {@link $cacheFactory $cacheFactory} set. */ destroy: function() { data = null; stats = null; lruHash = null; delete caches[cacheId]; }, /** * @ngdoc method * @name $cacheFactory.Cache#info * @kind function * * @description * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}. * * @returns {object} an object with the following properties: *
      *
    • **id**: the id of the cache instance
    • *
    • **size**: the number of entries kept in the cache instance
    • *
    • **...**: any additional properties from the options object when creating the * cache.
    • *
    */ info: function() { return extend({}, stats, {size: size}); } }; /** * makes the `entry` the freshEnd of the LRU linked list */ function refresh(entry) { if (entry != freshEnd) { if (!staleEnd) { staleEnd = entry; } else if (staleEnd == entry) { staleEnd = entry.n; } link(entry.n, entry.p); link(entry, freshEnd); freshEnd = entry; freshEnd.n = null; } } /** * bidirectionally links two entries of the LRU linked list */ function link(nextEntry, prevEntry) { if (nextEntry != prevEntry) { if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify } } } /** * @ngdoc method * @name $cacheFactory#info * * @description * Get information about all the caches that have been created * * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info` */ cacheFactory.info = function() { var info = {}; forEach(caches, function(cache, cacheId) { info[cacheId] = cache.info(); }); return info; }; /** * @ngdoc method * @name $cacheFactory#get * * @description * Get access to a cache object by the `cacheId` used when it was created. * * @param {string} cacheId Name or id of a cache to access. * @returns {object} Cache object identified by the cacheId or undefined if no such cache. */ cacheFactory.get = function(cacheId) { return caches[cacheId]; }; return cacheFactory; }; } /** * @ngdoc service * @name $templateCache * * @description * The first time a template is used, it is loaded in the template cache for quick retrieval. You * can load templates directly into the cache in a `script` tag, or by consuming the * `$templateCache` service directly. * * Adding via the `script` tag: * * ```html * * ``` * * **Note:** the `script` tag containing the template does not need to be included in the `head` of * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE, * element with ng-app attribute), otherwise the template will be ignored. * * Adding via the $templateCache service: * * ```js * var myApp = angular.module('myApp', []); * myApp.run(function($templateCache) { * $templateCache.put('templateId.html', 'This is the content of the template'); * }); * ``` * * To retrieve the template later, simply use it in your HTML: * ```html *
    * ``` * * or get it via Javascript: * ```js * $templateCache.get('templateId.html') * ``` * * See {@link ng.$cacheFactory $cacheFactory}. * */ function $TemplateCacheProvider() { this.$get = ['$cacheFactory', function($cacheFactory) { return $cacheFactory('templates'); }]; } /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! * * DOM-related variables: * * - "node" - DOM Node * - "element" - DOM Element or Node * - "$node" or "$element" - jqLite-wrapped node or element * * * Compiler related stuff: * * - "linkFn" - linking fn of a single directive * - "nodeLinkFn" - function that aggregates all linking fns for a particular node * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) */ /** * @ngdoc service * @name $compile * @kind function * * @description * Compiles an HTML string or DOM into a template and produces a template function, which * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together. * * The compilation is a process of walking the DOM tree and matching DOM elements to * {@link ng.$compileProvider#directive directives}. * *
    * **Note:** This document is an in-depth reference of all directive options. * For a gentle introduction to directives with examples of common use cases, * see the {@link guide/directive directive guide}. *
    * * ## Comprehensive Directive API * * There are many different options for a directive. * * The difference resides in the return value of the factory function. * You can either return a "Directive Definition Object" (see below) that defines the directive properties, * or just the `postLink` function (all other properties will have the default values). * *
    * **Best Practice:** It's recommended to use the "directive definition object" form. *
    * * Here's an example directive declared with a Directive Definition Object: * * ```js * var myModule = angular.module(...); * * myModule.directive('directiveName', function factory(injectables) { * var directiveDefinitionObject = { * priority: 0, * template: '
    ', // or // function(tElement, tAttrs) { ... }, * // or * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... }, * transclude: false, * restrict: 'A', * scope: false, * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... }, * controllerAs: 'stringAlias', * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'], * compile: function compile(tElement, tAttrs, transclude) { * return { * pre: function preLink(scope, iElement, iAttrs, controller) { ... }, * post: function postLink(scope, iElement, iAttrs, controller) { ... } * } * // or * // return function postLink( ... ) { ... } * }, * // or * // link: { * // pre: function preLink(scope, iElement, iAttrs, controller) { ... }, * // post: function postLink(scope, iElement, iAttrs, controller) { ... } * // } * // or * // link: function postLink( ... ) { ... } * }; * return directiveDefinitionObject; * }); * ``` * *
    * **Note:** Any unspecified options will use the default value. You can see the default values below. *
    * * Therefore the above can be simplified as: * * ```js * var myModule = angular.module(...); * * myModule.directive('directiveName', function factory(injectables) { * var directiveDefinitionObject = { * link: function postLink(scope, iElement, iAttrs) { ... } * }; * return directiveDefinitionObject; * // or * // return function postLink(scope, iElement, iAttrs) { ... } * }); * ``` * * * * ### Directive Definition Object * * The directive definition object provides instructions to the {@link ng.$compile * compiler}. The attributes are: * * #### `priority` * When there are multiple directives defined on a single DOM element, sometimes it * is necessary to specify the order in which the directives are applied. The `priority` is used * to sort the directives before their `compile` functions get called. Priority is defined as a * number. Directives with greater numerical `priority` are compiled first. Pre-link functions * are also run in priority order, but post-link functions are run in reverse order. The order * of directives with the same priority is undefined. The default priority is `0`. * * #### `terminal` * If set to true then the current `priority` will be the last set of directives * which will execute (any directives at the current priority will still execute * as the order of execution on same `priority` is undefined). * * #### `scope` * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the * same element request a new scope, only one new scope is created. The new scope rule does not * apply for the root of the template since the root of the template always gets a new scope. * * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from * normal scope in that it does not prototypically inherit from the parent scope. This is useful * when creating reusable components, which should not accidentally read or modify data in the * parent scope. * * The 'isolate' scope takes an object hash which defines a set of local scope properties * derived from the parent scope. These local properties are useful for aliasing values for * templates. Locals definition is a hash of local scope property to its source: * * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is * always a string since DOM attributes are strings. If no `attr` name is specified then the * attribute name is assumed to be the same as the local name. * Given `` and widget definition * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the * `localName` property on the widget scope. The `name` is read from the parent scope (not * component scope). * * * `=` or `=attr` - set up bi-directional binding between a local scope property and the * parent scope property of name defined via the value of the `attr` attribute. If no `attr` * name is specified then the attribute name is assumed to be the same as the local name. * Given `` and widget definition of * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. * * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. * If no `attr` name is specified then the attribute name is assumed to be the same as the * local name. Given `` and widget definition of * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to * a function wrapper for the `count = count + value` expression. Often it's desirable to * pass data from the isolated scope via an expression to the parent scope, this can be * done by passing a map of local variable names and values into the expression wrapper fn. * For example, if the expression is `increment(amount)` then we can specify the amount value * by calling the `localFn` as `localFn({amount: 22})`. * * * * #### `controller` * Controller constructor function. The controller is instantiated before the * pre-linking phase and it is shared with other directives (see * `require` attribute). This allows the directives to communicate with each other and augment * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals: * * * `$scope` - Current scope associated with the element * * `$element` - Current element * * `$attrs` - Current attributes object for the element * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope. * The scope can be overridden by an optional first argument. * `function([scope], cloneLinkingFn)`. * * * #### `require` * Require another directive and inject its controller as the fourth argument to the linking function. The * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the * injected argument will be an array in corresponding order. If no such directive can be * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with: * * * (no prefix) - Locate the required controller on the current element. Throw an error if not found. * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found. * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found. * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass * `null` to the `link` fn if not found. * * * #### `controllerAs` * Controller alias at the directive scope. An alias for the controller so it * can be referenced at the directive template. The directive needs to define a scope for this * configuration to be used. Useful in the case when directive is used as component. * * * #### `restrict` * String of subset of `EACM` which restricts the directive to a specific directive * declaration style. If omitted, the default (attributes only) is used. * * * `E` - Element name: `` * * `A` - Attribute (default): `
    ` * * `C` - Class: `
    ` * * `M` - Comment: `` * * * #### `template` * HTML markup that may: * * Replace the contents of the directive's element (default). * * Replace the directive's element itself (if `replace` is true - DEPRECATED). * * Wrap the contents of the directive's element (if `transclude` is true). * * Value may be: * * * A string. For example `
    {{delete_str}}
    `. * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile` * function api below) and returns a string value. * * * #### `templateUrl` * Same as `template` but the template is loaded from the specified URL. Because * the template loading is asynchronous the compilation/linking is suspended until the template * is loaded. * * You can specify `templateUrl` as a string representing the URL or as a function which takes two * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns * a string value representing the url. In either case, the template URL is passed through {@link * api/ng.$sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}. * * * #### `replace` ([*DEPRECATED*!], will be removed in next major release) * specify what the template should replace. Defaults to `false`. * * * `true` - the template will replace the directive's element. * * `false` - the template will replace the contents of the directive's element. * * The replacement process migrates all of the attributes / classes from the old element to the new * one. See the {@link guide/directive#creating-custom-directives_creating-directives_template-expanding-directive * Directives Guide} for an example. * * #### `transclude` * compile the content of the element and make it available to the directive. * Typically used with {@link ng.directive:ngTransclude * ngTransclude}. The advantage of transclusion is that the linking function receives a * transclusion function which is pre-bound to the correct scope. In a typical setup the widget * creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate` * scope. This makes it possible for the widget to have private state, and the transclusion to * be bound to the parent (pre-`isolate`) scope. * * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the * directive's element or the entire element: * * * `true` - transclude the content (i.e. the child nodes) of the directive's element. * * `'element'` - transclude the whole of the directive's element including any directives on this * element that defined at a lower priority than this directive. When used, the `template` * property is ignored. * *
    * **Note:** When testing an element transclude directive you must not place the directive at the root of the * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives * Testing Transclusion Directives}. *
    * * #### `compile` * * ```js * function compile(tElement, tAttrs, transclude) { ... } * ``` * * The compile function deals with transforming the template DOM. Since most directives do not do * template transformation, it is not used often. The compile function takes the following arguments: * * * `tElement` - template element - The element where the directive has been declared. It is * safe to do template transformation on the element and child elements only. * * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared * between all directive compile functions. * * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)` * *
    * **Note:** The template instance and the link instance may be different objects if the template has * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration * should be done in a linking function rather than in a compile function. *
    *
    * **Note:** The compile function cannot handle directives that recursively use themselves in their * own templates or compile functions. Compiling these directives results in an infinite loop and a * stack overflow errors. * * This can be avoided by manually using $compile in the postLink function to imperatively compile * a directive's template instead of relying on automatic template compilation via `template` or * `templateUrl` declaration or manual compilation inside the compile function. *
    * *
    * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it * e.g. does not know about the right outer scope. Please use the transclude function that is passed * to the link function instead. *
    * A compile function can have a return value which can be either a function or an object. * * * returning a (post-link) function - is equivalent to registering the linking function via the * `link` property of the config object when the compile function is empty. * * * returning an object with function(s) registered via `pre` and `post` properties - allows you to * control when a linking function should be called during the linking phase. See info about * pre-linking and post-linking functions below. * * * #### `link` * This property is used only if the `compile` property is not defined. * * ```js * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... } * ``` * * The link function is responsible for registering DOM listeners as well as updating the DOM. It is * executed after the template has been cloned. This is where most of the directive logic will be * put. * * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the * directive for registering {@link ng.$rootScope.Scope#$watch watches}. * * * `iElement` - instance element - The element where the directive is to be used. It is safe to * manipulate the children of the element only in `postLink` function since the children have * already been linked. * * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared * between all directive linking functions. * * * `controller` - a controller instance - A controller instance if at least one directive on the * element defines a controller. The controller is shared among all the directives, which allows * the directives to use the controllers as a communication channel. * * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope. * The scope can be overridden by an optional first argument. This is the same as the `$transclude` * parameter of directive controllers. * `function([scope], cloneLinkingFn)`. * * * #### Pre-linking function * * Executed before the child elements are linked. Not safe to do DOM transformation since the * compiler linking function will fail to locate the correct elements for linking. * * #### Post-linking function * * Executed after the child elements are linked. It is safe to do DOM transformation in the post-linking function. * * * ### Attributes * * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the * `link()` or `compile()` functions. It has a variety of uses. * * accessing *Normalized attribute names:* * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. * the attributes object allows for normalized access to * the attributes. * * * *Directive inter-communication:* All directives share the same instance of the attributes * object which allows the directives to use the attributes object as inter directive * communication. * * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object * allowing other directives to read the interpolated value. * * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also * the only way to easily get the actual value because during the linking phase the interpolation * hasn't been evaluated yet and so the value is at this time set to `undefined`. * * ```js * function linkingFn(scope, elm, attrs, ctrl) { * // get the attribute value * console.log(attrs.ngModel); * * // change the attribute * attrs.$set('ngModel', 'new value'); * * // observe changes to interpolated attribute * attrs.$observe('ngModel', function(value) { * console.log('ngModel has changed value to ' + value); * }); * } * ``` * * ## Example * *
    * **Note**: Typically directives are registered with `module.directive`. The example below is * to illustrate how `$compile` works. *
    *


    it('should auto compile', function() { var textarea = $('textarea'); var output = $('div[compile]'); // The initial state reads 'Hello Angular'. expect(output.getText()).toBe('Hello Angular'); textarea.clear(); textarea.sendKeys('{{name}}!'); expect(output.getText()).toBe('Angular!'); });
    * * * @param {string|DOMElement} element Element or HTML string to compile into a template function. * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives. * @param {number} maxPriority only apply directives lower than given priority (Only effects the * root element(s), not their children) * @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template * (a DOM element/tree) to a scope. Where: * * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the * `template` and call the `cloneAttachFn` function allowing the caller to attach the * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is * called as:
    `cloneAttachFn(clonedElement, scope)` where: * * * `clonedElement` - is a clone of the original `element` passed into the compiler. * * `scope` - is the current scope with which the linking function is working with. * * Calling the linking function returns the element of the template. It is either the original * element passed in, or the clone of the element if the `cloneAttachFn` is provided. * * After linking the view is not updated until after a call to $digest which typically is done by * Angular automatically. * * If you need access to the bound view, there are two ways to do it: * * - If you are not asking the linking function to clone the template, create the DOM element(s) * before you send them to the compiler and keep this reference around. * ```js * var element = $compile('

    {{total}}

    ')(scope); * ``` * * - if on the other hand, you need the element to be cloned, the view reference from the original * example would not point to the clone, but rather to the original template that was cloned. In * this case, you can access the clone via the cloneAttachFn: * ```js * var templateElement = angular.element('

    {{total}}

    '), * scope = ....; * * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) { * //attach the clone to DOM document at the right place * }); * * //now we have reference to the cloned DOM via `clonedElement` * ``` * * * For information on how the compiler works, see the * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. */ var $compileMinErr = minErr('$compile'); /** * @ngdoc provider * @name $compileProvider * @kind function * * @description */ $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider']; function $CompileProvider($provide, $$sanitizeUriProvider) { var hasDirectives = {}, Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/, CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/; // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes // The assumption is that future DOM event attribute names will begin with // 'on' and be composed of only English letters. var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/; /** * @ngdoc method * @name $compileProvider#directive * @kind function * * @description * Register a new directive with the compiler. * * @param {string|Object} name Name of the directive in camel-case (i.e. ngBind which * will match as ng-bind), or an object map of directives where the keys are the * names and the values are the factories. * @param {Function|Array} directiveFactory An injectable directive factory function. See * {@link guide/directive} for more info. * @returns {ng.$compileProvider} Self for chaining. */ this.directive = function registerDirective(name, directiveFactory) { assertNotHasOwnProperty(name, 'directive'); if (isString(name)) { assertArg(directiveFactory, 'directiveFactory'); if (!hasDirectives.hasOwnProperty(name)) { hasDirectives[name] = []; $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', function($injector, $exceptionHandler) { var directives = []; forEach(hasDirectives[name], function(directiveFactory, index) { try { var directive = $injector.invoke(directiveFactory); if (isFunction(directive)) { directive = { compile: valueFn(directive) }; } else if (!directive.compile && directive.link) { directive.compile = valueFn(directive.link); } directive.priority = directive.priority || 0; directive.index = index; directive.name = directive.name || name; directive.require = directive.require || (directive.controller && directive.name); directive.restrict = directive.restrict || 'A'; directives.push(directive); } catch (e) { $exceptionHandler(e); } }); return directives; }]); } hasDirectives[name].push(directiveFactory); } else { forEach(name, reverseParams(registerDirective)); } return this; }; /** * @ngdoc method * @name $compileProvider#aHrefSanitizationWhitelist * @kind function * * @description * Retrieves or overrides the default regular expression that is used for whitelisting of safe * urls during a[href] sanitization. * * The sanitization is a security measure aimed at prevent XSS attacks via html links. * * Any url about to be assigned to a[href] via data-binding is first normalized and turned into * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist` * regular expression. If a match is found, the original url is written into the dom. Otherwise, * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. * * @param {RegExp=} regexp New regexp to whitelist urls with. * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for * chaining otherwise. */ this.aHrefSanitizationWhitelist = function(regexp) { if (isDefined(regexp)) { $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp); return this; } else { return $$sanitizeUriProvider.aHrefSanitizationWhitelist(); } }; /** * @ngdoc method * @name $compileProvider#imgSrcSanitizationWhitelist * @kind function * * @description * Retrieves or overrides the default regular expression that is used for whitelisting of safe * urls during img[src] sanitization. * * The sanitization is a security measure aimed at prevent XSS attacks via html links. * * Any url about to be assigned to img[src] via data-binding is first normalized and turned into * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist` * regular expression. If a match is found, the original url is written into the dom. Otherwise, * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM. * * @param {RegExp=} regexp New regexp to whitelist urls with. * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for * chaining otherwise. */ this.imgSrcSanitizationWhitelist = function(regexp) { if (isDefined(regexp)) { $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp); return this; } else { return $$sanitizeUriProvider.imgSrcSanitizationWhitelist(); } }; this.$get = [ '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri', function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) { var Attributes = function(element, attr) { this.$$element = element; this.$attr = attr || {}; }; Attributes.prototype = { /** * @ngdoc method * @name $compile.directive.Attributes#$normalize * @kind function * * @description * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or * `data-`) to its normalized, camelCase form. * * Also there is special case for Moz prefix starting with upper case letter. * * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives} * * @param {string} name Name to normalize */ $normalize: directiveNormalize, /** * @ngdoc method * @name $compile.directive.Attributes#$addClass * @kind function * * @description * Adds the CSS class value specified by the classVal parameter to the element. If animations * are enabled then an animation will be triggered for the class addition. * * @param {string} classVal The className value that will be added to the element */ $addClass : function(classVal) { if(classVal && classVal.length > 0) { $animate.addClass(this.$$element, classVal); } }, /** * @ngdoc method * @name $compile.directive.Attributes#$removeClass * @kind function * * @description * Removes the CSS class value specified by the classVal parameter from the element. If * animations are enabled then an animation will be triggered for the class removal. * * @param {string} classVal The className value that will be removed from the element */ $removeClass : function(classVal) { if(classVal && classVal.length > 0) { $animate.removeClass(this.$$element, classVal); } }, /** * @ngdoc method * @name $compile.directive.Attributes#$updateClass * @kind function * * @description * Adds and removes the appropriate CSS class values to the element based on the difference * between the new and old CSS class values (specified as newClasses and oldClasses). * * @param {string} newClasses The current CSS className value * @param {string} oldClasses The former CSS className value */ $updateClass : function(newClasses, oldClasses) { var toAdd = tokenDifference(newClasses, oldClasses); var toRemove = tokenDifference(oldClasses, newClasses); if(toAdd.length === 0) { $animate.removeClass(this.$$element, toRemove); } else if(toRemove.length === 0) { $animate.addClass(this.$$element, toAdd); } else { $animate.setClass(this.$$element, toAdd, toRemove); } }, /** * Set a normalized attribute on the element in a way such that all directives * can share the attribute. This function properly handles boolean attributes. * @param {string} key Normalized key. (ie ngAttribute) * @param {string|boolean} value The value to set. If `null` attribute will be deleted. * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. * Defaults to true. * @param {string=} attrName Optional none normalized name. Defaults to key. */ $set: function(key, value, writeAttr, attrName) { // TODO: decide whether or not to throw an error if "class" //is set through this function since it may cause $updateClass to //become unstable. var booleanKey = getBooleanAttrName(this.$$element[0], key), normalizedVal, nodeName; if (booleanKey) { this.$$element.prop(key, value); attrName = booleanKey; } this[key] = value; // translate normalized key to actual key if (attrName) { this.$attr[key] = attrName; } else { attrName = this.$attr[key]; if (!attrName) { this.$attr[key] = attrName = snake_case(key, '-'); } } nodeName = nodeName_(this.$$element); // sanitize a[href] and img[src] values if ((nodeName === 'A' && key === 'href') || (nodeName === 'IMG' && key === 'src')) { this[key] = value = $$sanitizeUri(value, key === 'src'); } if (writeAttr !== false) { if (value === null || value === undefined) { this.$$element.removeAttr(attrName); } else { this.$$element.attr(attrName, value); } } // fire observers var $$observers = this.$$observers; $$observers && forEach($$observers[key], function(fn) { try { fn(value); } catch (e) { $exceptionHandler(e); } }); }, /** * @ngdoc method * @name $compile.directive.Attributes#$observe * @kind function * * @description * Observes an interpolated attribute. * * The observer function will be invoked once during the next `$digest` following * compilation. The observer is then invoked whenever the interpolated value * changes. * * @param {string} key Normalized key. (ie ngAttribute) . * @param {function(interpolatedValue)} fn Function that will be called whenever the interpolated value of the attribute changes. * See the {@link guide/directive#Attributes Directives} guide for more info. * @returns {function()} the `fn` parameter. */ $observe: function(key, fn) { var attrs = this, $$observers = (attrs.$$observers || (attrs.$$observers = {})), listeners = ($$observers[key] || ($$observers[key] = [])); listeners.push(fn); $rootScope.$evalAsync(function() { if (!listeners.$$inter) { // no one registered attribute interpolation function, so lets call it manually fn(attrs[key]); } }); return fn; } }; var startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') ? identity : function denormalizeTemplate(template) { return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); }, NG_ATTR_BINDING = /^ngAttr[A-Z]/; return compile; //================================ function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) { if (!($compileNodes instanceof jqLite)) { // jquery always rewraps, whereas we need to preserve the original selector so that we can // modify it. $compileNodes = jqLite($compileNodes); } // We can not compile top level text elements since text nodes can be merged and we will // not be able to attach scope data to them, so we will wrap them in forEach($compileNodes, function(node, index){ if (node.nodeType == 3 /* text node */ && node.nodeValue.match(/\S+/) /* non-empty */ ) { $compileNodes[index] = node = jqLite(node).wrap('').parent()[0]; } }); var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority, ignoreDirective, previousCompileContext); safeAddClass($compileNodes, 'ng-scope'); return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn){ assertArg(scope, 'scope'); // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. var $linkNode = cloneConnectFn ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! : $compileNodes; forEach(transcludeControllers, function(instance, name) { $linkNode.data('$' + name + 'Controller', instance); }); // Attach scope only to non-text nodes. for(var i = 0, ii = $linkNode.length; i addDirective(directives, directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority, ignoreDirective); // iterate over the attributes for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes, j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { var attrStartName = false; var attrEndName = false; attr = nAttrs[j]; if (!msie || msie >= 8 || attr.specified) { name = attr.name; value = trim(attr.value); // support ngAttr attribute binding ngAttrName = directiveNormalize(name); if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) { name = snake_case(ngAttrName.substr(6), '-'); } var directiveNName = ngAttrName.replace(/(Start|End)$/, ''); if (ngAttrName === directiveNName + 'Start') { attrStartName = name; attrEndName = name.substr(0, name.length - 5) + 'end'; name = name.substr(0, name.length - 6); } nName = directiveNormalize(name.toLowerCase()); attrsMap[nName] = name; if (isNgAttr || !attrs.hasOwnProperty(nName)) { attrs[nName] = value; if (getBooleanAttrName(node, nName)) { attrs[nName] = true; // presence means true } } addAttrInterpolateDirective(node, directives, value, nName); addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName, attrEndName); } } // use class as directive className = node.className; if (isString(className) && className !== '') { while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { nName = directiveNormalize(match[2]); if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) { attrs[nName] = trim(match[3]); } className = className.substr(match.index + match[0].length); } } break; case 3: /* Text Node */ addTextInterpolateDirective(directives, node.nodeValue); break; case 8: /* Comment */ try { match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); if (match) { nName = directiveNormalize(match[1]); if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) { attrs[nName] = trim(match[2]); } } } catch (e) { // turns out that under some circumstances IE9 throws errors when one attempts to read // comment's node value. // Just ignore it and continue. (Can't seem to reproduce in test case.) } break; } directives.sort(byPriority); return directives; } /** * Given a node with an directive-start it collects all of the siblings until it finds * directive-end. * @param node * @param attrStart * @param attrEnd * @returns {*} */ function groupScan(node, attrStart, attrEnd) { var nodes = []; var depth = 0; if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) { var startNode = node; do { if (!node) { throw $compileMinErr('uterdir', "Unterminated attribute, found '{0}' but no matching '{1}' found.", attrStart, attrEnd); } if (node.nodeType == 1 /** Element **/) { if (node.hasAttribute(attrStart)) depth++; if (node.hasAttribute(attrEnd)) depth--; } nodes.push(node); node = node.nextSibling; } while (depth > 0); } else { nodes.push(node); } return jqLite(nodes); } /** * Wrapper for linking function which converts normal linking function into a grouped * linking function. * @param linkFn * @param attrStart * @param attrEnd * @returns {Function} */ function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) { return function(scope, element, attrs, controllers, transcludeFn) { element = groupScan(element[0], attrStart, attrEnd); return linkFn(scope, element, attrs, controllers, transcludeFn); }; } /** * Once the directives have been collected, their compile functions are executed. This method * is responsible for inlining directive templates as well as terminating the application * of the directives if the terminal directive has been reached. * * @param {Array} directives Array of collected directives to execute their compile function. * this needs to be pre-sorted by priority order. * @param {Node} compileNode The raw DOM node to apply the compile functions to * @param {Object} templateAttrs The shared attribute function * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the * scope argument is auto-generated to the new * child of the transcluded parent scope. * @param {JQLite} jqCollection If we are working on the root of the compile tree then this * argument has the root jqLite array so that we can replace nodes * on it. * @param {Object=} originalReplaceDirective An optional directive that will be ignored when * compiling the transclusion. * @param {Array.} preLinkFns * @param {Array.} postLinkFns * @param {Object} previousCompileContext Context used for previous compilation of the current * node * @returns {Function} linkFn */ function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, jqCollection, originalReplaceDirective, preLinkFns, postLinkFns, previousCompileContext) { previousCompileContext = previousCompileContext || {}; var terminalPriority = -Number.MAX_VALUE, newScopeDirective, controllerDirectives = previousCompileContext.controllerDirectives, newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective, templateDirective = previousCompileContext.templateDirective, nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective, hasTranscludeDirective = false, hasTemplate = false, hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective, $compileNode = templateAttrs.$$element = jqLite(compileNode), directive, directiveName, $template, replaceDirective = originalReplaceDirective, childTranscludeFn = transcludeFn, linkFn, directiveValue; // executes all directives on the current element for(var i = 0, ii = directives.length; i < ii; i++) { directive = directives[i]; var attrStart = directive.$$start; var attrEnd = directive.$$end; // collect multiblock sections if (attrStart) { $compileNode = groupScan(compileNode, attrStart, attrEnd); } $template = undefined; if (terminalPriority > directive.priority) { break; // prevent further processing of directives } if (directiveValue = directive.scope) { newScopeDirective = newScopeDirective || directive; // skip the check for directives with async templates, we'll check the derived sync // directive when the template arrives if (!directive.templateUrl) { assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive, $compileNode); if (isObject(directiveValue)) { newIsolateScopeDirective = directive; } } } directiveName = directive.name; if (!directive.templateUrl && directive.controller) { directiveValue = directive.controller; controllerDirectives = controllerDirectives || {}; assertNoDuplicate("'" + directiveName + "' controller", controllerDirectives[directiveName], directive, $compileNode); controllerDirectives[directiveName] = directive; } if (directiveValue = directive.transclude) { hasTranscludeDirective = true; // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion. // This option should only be used by directives that know how to safely handle element transclusion, // where the transcluded nodes are added or replaced after linking. if (!directive.$$tlb) { assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode); nonTlbTranscludeDirective = directive; } if (directiveValue == 'element') { hasElementTranscludeDirective = true; terminalPriority = directive.priority; $template = $compileNode; $compileNode = templateAttrs.$$element = jqLite(document.createComment(' ' + directiveName + ': ' + templateAttrs[directiveName] + ' ')); compileNode = $compileNode[0]; replaceWith(jqCollection, sliceArgs($template), compileNode); childTranscludeFn = compile($template, transcludeFn, terminalPriority, replaceDirective && replaceDirective.name, { // Don't pass in: // - controllerDirectives - otherwise we'll create duplicates controllers // - newIsolateScopeDirective or templateDirective - combining templates with // element transclusion doesn't make sense. // // We need only nonTlbTranscludeDirective so that we prevent putting transclusion // on the same element more than once. nonTlbTranscludeDirective: nonTlbTranscludeDirective }); } else { $template = jqLite(jqLiteClone(compileNode)).contents(); $compileNode.empty(); // clear contents childTranscludeFn = compile($template, transcludeFn); } } if (directive.template) { hasTemplate = true; assertNoDuplicate('template', templateDirective, directive, $compileNode); templateDirective = directive; directiveValue = (isFunction(directive.template)) ? directive.template($compileNode, templateAttrs) : directive.template; directiveValue = denormalizeTemplate(directiveValue); if (directive.replace) { replaceDirective = directive; if (jqLiteIsTextNode(directiveValue)) { $template = []; } else { $template = jqLite(trim(directiveValue)); } compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { throw $compileMinErr('tplrt', "Template for directive '{0}' must have exactly one root element. {1}", directiveName, ''); } replaceWith(jqCollection, $compileNode, compileNode); var newTemplateAttrs = {$attr: {}}; // combine directives from the original node and from the template: // - take the array of directives for this element // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed) // - collect directives from the template and sort them by priority // - combine directives as: processed + template + unprocessed var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs); var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1)); if (newIsolateScopeDirective) { markDirectivesAsIsolate(templateDirectives); } directives = directives.concat(templateDirectives).concat(unprocessedDirectives); mergeTemplateAttributes(templateAttrs, newTemplateAttrs); ii = directives.length; } else { $compileNode.html(directiveValue); } } if (directive.templateUrl) { hasTemplate = true; assertNoDuplicate('template', templateDirective, directive, $compileNode); templateDirective = directive; if (directive.replace) { replaceDirective = directive; } nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode, templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, { controllerDirectives: controllerDirectives, newIsolateScopeDirective: newIsolateScopeDirective, templateDirective: templateDirective, nonTlbTranscludeDirective: nonTlbTranscludeDirective }); ii = directives.length; } else if (directive.compile) { try { linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); if (isFunction(linkFn)) { addLinkFns(null, linkFn, attrStart, attrEnd); } else if (linkFn) { addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd); } } catch (e) { $exceptionHandler(e, startingTag($compileNode)); } } if (directive.terminal) { nodeLinkFn.terminal = true; terminalPriority = Math.max(terminalPriority, directive.priority); } } nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true; nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective; nodeLinkFn.templateOnThisElement = hasTemplate; nodeLinkFn.transclude = childTranscludeFn; previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective; // might be normal or delayed nodeLinkFn depending on if templateUrl is present return nodeLinkFn; //////////////////// function addLinkFns(pre, post, attrStart, attrEnd) { if (pre) { if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd); pre.require = directive.require; pre.directiveName = directiveName; if (newIsolateScopeDirective === directive || directive.$$isolateScope) { pre = cloneAndAnnotateFn(pre, {isolateScope: true}); } preLinkFns.push(pre); } if (post) { if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd); post.require = directive.require; post.directiveName = directiveName; if (newIsolateScopeDirective === directive || directive.$$isolateScope) { post = cloneAndAnnotateFn(post, {isolateScope: true}); } postLinkFns.push(post); } } function getControllers(directiveName, require, $element, elementControllers) { var value, retrievalMethod = 'data', optional = false; if (isString(require)) { while((value = require.charAt(0)) == '^' || value == '?') { require = require.substr(1); if (value == '^') { retrievalMethod = 'inheritedData'; } optional = optional || value == '?'; } value = null; if (elementControllers && retrievalMethod === 'data') { value = elementControllers[require]; } value = value || $element[retrievalMethod]('$' + require + 'Controller'); if (!value && !optional) { throw $compileMinErr('ctreq', "Controller '{0}', required by directive '{1}', can't be found!", require, directiveName); } return value; } else if (isArray(require)) { value = []; forEach(require, function(require) { value.push(getControllers(directiveName, require, $element, elementControllers)); }); } return value; } function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { var attrs, $element, i, ii, linkFn, controller, isolateScope, elementControllers = {}, transcludeFn; attrs = (compileNode === linkNode) ? templateAttrs : shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); $element = attrs.$$element; if (newIsolateScopeDirective) { var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/; isolateScope = scope.$new(true); if (templateDirective && (templateDirective === newIsolateScopeDirective || templateDirective === newIsolateScopeDirective.$$originalDirective)) { $element.data('$isolateScope', isolateScope); } else { $element.data('$isolateScopeNoTemplate', isolateScope); } safeAddClass($element, 'ng-isolate-scope'); forEach(newIsolateScopeDirective.scope, function(definition, scopeName) { var match = definition.match(LOCAL_REGEXP) || [], attrName = match[3] || scopeName, optional = (match[2] == '?'), mode = match[1], // @, =, or & lastValue, parentGet, parentSet, compare; isolateScope.$$isolateBindings[scopeName] = mode + attrName; switch (mode) { case '@': attrs.$observe(attrName, function(value) { isolateScope[scopeName] = value; }); attrs.$$observers[attrName].$$scope = scope; if( attrs[attrName] ) { // If the attribute has been provided then we trigger an interpolation to ensure // the value is there for use in the link fn isolateScope[scopeName] = $interpolate(attrs[attrName])(scope); } break; case '=': if (optional && !attrs[attrName]) { return; } parentGet = $parse(attrs[attrName]); if (parentGet.literal) { compare = equals; } else { compare = function(a,b) { return a === b || (a !== a && b !== b); }; } parentSet = parentGet.assign || function() { // reset the change, or we will throw this exception on every $digest lastValue = isolateScope[scopeName] = parentGet(scope); throw $compileMinErr('nonassign', "Expression '{0}' used with directive '{1}' is non-assignable!", attrs[attrName], newIsolateScopeDirective.name); }; lastValue = isolateScope[scopeName] = parentGet(scope); isolateScope.$watch(function parentValueWatch() { var parentValue = parentGet(scope); if (!compare(parentValue, isolateScope[scopeName])) { // we are out of sync and need to copy if (!compare(parentValue, lastValue)) { // parent changed and it has precedence isolateScope[scopeName] = parentValue; } else { // if the parent can be assigned then do so parentSet(scope, parentValue = isolateScope[scopeName]); } } return lastValue = parentValue; }, null, parentGet.literal); break; case '&': parentGet = $parse(attrs[attrName]); isolateScope[scopeName] = function(locals) { return parentGet(scope, locals); }; break; default: throw $compileMinErr('iscp', "Invalid isolate scope definition for directive '{0}'." + " Definition: {... {1}: '{2}' ...}", newIsolateScopeDirective.name, scopeName, definition); } }); } transcludeFn = boundTranscludeFn && controllersBoundTransclude; if (controllerDirectives) { forEach(controllerDirectives, function(directive) { var locals = { $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope, $element: $element, $attrs: attrs, $transclude: transcludeFn }, controllerInstance; controller = directive.controller; if (controller == '@') { controller = attrs[directive.name]; } controllerInstance = $controller(controller, locals); // For directives with element transclusion the element is a comment, // but jQuery .data doesn't support attaching data to comment nodes as it's hard to // clean up (http://bugs.jquery.com/ticket/8335). // Instead, we save the controllers for the element in a local hash and attach to .data // later, once we have the actual element. elementControllers[directive.name] = controllerInstance; if (!hasElementTranscludeDirective) { $element.data('$' + directive.name + 'Controller', controllerInstance); } if (directive.controllerAs) { locals.$scope[directive.controllerAs] = controllerInstance; } }); } // PRELINKING for(i = 0, ii = preLinkFns.length; i < ii; i++) { try { linkFn = preLinkFns[i]; linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); } catch (e) { $exceptionHandler(e, startingTag($element)); } } // RECURSION // We only pass the isolate scope, if the isolate directive has a template, // otherwise the child elements do not belong to the isolate directive. var scopeToChild = scope; if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) { scopeToChild = isolateScope; } childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn); // POSTLINKING for(i = postLinkFns.length - 1; i >= 0; i--) { try { linkFn = postLinkFns[i]; linkFn(linkFn.isolateScope ? isolateScope : scope, $element, attrs, linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers), transcludeFn); } catch (e) { $exceptionHandler(e, startingTag($element)); } } // This is the function that is injected as `$transclude`. function controllersBoundTransclude(scope, cloneAttachFn) { var transcludeControllers; // no scope passed if (arguments.length < 2) { cloneAttachFn = scope; scope = undefined; } if (hasElementTranscludeDirective) { transcludeControllers = elementControllers; } return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers); } } } function markDirectivesAsIsolate(directives) { // mark all directives as needing isolate scope. for (var j = 0, jj = directives.length; j < jj; j++) { directives[j] = inherit(directives[j], {$$isolateScope: true}); } } /** * looks up the directive and decorates it with exception handling and proper parameters. We * call this the boundDirective. * * @param {string} name name of the directive to look up. * @param {string} location The directive must be found in specific format. * String containing any of theses characters: * * * `E`: element name * * `A': attribute * * `C`: class * * `M`: comment * @returns {boolean} true if directive was added. */ function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName, endAttrName) { if (name === ignoreDirective) return null; var match = null; if (hasDirectives.hasOwnProperty(name)) { for(var directive, directives = $injector.get(name + Suffix), i = 0, ii = directives.length; i directive.priority) && directive.restrict.indexOf(location) != -1) { if (startAttrName) { directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName}); } tDirectives.push(directive); match = directive; } } catch(e) { $exceptionHandler(e); } } } return match; } /** * When the element is replaced with HTML template then the new attributes * on the template need to be merged with the existing attributes in the DOM. * The desired effect is to have both of the attributes present. * * @param {object} dst destination attributes (original DOM) * @param {object} src source attributes (from the directive template) */ function mergeTemplateAttributes(dst, src) { var srcAttr = src.$attr, dstAttr = dst.$attr, $element = dst.$$element; // reapply the old attributes to the new element forEach(dst, function(value, key) { if (key.charAt(0) != '$') { if (src[key] && src[key] !== value) { value += (key === 'style' ? ';' : ' ') + src[key]; } dst.$set(key, value, true, srcAttr[key]); } }); // copy the new attributes on the old attrs object forEach(src, function(value, key) { if (key == 'class') { safeAddClass($element, value); dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; } else if (key == 'style') { $element.attr('style', $element.attr('style') + ';' + value); dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value; // `dst` will never contain hasOwnProperty as DOM parser won't let it. // You will get an "InvalidCharacterError: DOM Exception 5" error if you // have an attribute like "has-own-property" or "data-has-own-property", etc. } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { dst[key] = value; dstAttr[key] = srcAttr[key]; } }); } function compileTemplateUrl(directives, $compileNode, tAttrs, $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { var linkQueue = [], afterTemplateNodeLinkFn, afterTemplateChildLinkFn, beforeTemplateCompileNode = $compileNode[0], origAsyncDirective = directives.shift(), // The fact that we have to copy and patch the directive seems wrong! derivedSyncDirective = extend({}, origAsyncDirective, { templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective }), templateUrl = (isFunction(origAsyncDirective.templateUrl)) ? origAsyncDirective.templateUrl($compileNode, tAttrs) : origAsyncDirective.templateUrl; $compileNode.empty(); $http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}). success(function(content) { var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn; content = denormalizeTemplate(content); if (origAsyncDirective.replace) { if (jqLiteIsTextNode(content)) { $template = []; } else { $template = jqLite(trim(content)); } compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { throw $compileMinErr('tplrt', "Template for directive '{0}' must have exactly one root element. {1}", origAsyncDirective.name, templateUrl); } tempTemplateAttrs = {$attr: {}}; replaceWith($rootElement, $compileNode, compileNode); var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs); if (isObject(origAsyncDirective.scope)) { markDirectivesAsIsolate(templateDirectives); } directives = templateDirectives.concat(directives); mergeTemplateAttributes(tAttrs, tempTemplateAttrs); } else { compileNode = beforeTemplateCompileNode; $compileNode.html(content); } directives.unshift(derivedSyncDirective); afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns, previousCompileContext); forEach($rootElement, function(node, i) { if (node == compileNode) { $rootElement[i] = $compileNode[0]; } }); afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn); while(linkQueue.length) { var scope = linkQueue.shift(), beforeTemplateLinkNode = linkQueue.shift(), linkRootElement = linkQueue.shift(), boundTranscludeFn = linkQueue.shift(), linkNode = $compileNode[0]; if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { var oldClasses = beforeTemplateLinkNode.className; if (!(previousCompileContext.hasElementTranscludeDirective && origAsyncDirective.replace)) { // it was cloned therefore we have to clone as well. linkNode = jqLiteClone(compileNode); } replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); // Copy in CSS classes from original node safeAddClass(jqLite(linkNode), oldClasses); } if (afterTemplateNodeLinkFn.transcludeOnThisElement) { childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); } else { childBoundTranscludeFn = boundTranscludeFn; } afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, childBoundTranscludeFn); } linkQueue = null; }). error(function(response, code, headers, config) { throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url); }); return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) { var childBoundTranscludeFn = boundTranscludeFn; if (linkQueue) { linkQueue.push(scope); linkQueue.push(node); linkQueue.push(rootElement); linkQueue.push(childBoundTranscludeFn); } else { if (afterTemplateNodeLinkFn.transcludeOnThisElement) { childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn); } afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn); } }; } /** * Sorting function for bound directives. */ function byPriority(a, b) { var diff = b.priority - a.priority; if (diff !== 0) return diff; if (a.name !== b.name) return (a.name < b.name) ? -1 : 1; return a.index - b.index; } function assertNoDuplicate(what, previousDirective, directive, element) { if (previousDirective) { throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}', previousDirective.name, directive.name, what, startingTag(element)); } } function addTextInterpolateDirective(directives, text) { var interpolateFn = $interpolate(text, true); if (interpolateFn) { directives.push({ priority: 0, compile: function textInterpolateCompileFn(templateNode) { // when transcluding a template that has bindings in the root // then we don't have a parent and should do this in the linkFn var parent = templateNode.parent(), hasCompileParent = parent.length; if (hasCompileParent) safeAddClass(templateNode.parent(), 'ng-binding'); return function textInterpolateLinkFn(scope, node) { var parent = node.parent(), bindings = parent.data('$binding') || []; bindings.push(interpolateFn); parent.data('$binding', bindings); if (!hasCompileParent) safeAddClass(parent, 'ng-binding'); scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { node[0].nodeValue = value; }); }; } }); } } function getTrustedContext(node, attrNormalizedName) { if (attrNormalizedName == "srcdoc") { return $sce.HTML; } var tag = nodeName_(node); // maction[xlink:href] can source SVG. It's not limited to . if (attrNormalizedName == "xlinkHref" || (tag == "FORM" && attrNormalizedName == "action") || (tag != "IMG" && (attrNormalizedName == "src" || attrNormalizedName == "ngSrc"))) { return $sce.RESOURCE_URL; } } function addAttrInterpolateDirective(node, directives, value, name) { var interpolateFn = $interpolate(value, true); // no interpolation found -> ignore if (!interpolateFn) return; if (name === "multiple" && nodeName_(node) === "SELECT") { throw $compileMinErr("selmulti", "Binding to the 'multiple' attribute is not supported. Element: {0}", startingTag(node)); } directives.push({ priority: 100, compile: function() { return { pre: function attrInterpolatePreLinkFn(scope, element, attr) { var $$observers = (attr.$$observers || (attr.$$observers = {})); if (EVENT_HANDLER_ATTR_REGEXP.test(name)) { throw $compileMinErr('nodomevents', "Interpolations for HTML DOM event attributes are disallowed. Please use the " + "ng- versions (such as ng-click instead of onclick) instead."); } // we need to interpolate again, in case the attribute value has been updated // (e.g. by another directive's compile function) interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name)); // if attribute was updated so that there is no interpolation going on we don't want to // register any observers if (!interpolateFn) return; // TODO(i): this should likely be attr.$set(name, iterpolateFn(scope) so that we reset the // actual attr value attr[name] = interpolateFn(scope); ($$observers[name] || ($$observers[name] = [])).$$inter = true; (attr.$$observers && attr.$$observers[name].$$scope || scope). $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) { //special case for class attribute addition + removal //so that class changes can tap into the animation //hooks provided by the $animate service. Be sure to //skip animations when the first digest occurs (when //both the new and the old values are the same) since //the CSS classes are the non-interpolated values if(name === 'class' && newValue != oldValue) { attr.$updateClass(newValue, oldValue); } else { attr.$set(name, newValue); } }); } }; } }); } /** * This is a special jqLite.replaceWith, which can replace items which * have no parents, provided that the containing jqLite collection is provided. * * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes * in the root of the tree. * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep * the shell, but replace its DOM node reference. * @param {Node} newNode The new DOM node. */ function replaceWith($rootElement, elementsToRemove, newNode) { var firstElementToRemove = elementsToRemove[0], removeCount = elementsToRemove.length, parent = firstElementToRemove.parentNode, i, ii; if ($rootElement) { for(i = 0, ii = $rootElement.length; i < ii; i++) { if ($rootElement[i] == firstElementToRemove) { $rootElement[i++] = newNode; for (var j = i, j2 = j + removeCount - 1, jj = $rootElement.length; j < jj; j++, j2++) { if (j2 < jj) { $rootElement[j] = $rootElement[j2]; } else { delete $rootElement[j]; } } $rootElement.length -= removeCount - 1; break; } } } if (parent) { parent.replaceChild(newNode, firstElementToRemove); } var fragment = document.createDocumentFragment(); fragment.appendChild(firstElementToRemove); newNode[jqLite.expando] = firstElementToRemove[jqLite.expando]; for (var k = 1, kk = elementsToRemove.length; k < kk; k++) { var element = elementsToRemove[k]; jqLite(element).remove(); // must do this way to clean up expando fragment.appendChild(element); delete elementsToRemove[k]; } elementsToRemove[0] = newNode; elementsToRemove.length = 1; } function cloneAndAnnotateFn(fn, annotation) { return extend(function() { return fn.apply(null, arguments); }, fn, annotation); } }]; } var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; /** * Converts all accepted directives format into proper directive name. * @param name Name to normalize */ function directiveNormalize(name) { return camelCase(name.replace(PREFIX_REGEXP, '')); } /** * @ngdoc type * @name $compile.directive.Attributes * * @description * A shared object between directive compile / linking functions which contains normalized DOM * element attributes. The values reflect current binding state `{{ }}`. The normalization is * needed since all of these are treated as equivalent in Angular: * * ``` * * ``` */ /** * @ngdoc property * @name $compile.directive.Attributes#$attr * * @description * A map of DOM element attribute names to the normalized name. This is * needed to do reverse lookup from normalized name back to actual name. */ /** * @ngdoc method * @name $compile.directive.Attributes#$set * @kind function * * @description * Set DOM element attribute value. * * * @param {string} name Normalized element attribute name of the property to modify. The name is * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr} * property to the original name. * @param {string} value Value to set the attribute to. The value can be an interpolated string. */ /** * Closure compiler type information */ function nodesetLinkingFn( /* angular.Scope */ scope, /* NodeList */ nodeList, /* Element */ rootElement, /* function(Function) */ boundTranscludeFn ){} function directiveLinkingFn( /* nodesetLinkingFn */ nodesetLinkingFn, /* angular.Scope */ scope, /* Node */ node, /* Element */ rootElement, /* function(Function) */ boundTranscludeFn ){} function tokenDifference(str1, str2) { var values = '', tokens1 = str1.split(/\s+/), tokens2 = str2.split(/\s+/); outer: for(var i = 0; i < tokens1.length; i++) { var token = tokens1[i]; for(var j = 0; j < tokens2.length; j++) { if(token == tokens2[j]) continue outer; } values += (values.length > 0 ? ' ' : '') + token; } return values; } /** * @ngdoc provider * @name $controllerProvider * @description * The {@link ng.$controller $controller service} is used by Angular to create new * controllers. * * This provider allows controller registration via the * {@link ng.$controllerProvider#register register} method. */ function $ControllerProvider() { var controllers = {}, CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/; /** * @ngdoc method * @name $controllerProvider#register * @param {string|Object} name Controller name, or an object map of controllers where the keys are * the names and the values are the constructors. * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI * annotations in the array notation). */ this.register = function(name, constructor) { assertNotHasOwnProperty(name, 'controller'); if (isObject(name)) { extend(controllers, name); } else { controllers[name] = constructor; } }; this.$get = ['$injector', '$window', function($injector, $window) { /** * @ngdoc service * @name $controller * @requires $injector * * @param {Function|string} constructor If called with a function then it's considered to be the * controller constructor function. Otherwise it's considered to be a string which is used * to retrieve the controller constructor using the following steps: * * * check if a controller with given name is registered via `$controllerProvider` * * check if evaluating the string on the current scope returns a constructor * * check `window[constructor]` on the global `window` object * * @param {Object} locals Injection locals for Controller. * @return {Object} Instance of given controller. * * @description * `$controller` service is responsible for instantiating controllers. * * It's just a simple call to {@link auto.$injector $injector}, but extracted into * a service, so that one can override this service with [BC version](https://gist.github.com/1649788). */ return function(expression, locals) { var instance, match, constructor, identifier; if(isString(expression)) { match = expression.match(CNTRL_REG), constructor = match[1], identifier = match[3]; expression = controllers.hasOwnProperty(constructor) ? controllers[constructor] : getter(locals.$scope, constructor, true) || getter($window, constructor, true); assertArgFn(expression, constructor, true); } instance = $injector.instantiate(expression, locals); if (identifier) { if (!(locals && typeof locals.$scope === 'object')) { throw minErr('$controller')('noscp', "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.", constructor || expression.name, identifier); } locals.$scope[identifier] = instance; } return instance; }; }]; } /** * @ngdoc service * @name $document * @requires $window * * @description * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object. * * @example

    $document title:

    window.document title:

    angular.module('documentExample', []) .controller('ExampleController', ['$scope', '$document', function($scope, $document) { $scope.title = $document[0].title; $scope.windowTitle = angular.element(window.document)[0].title; }]);
    */ function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; } /** * @ngdoc service * @name $exceptionHandler * @requires ng.$log * * @description * Any uncaught exception in angular expressions is delegated to this service. * The default implementation simply delegates to `$log.error` which logs it into * the browser console. * * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing. * * ## Example: * * ```js * angular.module('exceptionOverride', []).factory('$exceptionHandler', function () { * return function (exception, cause) { * exception.message += ' (caused by "' + cause + '")'; * throw exception; * }; * }); * ``` * * This example will override the normal action of `$exceptionHandler`, to make angular * exceptions fail hard when they happen, instead of just logging to the console. * * @param {Error} exception Exception associated with the error. * @param {string=} cause optional information about the context in which * the error was thrown. * */ function $ExceptionHandlerProvider() { this.$get = ['$log', function($log) { return function(exception, cause) { $log.error.apply($log, arguments); }; }]; } /** * Parse headers into key value object * * @param {string} headers Raw headers as a string * @returns {Object} Parsed headers as key value object */ function parseHeaders(headers) { var parsed = {}, key, val, i; if (!headers) return parsed; forEach(headers.split('\n'), function(line) { i = line.indexOf(':'); key = lowercase(trim(line.substr(0, i))); val = trim(line.substr(i + 1)); if (key) { parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val; } }); return parsed; } /** * Returns a function that provides access to parsed headers. * * Headers are lazy parsed when first requested. * @see parseHeaders * * @param {(string|Object)} headers Headers to provide access to. * @returns {function(string=)} Returns a getter function which if called with: * * - if called with single an argument returns a single header value or null * - if called with no arguments returns an object containing all headers. */ function headersGetter(headers) { var headersObj = isObject(headers) ? headers : undefined; return function(name) { if (!headersObj) headersObj = parseHeaders(headers); if (name) { return headersObj[lowercase(name)] || null; } return headersObj; }; } /** * Chain all given functions * * This function is used for both request and response transforming * * @param {*} data Data to transform. * @param {function(string=)} headers Http headers getter fn. * @param {(Function|Array.)} fns Function or an array of functions. * @returns {*} Transformed data. */ function transformData(data, headers, fns) { if (isFunction(fns)) return fns(data, headers); forEach(fns, function(fn) { data = fn(data, headers); }); return data; } function isSuccess(status) { return 200 <= status && status < 300; } /** * @ngdoc provider * @name $httpProvider * @description * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service. * */ function $HttpProvider() { var JSON_START = /^\s*(\[|\{[^\{])/, JSON_END = /[\}\]]\s*$/, PROTECTION_PREFIX = /^\)\]\}',?\n/, CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': 'application/json;charset=utf-8'}; /** * @ngdoc property * @name $httpProvider#defaults * @description * * Object containing default values for all {@link ng.$http $http} requests. * * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token. * Defaults value is `'XSRF-TOKEN'`. * * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the * XSRF token. Defaults value is `'X-XSRF-TOKEN'`. * * - **`defaults.headers`** - {Object} - Default headers for all $http requests. * Refer to {@link ng.$http#setting-http-headers $http} for documentation on * setting default headers. * - **`defaults.headers.common`** * - **`defaults.headers.post`** * - **`defaults.headers.put`** * - **`defaults.headers.patch`** **/ var defaults = this.defaults = { // transform incoming response data transformResponse: [function(data) { if (isString(data)) { // strip json vulnerability protection prefix data = data.replace(PROTECTION_PREFIX, ''); if (JSON_START.test(data) && JSON_END.test(data)) data = fromJson(data); } return data; }], // transform outgoing request data transformRequest: [function(d) { return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d; }], // default headers headers: { common: { 'Accept': 'application/json, text/plain, */*' }, post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON), patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON) }, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN' }; /** * @ngdoc property * @name $httpProvider#interceptors * @description * * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http} * pre-processing of request or postprocessing of responses. * * These service factories are ordered by request, i.e. they are applied in the same order as the * array, on request, but reverse order, on response. * * {@link ng.$http#interceptors Interceptors detailed info} **/ var interceptorFactories = this.interceptors = []; /** * For historical reasons, response interceptors are ordered by the order in which * they are applied to the response. (This is the opposite of interceptorFactories) */ var responseInterceptorFactories = this.responseInterceptors = []; this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { var defaultCache = $cacheFactory('$http'); /** * Interceptors stored in reverse order. Inner interceptors before outer interceptors. * The reversal is needed so that we can build up the interception chain around the * server request. */ var reversedInterceptors = []; forEach(interceptorFactories, function(interceptorFactory) { reversedInterceptors.unshift(isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)); }); forEach(responseInterceptorFactories, function(interceptorFactory, index) { var responseFn = isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory); /** * Response interceptors go before "around" interceptors (no real reason, just * had to pick one.) But they are already reversed, so we can't use unshift, hence * the splice. */ reversedInterceptors.splice(index, 0, { response: function(response) { return responseFn($q.when(response)); }, responseError: function(response) { return responseFn($q.reject(response)); } }); }); /** * @ngdoc service * @kind function * @name $http * @requires ng.$httpBackend * @requires $cacheFactory * @requires $rootScope * @requires $q * @requires $injector * * @description * The `$http` service is a core Angular service that facilitates communication with the remote * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest) * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP). * * For unit testing applications that use `$http` service, see * {@link ngMock.$httpBackend $httpBackend mock}. * * For a higher level of abstraction, please check out the {@link ngResource.$resource * $resource} service. * * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage * it is important to familiarize yourself with these APIs and the guarantees they provide. * * * # General usage * The `$http` service is a function which takes a single argument — a configuration object — * that is used to generate an HTTP request and returns a {@link ng.$q promise} * with two $http specific methods: `success` and `error`. * * ```js * $http({method: 'GET', url: '/someUrl'}). * success(function(data, status, headers, config) { * // this callback will be called asynchronously * // when the response is available * }). * error(function(data, status, headers, config) { * // called asynchronously if an error occurs * // or server returns response with an error status. * }); * ``` * * Since the returned value of calling the $http function is a `promise`, you can also use * the `then` method to register callbacks, and these callbacks will receive a single argument – * an object representing the response. See the API signature and type info below for more * details. * * A response status code between 200 and 299 is considered a success status and * will result in the success callback being called. Note that if the response is a redirect, * XMLHttpRequest will transparently follow it, meaning that the error callback will not be * called for such responses. * * # Writing Unit Tests that use $http * When unit testing (using {@link ngMock ngMock}), it is necessary to call * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending * request using trained responses. * * ``` * $httpBackend.expectGET(...); * $http.get(...); * $httpBackend.flush(); * ``` * * # Shortcut methods * * Shortcut methods are also available. All shortcut methods require passing in the URL, and * request data must be passed in for POST/PUT requests. * * ```js * $http.get('/someUrl').success(successCallback); * $http.post('/someUrl', data).success(successCallback); * ``` * * Complete list of shortcut methods: * * - {@link ng.$http#get $http.get} * - {@link ng.$http#head $http.head} * - {@link ng.$http#post $http.post} * - {@link ng.$http#put $http.put} * - {@link ng.$http#delete $http.delete} * - {@link ng.$http#jsonp $http.jsonp} * - {@link ng.$http#patch $http.patch} * * * # Setting HTTP Headers * * The $http service will automatically add certain HTTP headers to all requests. These defaults * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration * object, which currently contains this default configuration: * * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): * - `Accept: application/json, text/plain, * / *` * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests) * - `Content-Type: application/json` * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests) * - `Content-Type: application/json` * * To add or overwrite these defaults, simply add or remove a property from these configuration * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object * with the lowercased HTTP method name as the key, e.g. * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }. * * The defaults can also be set at runtime via the `$http.defaults` object in the same * fashion. For example: * * ``` * module.run(function($http) { * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w' * }); * ``` * * In addition, you can supply a `headers` property in the config object passed when * calling `$http(config)`, which overrides the defaults without changing them globally. * * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis, * Use the `headers` property, setting the desired header to `undefined`. For example: * * ```js * var req = { * method: 'POST', * url: 'http://example.com', * headers: { * 'Content-Type': undefined * }, * data: { test: 'test' }, * } * * $http(req).success(function(){...}).error(function(){...}); * ``` * * # Transforming Requests and Responses * * Both requests and responses can be transformed using transform functions. By default, Angular * applies these transformations: * * Request transformations: * * - If the `data` property of the request configuration object contains an object, serialize it * into JSON format. * * Response transformations: * * - If XSRF prefix is detected, strip it (see Security Considerations section below). * - If JSON response is detected, deserialize it using a JSON parser. * * To globally augment or override the default transforms, modify the * `$httpProvider.defaults.transformRequest` and `$httpProvider.defaults.transformResponse` * properties. These properties are by default an array of transform functions, which allows you * to `push` or `unshift` a new transformation function into the transformation chain. You can * also decide to completely override any default transformations by assigning your * transformation functions to these properties directly without the array wrapper. These defaults * are again available on the $http factory at run-time, which may be useful if you have run-time * services you wish to be involved in your transformations. * * Similarly, to locally override the request/response transforms, augment the * `transformRequest` and/or `transformResponse` properties of the configuration object passed * into `$http`. * * * # Caching * * To enable caching, set the request configuration `cache` property to `true` (to use default * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}). * When the cache is enabled, `$http` stores the response from the server in the specified * cache. The next time the same request is made, the response is served from the cache without * sending a request to the server. * * Note that even if the response is served from cache, delivery of the data is asynchronous in * the same way that real requests are. * * If there are multiple GET requests for the same URL that should be cached using the same * cache, but the cache is not populated yet, only one request to the server will be made and * the remaining requests will be fulfilled using the response from the first request. * * You can change the default cache to a new object (built with * {@link ng.$cacheFactory `$cacheFactory`}) by updating the * {@link ng.$http#properties_defaults `$http.defaults.cache`} property. All requests who set * their `cache` property to `true` will now use this cache object. * * If you set the default cache to `false` then only requests that specify their own custom * cache object will be cached. * * # Interceptors * * Before you start creating interceptors, be sure to understand the * {@link ng.$q $q and deferred/promise APIs}. * * For purposes of global error handling, authentication, or any kind of synchronous or * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be * able to intercept requests before they are handed to the server and * responses before they are handed over to the application code that * initiated these requests. The interceptors leverage the {@link ng.$q * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing. * * The interceptors are service factories that are registered with the `$httpProvider` by * adding them to the `$httpProvider.interceptors` array. The factory is called and * injected with dependencies (if specified) and returns the interceptor. * * There are two kinds of interceptors (and two kinds of rejection interceptors): * * * `request`: interceptors get called with a http `config` object. The function is free to * modify the `config` object or create a new one. The function needs to return the `config` * object directly, or a promise containing the `config` or a new `config` object. * * `requestError`: interceptor gets called when a previous interceptor threw an error or * resolved with a rejection. * * `response`: interceptors get called with http `response` object. The function is free to * modify the `response` object or create a new one. The function needs to return the `response` * object directly, or as a promise containing the `response` or a new `response` object. * * `responseError`: interceptor gets called when a previous interceptor threw an error or * resolved with a rejection. * * * ```js * // register the interceptor as a service * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { * return { * // optional method * 'request': function(config) { * // do something on success * return config; * }, * * // optional method * 'requestError': function(rejection) { * // do something on error * if (canRecover(rejection)) { * return responseOrNewPromise * } * return $q.reject(rejection); * }, * * * * // optional method * 'response': function(response) { * // do something on success * return response; * }, * * // optional method * 'responseError': function(rejection) { * // do something on error * if (canRecover(rejection)) { * return responseOrNewPromise * } * return $q.reject(rejection); * } * }; * }); * * $httpProvider.interceptors.push('myHttpInterceptor'); * * * // alternatively, register the interceptor via an anonymous factory * $httpProvider.interceptors.push(function($q, dependency1, dependency2) { * return { * 'request': function(config) { * // same as above * }, * * 'response': function(response) { * // same as above * } * }; * }); * ``` * * # Response interceptors (DEPRECATED) * * Before you start creating interceptors, be sure to understand the * {@link ng.$q $q and deferred/promise APIs}. * * For purposes of global error handling, authentication or any kind of synchronous or * asynchronous preprocessing of received responses, it is desirable to be able to intercept * responses for http requests before they are handed over to the application code that * initiated these requests. The response interceptors leverage the {@link ng.$q * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. * * The interceptors are service factories that are registered with the $httpProvider by * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and * injected with dependencies (if specified) and returns the interceptor — a function that * takes a {@link ng.$q promise} and returns the original or a new promise. * * ```js * // register the interceptor as a service * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { * return function(promise) { * return promise.then(function(response) { * // do something on success * return response; * }, function(response) { * // do something on error * if (canRecover(response)) { * return responseOrNewPromise * } * return $q.reject(response); * }); * } * }); * * $httpProvider.responseInterceptors.push('myHttpInterceptor'); * * * // register the interceptor via an anonymous factory * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) { * return function(promise) { * // same as above * } * }); * ``` * * * # Security Considerations * * When designing web applications, consider security threats from: * * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) * * Both server and the client must cooperate in order to eliminate these threats. Angular comes * pre-configured with strategies that address these issues, but for this to work backend server * cooperation is required. * * ## JSON Vulnerability Protection * * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx) * allows third party website to turn your JSON resource URL into * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To * counter this your server can prefix all JSON requests with following string `")]}',\n"`. * Angular will automatically strip the prefix before processing it as JSON. * * For example if your server needs to return: * ```js * ['one','two'] * ``` * * which is vulnerable to attack, your server can return: * ```js * )]}', * ['one','two'] * ``` * * Angular will strip the prefix, before processing the JSON. * * * ## Cross Site Request Forgery (XSRF) Protection * * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which * an unauthorized site can gain your user's private data. Angular provides a mechanism * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only * JavaScript that runs on your domain could read the cookie, your server can be assured that * the XHR came from JavaScript running on your domain. The header will not be set for * cross-domain requests. * * To take advantage of this, your server needs to set a token in a JavaScript readable session * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure * that only JavaScript running on your domain could have sent the request. The token must be * unique for each user and must be verifiable by the server (to prevent the JavaScript from * making up its own tokens). We recommend that the token is a digest of your site's * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography)) * for added security. * * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time, * or the per-request config object. * * * @param {object} config Object describing the request to be made and how it should be * processed. The object has following properties: * * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. * - **params** – `{Object.}` – Map of strings or objects which will be turned * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be * JSONified. * - **data** – `{string|Object}` – Data to be sent as the request message data. * - **headers** – `{Object}` – Map of strings or functions which return strings representing * HTTP headers to send to the server. If the return value of a function is null, the * header will not be sent. * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token. * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token. * - **transformRequest** – * `{function(data, headersGetter)|Array.}` – * transform function or an array of such functions. The transform function takes the http * request body and headers and returns its transformed (typically serialized) version. * - **transformResponse** – * `{function(data, headersGetter)|Array.}` – * transform function or an array of such functions. The transform function takes the http * response body and headers and returns its transformed (typically deserialized) version. * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the * GET request, otherwise if a cache instance built with * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for * caching. * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} * that should abort the request when resolved. * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials) * for more information. * - **responseType** - `{string}` - see * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType). * * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the * standard `then` method and two http specific methods: `success` and `error`. The `then` * method takes two arguments a success and an error callback which will be called with a * response object. The `success` and `error` methods take a single argument - a function that * will be called when the request succeeds or fails respectively. The arguments passed into * these functions are destructured representation of the response object passed into the * `then` method. The response object has these properties: * * - **data** – `{string|Object}` – The response body transformed with the transform * functions. * - **status** – `{number}` – HTTP status code of the response. * - **headers** – `{function([headerName])}` – Header getter function. * - **config** – `{Object}` – The configuration object that was used to generate the request. * - **statusText** – `{string}` – HTTP status text of the response. * * @property {Array.} pendingRequests Array of config objects for currently pending * requests. This is primarily meant to be used for debugging purposes. * * * @example

    http status code: {{status}}
    http response data: {{data}}
    angular.module('httpExample', []) .controller('FetchController', ['$scope', '$http', '$templateCache', function($scope, $http, $templateCache) { $scope.method = 'GET'; $scope.url = 'http-hello.html'; $scope.fetch = function() { $scope.code = null; $scope.response = null; $http({method: $scope.method, url: $scope.url, cache: $templateCache}). success(function(data, status) { $scope.status = status; $scope.data = data; }). error(function(data, status) { $scope.data = data || "Request failed"; $scope.status = status; }); }; $scope.updateModel = function(method, url) { $scope.method = method; $scope.url = url; }; }]); Hello, $http! var status = element(by.binding('status')); var data = element(by.binding('data')); var fetchBtn = element(by.id('fetchbtn')); var sampleGetBtn = element(by.id('samplegetbtn')); var sampleJsonpBtn = element(by.id('samplejsonpbtn')); var invalidJsonpBtn = element(by.id('invalidjsonpbtn')); it('should make an xhr GET request', function() { sampleGetBtn.click(); fetchBtn.click(); expect(status.getText()).toMatch('200'); expect(data.getText()).toMatch(/Hello, \$http!/); }); // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185 // it('should make a JSONP request to angularjs.org', function() { // sampleJsonpBtn.click(); // fetchBtn.click(); // expect(status.getText()).toMatch('200'); // expect(data.getText()).toMatch(/Super Hero!/); // }); it('should make JSONP request to invalid URL and invoke the error handler', function() { invalidJsonpBtn.click(); fetchBtn.click(); expect(status.getText()).toMatch('0'); expect(data.getText()).toMatch('Request failed'); });
    */ function $http(requestConfig) { var config = { method: 'get', transformRequest: defaults.transformRequest, transformResponse: defaults.transformResponse }; var headers = mergeHeaders(requestConfig); extend(config, requestConfig); config.headers = headers; config.method = uppercase(config.method); var serverRequest = function(config) { headers = config.headers; var reqData = transformData(config.data, headersGetter(headers), config.transformRequest); // strip content-type if data is undefined if (isUndefined(reqData)) { forEach(headers, function(value, header) { if (lowercase(header) === 'content-type') { delete headers[header]; } }); } if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { config.withCredentials = defaults.withCredentials; } // send request return sendReq(config, reqData, headers).then(transformResponse, transformResponse); }; var chain = [serverRequest, undefined]; var promise = $q.when(config); // apply interceptors forEach(reversedInterceptors, function(interceptor) { if (interceptor.request || interceptor.requestError) { chain.unshift(interceptor.request, interceptor.requestError); } if (interceptor.response || interceptor.responseError) { chain.push(interceptor.response, interceptor.responseError); } }); while(chain.length) { var thenFn = chain.shift(); var rejectFn = chain.shift(); promise = promise.then(thenFn, rejectFn); } promise.success = function(fn) { promise.then(function(response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.error = function(fn) { promise.then(null, function(response) { fn(response.data, response.status, response.headers, config); }); return promise; }; return promise; function transformResponse(response) { // make a copy since the response must be cacheable var resp = extend({}, response, { data: transformData(response.data, response.headers, config.transformResponse) }); return (isSuccess(response.status)) ? resp : $q.reject(resp); } function mergeHeaders(config) { var defHeaders = defaults.headers, reqHeaders = extend({}, config.headers), defHeaderName, lowercaseDefHeaderName, reqHeaderName; defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); // using for-in instead of forEach to avoid unecessary iteration after header has been found defaultHeadersIteration: for (defHeaderName in defHeaders) { lowercaseDefHeaderName = lowercase(defHeaderName); for (reqHeaderName in reqHeaders) { if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { continue defaultHeadersIteration; } } reqHeaders[defHeaderName] = defHeaders[defHeaderName]; } // execute if header value is a function for merged headers execHeaders(reqHeaders); return reqHeaders; function execHeaders(headers) { var headerContent; forEach(headers, function(headerFn, header) { if (isFunction(headerFn)) { headerContent = headerFn(); if (headerContent != null) { headers[header] = headerContent; } else { delete headers[header]; } } }); } } } $http.pendingRequests = []; /** * @ngdoc method * @name $http#get * * @description * Shortcut method to perform `GET` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ /** * @ngdoc method * @name $http#delete * * @description * Shortcut method to perform `DELETE` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ /** * @ngdoc method * @name $http#head * * @description * Shortcut method to perform `HEAD` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ /** * @ngdoc method * @name $http#jsonp * * @description * Shortcut method to perform `JSONP` request. * * @param {string} url Relative or absolute URL specifying the destination of the request. * The name of the callback should be the string `JSON_CALLBACK`. * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ createShortMethods('get', 'delete', 'head', 'jsonp'); /** * @ngdoc method * @name $http#post * * @description * Shortcut method to perform `POST` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {*} data Request content * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ /** * @ngdoc method * @name $http#put * * @description * Shortcut method to perform `PUT` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {*} data Request content * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ /** * @ngdoc method * @name $http#patch * * @description * Shortcut method to perform `PATCH` request. * * @param {string} url Relative or absolute URL specifying the destination of the request * @param {*} data Request content * @param {Object=} config Optional configuration object * @returns {HttpPromise} Future object */ createShortMethodsWithData('post', 'put', 'patch'); /** * @ngdoc property * @name $http#defaults * * @description * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of * default headers, withCredentials as well as request and response transformations. * * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. */ $http.defaults = defaults; return $http; function createShortMethods(names) { forEach(arguments, function(name) { $http[name] = function(url, config) { return $http(extend(config || {}, { method: name, url: url })); }; }); } function createShortMethodsWithData(name) { forEach(arguments, function(name) { $http[name] = function(url, data, config) { return $http(extend(config || {}, { method: name, url: url, data: data })); }; }); } /** * Makes the request. * * !!! ACCESSES CLOSURE VARS: * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests */ function sendReq(config, reqData, reqHeaders) { var deferred = $q.defer(), promise = deferred.promise, cache, cachedResp, url = buildUrl(config.url, config.params); $http.pendingRequests.push(config); promise.then(removePendingReq, removePendingReq); if ((config.cache || defaults.cache) && config.cache !== false && (config.method === 'GET' || config.method === 'JSONP')) { cache = isObject(config.cache) ? config.cache : isObject(defaults.cache) ? defaults.cache : defaultCache; } if (cache) { cachedResp = cache.get(url); if (isDefined(cachedResp)) { if (isPromiseLike(cachedResp)) { // cached request has already been sent, but there is no response yet cachedResp.then(removePendingReq, removePendingReq); return cachedResp; } else { // serving from cache if (isArray(cachedResp)) { resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]); } else { resolvePromise(cachedResp, 200, {}, 'OK'); } } } else { // put the promise for the non-transformed response into cache as a placeholder cache.put(url, promise); } } // if we won't have the response in cache, set the xsrf headers and // send the request to the backend if (isUndefined(cachedResp)) { var xsrfValue = urlIsSameOrigin(config.url) ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] : undefined; if (xsrfValue) { reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue; } $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, config.withCredentials, config.responseType); } return promise; /** * Callback registered to $httpBackend(): * - caches the response if desired * - resolves the raw $http promise * - calls $apply */ function done(status, response, headersString, statusText) { if (cache) { if (isSuccess(status)) { cache.put(url, [status, response, parseHeaders(headersString), statusText]); } else { // remove promise from the cache cache.remove(url); } } resolvePromise(response, status, headersString, statusText); if (!$rootScope.$$phase) $rootScope.$apply(); } /** * Resolves the raw $http promise. */ function resolvePromise(response, status, headers, statusText) { // normalize internal statuses to 0 status = Math.max(status, 0); (isSuccess(status) ? deferred.resolve : deferred.reject)({ data: response, status: status, headers: headersGetter(headers), config: config, statusText : statusText }); } function removePendingReq() { var idx = indexOf($http.pendingRequests, config); if (idx !== -1) $http.pendingRequests.splice(idx, 1); } } function buildUrl(url, params) { if (!params) return url; var parts = []; forEachSorted(params, function(value, key) { if (value === null || isUndefined(value)) return; if (!isArray(value)) value = [value]; forEach(value, function(v) { if (isObject(v)) { if (isDate(v)){ v = v.toISOString(); } else { v = toJson(v); } } parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(v)); }); }); if(parts.length > 0) { url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); } return url; } }]; } function createXhr(method) { //if IE and the method is not RFC2616 compliant, or if XMLHttpRequest //is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest //if it is available if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) || !window.XMLHttpRequest)) { return new window.ActiveXObject("Microsoft.XMLHTTP"); } else if (window.XMLHttpRequest) { return new window.XMLHttpRequest(); } throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest."); } /** * @ngdoc service * @name $httpBackend * @requires $window * @requires $document * * @description * HTTP backend used by the {@link ng.$http service} that delegates to * XMLHttpRequest object or JSONP and deals with browser incompatibilities. * * You should never need to use this service directly, instead use the higher-level abstractions: * {@link ng.$http $http} or {@link ngResource.$resource $resource}. * * During testing this implementation is swapped with {@link ngMock.$httpBackend mock * $httpBackend} which can be trained with responses. */ function $HttpBackendProvider() { this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]); }]; } function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) { var ABORTED = -1; // TODO(vojta): fix the signature return function(method, url, post, callback, headers, timeout, withCredentials, responseType) { var status; $browser.$$incOutstandingRequestCount(); url = url || $browser.url(); if (lowercase(method) == 'jsonp') { var callbackId = '_' + (callbacks.counter++).toString(36); callbacks[callbackId] = function(data) { callbacks[callbackId].data = data; callbacks[callbackId].called = true; }; var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), callbackId, function(status, text) { completeRequest(callback, status, callbacks[callbackId].data, "", text); callbacks[callbackId] = noop; }); } else { var xhr = createXhr(method); xhr.open(method, url, true); forEach(headers, function(value, key) { if (isDefined(value)) { xhr.setRequestHeader(key, value); } }); // In IE6 and 7, this might be called synchronously when xhr.send below is called and the // response is in the cache. the promise api will ensure that to the app code the api is // always async xhr.onreadystatechange = function() { // onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by // xhrs that are resolved while the app is in the background (see #5426). // since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before // continuing // // we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and // Safari respectively. if (xhr && xhr.readyState == 4) { var responseHeaders = null, response = null, statusText = ''; if(status !== ABORTED) { responseHeaders = xhr.getAllResponseHeaders(); // responseText is the old-school way of retrieving response (supported by IE8 & 9) // response/responseType properties were introduced in XHR Level2 spec (supported by IE10) response = ('response' in xhr) ? xhr.response : xhr.responseText; } // Accessing statusText on an aborted xhr object will // throw an 'c00c023f error' in IE9 and lower, don't touch it. if (!(status === ABORTED && msie < 10)) { statusText = xhr.statusText; } completeRequest(callback, status || xhr.status, response, responseHeaders, statusText); } }; if (withCredentials) { xhr.withCredentials = true; } if (responseType) { try { xhr.responseType = responseType; } catch (e) { // WebKit added support for the json responseType value on 09/03/2013 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are // known to throw when setting the value "json" as the response type. Other older // browsers implementing the responseType // // The json response type can be ignored if not supported, because JSON payloads are // parsed on the client-side regardless. if (responseType !== 'json') { throw e; } } } xhr.send(post || null); } if (timeout > 0) { var timeoutId = $browserDefer(timeoutRequest, timeout); } else if (isPromiseLike(timeout)) { timeout.then(timeoutRequest); } function timeoutRequest() { status = ABORTED; jsonpDone && jsonpDone(); xhr && xhr.abort(); } function completeRequest(callback, status, response, headersString, statusText) { // cancel timeout and subsequent timeout promise resolution timeoutId && $browserDefer.cancel(timeoutId); jsonpDone = xhr = null; // fix status code when it is 0 (0 status is undocumented). // Occurs when accessing file resources or on Android 4.1 stock browser // while retrieving files from application cache. if (status === 0) { status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0; } // normalize IE bug (http://bugs.jquery.com/ticket/1450) status = status === 1223 ? 204 : status; statusText = statusText || ''; callback(status, response, headersString, statusText); $browser.$$completeOutstandingRequest(noop); } }; function jsonpReq(url, callbackId, done) { // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: // - fetches local scripts via XHR and evals them // - adds and immediately removes script elements from the document var script = rawDocument.createElement('script'), callback = null; script.type = "text/javascript"; script.src = url; script.async = true; callback = function(event) { removeEventListenerFn(script, "load", callback); removeEventListenerFn(script, "error", callback); rawDocument.body.removeChild(script); script = null; var status = -1; var text = "unknown"; if (event) { if (event.type === "load" && !callbacks[callbackId].called) { event = { type: "error" }; } text = event.type; status = event.type === "error" ? 404 : 200; } if (done) { done(status, text); } }; addEventListenerFn(script, "load", callback); addEventListenerFn(script, "error", callback); if (msie <= 8) { script.onreadystatechange = function() { if (isString(script.readyState) && /loaded|complete/.test(script.readyState)) { script.onreadystatechange = null; callback({ type: 'load' }); } }; } rawDocument.body.appendChild(script); return callback; } } var $interpolateMinErr = minErr('$interpolate'); /** * @ngdoc provider * @name $interpolateProvider * @kind function * * @description * * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. * * @example
    //demo.label//
    it('should interpolate binding with custom symbols', function() { expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.'); });
    */ function $InterpolateProvider() { var startSymbol = '{{'; var endSymbol = '}}'; /** * @ngdoc method * @name $interpolateProvider#startSymbol * @description * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. * * @param {string=} value new value to set the starting symbol to. * @returns {string|self} Returns the symbol when used as getter and self if used as setter. */ this.startSymbol = function(value){ if (value) { startSymbol = value; return this; } else { return startSymbol; } }; /** * @ngdoc method * @name $interpolateProvider#endSymbol * @description * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. * * @param {string=} value new value to set the ending symbol to. * @returns {string|self} Returns the symbol when used as getter and self if used as setter. */ this.endSymbol = function(value){ if (value) { endSymbol = value; return this; } else { return endSymbol; } }; this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) { var startSymbolLength = startSymbol.length, endSymbolLength = endSymbol.length; /** * @ngdoc service * @name $interpolate * @kind function * * @requires $parse * @requires $sce * * @description * * Compiles a string with markup into an interpolation function. This service is used by the * HTML {@link ng.$compile $compile} service for data binding. See * {@link ng.$interpolateProvider $interpolateProvider} for configuring the * interpolation markup. * * * ```js * var $interpolate = ...; // injected * var exp = $interpolate('Hello {{name | uppercase}}!'); * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!'); * ``` * * * @param {string} text The text with markup to interpolate. * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have * embedded expression in order to return an interpolation function. Strings with no * embedded expression will return null for the interpolation function. * @param {string=} trustedContext when provided, the returned function passes the interpolated * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult, * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that * provides Strict Contextual Escaping for details. * @returns {function(context)} an interpolation function which is used to compute the * interpolated string. The function has these parameters: * * * `context`: an object against which any expressions embedded in the strings are evaluated * against. * */ function $interpolate(text, mustHaveExpression, trustedContext) { var startIndex, endIndex, index = 0, parts = [], length = text.length, hasInterpolation = false, fn, exp, concat = []; while(index < length) { if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { (index != startIndex) && parts.push(text.substring(index, startIndex)); parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); fn.exp = exp; index = endIndex + endSymbolLength; hasInterpolation = true; } else { // we did not find anything, so we have to add the remainder to the parts array (index != length) && parts.push(text.substring(index)); index = length; } } if (!(length = parts.length)) { // we added, nothing, must have been an empty string. parts.push(''); length = 1; } // Concatenating expressions makes it hard to reason about whether some combination of // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a // single expression be used for iframe[src], object[src], etc., we ensure that the value // that's used is assigned or constructed by some JS code somewhere that is more testable or // make it obvious that you bound the value to some user controlled value. This helps reduce // the load when auditing for XSS issues. if (trustedContext && parts.length > 1) { throw $interpolateMinErr('noconcat', "Error while interpolating: {0}\nStrict Contextual Escaping disallows " + "interpolations that concatenate multiple expressions when a trusted value is " + "required. See http://docs.angularjs.org/api/ng.$sce", text); } if (!mustHaveExpression || hasInterpolation) { concat.length = length; fn = function(context) { try { for(var i = 0, ii = length, part; i * **Note**: Intervals created by this service must be explicitly destroyed when you are finished * with them. In particular they are not automatically destroyed when a controller's scope or a * directive's element are destroyed. * You should take this into consideration and make sure to always cancel the interval at the * appropriate moment. See the example below for more details on how and when to do this. * * * @param {function()} fn A function that should be called repeatedly. * @param {number} delay Number of milliseconds between each function call. * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat * indefinitely. * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. * @returns {promise} A promise which will be notified on each iteration. * * @example * * * * *
    *
    * Date format:
    * Current time is: *
    * Blood 1 : {{blood_1}} * Blood 2 : {{blood_2}} * * * *
    *
    * *
    *
    */ function interval(fn, delay, count, invokeApply) { var setInterval = $window.setInterval, clearInterval = $window.clearInterval, deferred = $q.defer(), promise = deferred.promise, iteration = 0, skipApply = (isDefined(invokeApply) && !invokeApply); count = isDefined(count) ? count : 0; promise.then(null, null, fn); promise.$$intervalId = setInterval(function tick() { deferred.notify(iteration++); if (count > 0 && iteration >= count) { deferred.resolve(iteration); clearInterval(promise.$$intervalId); delete intervals[promise.$$intervalId]; } if (!skipApply) $rootScope.$apply(); }, delay); intervals[promise.$$intervalId] = deferred; return promise; } /** * @ngdoc method * @name $interval#cancel * * @description * Cancels a task associated with the `promise`. * * @param {promise} promise returned by the `$interval` function. * @returns {boolean} Returns `true` if the task was successfully canceled. */ interval.cancel = function(promise) { if (promise && promise.$$intervalId in intervals) { intervals[promise.$$intervalId].reject('canceled'); $window.clearInterval(promise.$$intervalId); delete intervals[promise.$$intervalId]; return true; } return false; }; return interval; }]; } /** * @ngdoc service * @name $locale * * @description * $locale service provides localization rules for various Angular components. As of right now the * only public api is: * * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) */ function $LocaleProvider(){ this.$get = function() { return { id: 'en-us', NUMBER_FORMATS: { DECIMAL_SEP: '.', GROUP_SEP: ',', PATTERNS: [ { // Decimal Pattern minInt: 1, minFrac: 0, maxFrac: 3, posPre: '', posSuf: '', negPre: '-', negSuf: '', gSize: 3, lgSize: 3 },{ //Currency Pattern minInt: 1, minFrac: 2, maxFrac: 2, posPre: '\u00A4', posSuf: '', negPre: '(\u00A4', negSuf: ')', gSize: 3, lgSize: 3 } ], CURRENCY_SYM: '$' }, DATETIME_FORMATS: { MONTH: 'January,February,March,April,May,June,July,August,September,October,November,December' .split(','), SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','), DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','), SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','), AMPMS: ['AM','PM'], medium: 'MMM d, y h:mm:ss a', short: 'M/d/yy h:mm a', fullDate: 'EEEE, MMMM d, y', longDate: 'MMMM d, y', mediumDate: 'MMM d, y', shortDate: 'M/d/yy', mediumTime: 'h:mm:ss a', shortTime: 'h:mm a' }, pluralCat: function(num) { if (num === 1) { return 'one'; } return 'other'; } }; }; } var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/, DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; var $locationMinErr = minErr('$location'); /** * Encode path using encodeUriSegment, ignoring forward slashes * * @param {string} path Path to encode * @returns {string} */ function encodePath(path) { var segments = path.split('/'), i = segments.length; while (i--) { segments[i] = encodeUriSegment(segments[i]); } return segments.join('/'); } function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) { var parsedUrl = urlResolve(absoluteUrl, appBase); locationObj.$$protocol = parsedUrl.protocol; locationObj.$$host = parsedUrl.hostname; locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null; } function parseAppUrl(relativeUrl, locationObj, appBase) { var prefixed = (relativeUrl.charAt(0) !== '/'); if (prefixed) { relativeUrl = '/' + relativeUrl; } var match = urlResolve(relativeUrl, appBase); locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ? match.pathname.substring(1) : match.pathname); locationObj.$$search = parseKeyValue(match.search); locationObj.$$hash = decodeURIComponent(match.hash); // make sure path starts with '/'; if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') { locationObj.$$path = '/' + locationObj.$$path; } } /** * * @param {string} begin * @param {string} whole * @returns {string} returns text from whole after begin or undefined if it does not begin with * expected string. */ function beginsWith(begin, whole) { if (whole.indexOf(begin) === 0) { return whole.substr(begin.length); } } function stripHash(url) { var index = url.indexOf('#'); return index == -1 ? url : url.substr(0, index); } function stripFile(url) { return url.substr(0, stripHash(url).lastIndexOf('/') + 1); } /* return the server only (scheme://host:port) */ function serverBase(url) { return url.substring(0, url.indexOf('/', url.indexOf('//') + 2)); } /** * LocationHtml5Url represents an url * This object is exposed as $location service when HTML5 mode is enabled and supported * * @constructor * @param {string} appBase application base URL * @param {string} basePrefix url path prefix */ function LocationHtml5Url(appBase, basePrefix) { this.$$html5 = true; basePrefix = basePrefix || ''; var appBaseNoFile = stripFile(appBase); parseAbsoluteUrl(appBase, this, appBase); /** * Parse given html5 (regular) url string into properties * @param {string} newAbsoluteUrl HTML5 url * @private */ this.$$parse = function(url) { var pathUrl = beginsWith(appBaseNoFile, url); if (!isString(pathUrl)) { throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url, appBaseNoFile); } parseAppUrl(pathUrl, this, appBase); if (!this.$$path) { this.$$path = '/'; } this.$$compose(); }; /** * Compose url and update `absUrl` property * @private */ this.$$compose = function() { var search = toKeyValue(this.$$search), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/' }; this.$$parseLinkUrl = function(url, relHref) { var appUrl, prevAppUrl; var rewrittenUrl; if ( (appUrl = beginsWith(appBase, url)) !== undefined ) { prevAppUrl = appUrl; if ( (appUrl = beginsWith(basePrefix, appUrl)) !== undefined ) { rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl); } else { rewrittenUrl = appBase + prevAppUrl; } } else if ( (appUrl = beginsWith(appBaseNoFile, url)) !== undefined ) { rewrittenUrl = appBaseNoFile + appUrl; } else if (appBaseNoFile == url + '/') { rewrittenUrl = appBaseNoFile; } if (rewrittenUrl) { this.$$parse(rewrittenUrl); } return !!rewrittenUrl; }; } /** * LocationHashbangUrl represents url * This object is exposed as $location service when developer doesn't opt into html5 mode. * It also serves as the base class for html5 mode fallback on legacy browsers. * * @constructor * @param {string} appBase application base URL * @param {string} hashPrefix hashbang prefix */ function LocationHashbangUrl(appBase, hashPrefix) { var appBaseNoFile = stripFile(appBase); parseAbsoluteUrl(appBase, this, appBase); /** * Parse given hashbang url into properties * @param {string} url Hashbang url * @private */ this.$$parse = function(url) { var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url); var withoutHashUrl = withoutBaseUrl.charAt(0) == '#' ? beginsWith(hashPrefix, withoutBaseUrl) : (this.$$html5) ? withoutBaseUrl : ''; if (!isString(withoutHashUrl)) { throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url, hashPrefix); } parseAppUrl(withoutHashUrl, this, appBase); this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase); this.$$compose(); /* * In Windows, on an anchor node on documents loaded from * the filesystem, the browser will return a pathname * prefixed with the drive name ('/C:/path') when a * pathname without a drive is set: * * a.setAttribute('href', '/foo') * * a.pathname === '/C:/foo' //true * * Inside of Angular, we're always using pathnames that * do not include drive names for routing. */ function removeWindowsDriveName (path, url, base) { /* Matches paths for file protocol on windows, such as /C:/foo/bar, and captures only /foo/bar. */ var windowsFilePathExp = /^\/[A-Z]:(\/.*)/; var firstPathSegmentMatch; //Get the relative path from the input URL. if (url.indexOf(base) === 0) { url = url.replace(base, ''); } // The input URL intentionally contains a first path segment that ends with a colon. if (windowsFilePathExp.exec(url)) { return path; } firstPathSegmentMatch = windowsFilePathExp.exec(path); return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path; } }; /** * Compose hashbang url and update `absUrl` property * @private */ this.$$compose = function() { var search = toKeyValue(this.$$search), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : ''); }; this.$$parseLinkUrl = function(url, relHref) { if(stripHash(appBase) == stripHash(url)) { this.$$parse(url); return true; } return false; }; } /** * LocationHashbangUrl represents url * This object is exposed as $location service when html5 history api is enabled but the browser * does not support it. * * @constructor * @param {string} appBase application base URL * @param {string} hashPrefix hashbang prefix */ function LocationHashbangInHtml5Url(appBase, hashPrefix) { this.$$html5 = true; LocationHashbangUrl.apply(this, arguments); var appBaseNoFile = stripFile(appBase); this.$$parseLinkUrl = function(url, relHref) { var rewrittenUrl; var appUrl; if ( appBase == stripHash(url) ) { rewrittenUrl = url; } else if ( (appUrl = beginsWith(appBaseNoFile, url)) ) { rewrittenUrl = appBase + hashPrefix + appUrl; } else if ( appBaseNoFile === url + '/') { rewrittenUrl = appBaseNoFile; } if (rewrittenUrl) { this.$$parse(rewrittenUrl); } return !!rewrittenUrl; }; this.$$compose = function() { var search = toKeyValue(this.$$search), hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#' this.$$absUrl = appBase + hashPrefix + this.$$url; }; } LocationHashbangInHtml5Url.prototype = LocationHashbangUrl.prototype = LocationHtml5Url.prototype = { /** * Are we in html5 mode? * @private */ $$html5: false, /** * Has any change been replacing ? * @private */ $$replace: false, /** * @ngdoc method * @name $location#absUrl * * @description * This method is getter only. * * Return full url representation with all segments encoded according to rules specified in * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt). * * @return {string} full url */ absUrl: locationGetter('$$absUrl'), /** * @ngdoc method * @name $location#url * * @description * This method is getter / setter. * * Return url (e.g. `/path?a=b#hash`) when called without any parameter. * * Change path, search and hash, when called with parameter and return `$location`. * * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) * @return {string} url */ url: function(url) { if (isUndefined(url)) return this.$$url; var match = PATH_MATCH.exec(url); if (match[1]) this.path(decodeURIComponent(match[1])); if (match[2] || match[1]) this.search(match[3] || ''); this.hash(match[5] || ''); return this; }, /** * @ngdoc method * @name $location#protocol * * @description * This method is getter only. * * Return protocol of current url. * * @return {string} protocol of current url */ protocol: locationGetter('$$protocol'), /** * @ngdoc method * @name $location#host * * @description * This method is getter only. * * Return host of current url. * * @return {string} host of current url. */ host: locationGetter('$$host'), /** * @ngdoc method * @name $location#port * * @description * This method is getter only. * * Return port of current url. * * @return {Number} port */ port: locationGetter('$$port'), /** * @ngdoc method * @name $location#path * * @description * This method is getter / setter. * * Return path of current url when called without any parameter. * * Change path when called with parameter and return `$location`. * * Note: Path should always begin with forward slash (/), this method will add the forward slash * if it is missing. * * @param {(string|number)=} path New path * @return {string} path */ path: locationGetterSetter('$$path', function(path) { path = path !== null ? path.toString() : ''; return path.charAt(0) == '/' ? path : '/' + path; }), /** * @ngdoc method * @name $location#search * * @description * This method is getter / setter. * * Return search part (as object) of current url when called without any parameter. * * Change search part when called with parameter and return `$location`. * * * ```js * // given url http://example.com/#/some/path?foo=bar&baz=xoxo * var searchObject = $location.search(); * // => {foo: 'bar', baz: 'xoxo'} * * * // set foo to 'yipee' * $location.search('foo', 'yipee'); * // => $location * ``` * * @param {string|Object.|Object.>} search New search params - string or * hash object. * * When called with a single argument the method acts as a setter, setting the `search` component * of `$location` to the specified value. * * If the argument is a hash object containing an array of values, these values will be encoded * as duplicate search parameters in the url. * * @param {(string|Number|Array|boolean)=} paramValue If `search` is a string or number, then `paramValue` * will override only a single search property. * * If `paramValue` is an array, it will override the property of the `search` component of * `$location` specified via the first argument. * * If `paramValue` is `null`, the property specified via the first argument will be deleted. * * If `paramValue` is `true`, the property specified via the first argument will be added with no * value nor trailing equal sign. * * @return {Object} If called with no arguments returns the parsed `search` object. If called with * one or more arguments returns `$location` object itself. */ search: function(search, paramValue) { switch (arguments.length) { case 0: return this.$$search; case 1: if (isString(search) || isNumber(search)) { search = search.toString(); this.$$search = parseKeyValue(search); } else if (isObject(search)) { // remove object undefined or null properties forEach(search, function(value, key) { if (value == null) delete search[key]; }); this.$$search = search; } else { throw $locationMinErr('isrcharg', 'The first argument of the `$location#search()` call must be a string or an object.'); } break; default: if (isUndefined(paramValue) || paramValue === null) { delete this.$$search[search]; } else { this.$$search[search] = paramValue; } } this.$$compose(); return this; }, /** * @ngdoc method * @name $location#hash * * @description * This method is getter / setter. * * Return hash fragment when called without any parameter. * * Change hash fragment when called with parameter and return `$location`. * * @param {(string|number)=} hash New hash fragment * @return {string} hash */ hash: locationGetterSetter('$$hash', function(hash) { return hash !== null ? hash.toString() : ''; }), /** * @ngdoc method * @name $location#replace * * @description * If called, all changes to $location during current `$digest` will be replacing current history * record, instead of adding new one. */ replace: function() { this.$$replace = true; return this; } }; function locationGetter(property) { return function() { return this[property]; }; } function locationGetterSetter(property, preprocess) { return function(value) { if (isUndefined(value)) return this[property]; this[property] = preprocess(value); this.$$compose(); return this; }; } /** * @ngdoc service * @name $location * * @requires $rootElement * * @description * The $location service parses the URL in the browser address bar (based on the * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL * available to your application. Changes to the URL in the address bar are reflected into * $location service and changes to $location are reflected into the browser address bar. * * **The $location service:** * * - Exposes the current URL in the browser address bar, so you can * - Watch and observe the URL. * - Change the URL. * - Synchronizes the URL with the browser when the user * - Changes the address bar. * - Clicks the back or forward button (or clicks a History link). * - Clicks on a link. * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). * * For more information see {@link guide/$location Developer Guide: Using $location} */ /** * @ngdoc provider * @name $locationProvider * @description * Use the `$locationProvider` to configure how the application deep linking paths are stored. */ function $LocationProvider(){ var hashPrefix = '', html5Mode = false; /** * @ngdoc method * @name $locationProvider#hashPrefix * @description * @param {string=} prefix Prefix for hash part (containing path and search) * @returns {*} current value if used as getter or itself (chaining) if used as setter */ this.hashPrefix = function(prefix) { if (isDefined(prefix)) { hashPrefix = prefix; return this; } else { return hashPrefix; } }; /** * @ngdoc method * @name $locationProvider#html5Mode * @description * @param {boolean=} mode Use HTML5 strategy if available. * @returns {*} current value if used as getter or itself (chaining) if used as setter */ this.html5Mode = function(mode) { if (isDefined(mode)) { html5Mode = mode; return this; } else { return html5Mode; } }; /** * @ngdoc event * @name $location#$locationChangeStart * @eventType broadcast on root scope * @description * Broadcasted before a URL will change. This change can be prevented by calling * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more * details about event object. Upon successful change * {@link ng.$location#events_$locationChangeSuccess $locationChangeSuccess} is fired. * * @param {Object} angularEvent Synthetic event object. * @param {string} newUrl New URL * @param {string=} oldUrl URL that was before it was changed. */ /** * @ngdoc event * @name $location#$locationChangeSuccess * @eventType broadcast on root scope * @description * Broadcasted after a URL was changed. * * @param {Object} angularEvent Synthetic event object. * @param {string} newUrl New URL * @param {string=} oldUrl URL that was before it was changed. */ this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', function( $rootScope, $browser, $sniffer, $rootElement) { var $location, LocationMode, baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to '' initialUrl = $browser.url(), appBase; if (html5Mode) { appBase = serverBase(initialUrl) + (baseHref || '/'); LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url; } else { appBase = stripHash(initialUrl); LocationMode = LocationHashbangUrl; } $location = new LocationMode(appBase, '#' + hashPrefix); $location.$$parseLinkUrl(initialUrl, initialUrl); var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i; $rootElement.on('click', function(event) { // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) // currently we open nice url link and redirect then if (event.ctrlKey || event.metaKey || event.which == 2) return; var elm = jqLite(event.target); // traverse the DOM up to find first A tag while (lowercase(elm[0].nodeName) !== 'a') { // ignore rewriting if no A tag (reached root element, or no parent - removed from document) if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; } var absHref = elm.prop('href'); // get the actual href attribute - see // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx var relHref = elm.attr('href') || elm.attr('xlink:href'); if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') { // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during // an animation. absHref = urlResolve(absHref.animVal).href; } // Ignore when url is started with javascript: or mailto: if (IGNORE_URI_REGEXP.test(absHref)) return; if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) { if ($location.$$parseLinkUrl(absHref, relHref)) { // We do a preventDefault for all urls that are part of the angular application, // in html5mode and also without, so that we are able to abort navigation without // getting double entries in the location history. event.preventDefault(); // update location manually if ($location.absUrl() != $browser.url()) { $rootScope.$apply(); // hack to work around FF6 bug 684208 when scenario runner clicks on links window.angular['ff-684208-preventDefault'] = true; } } } }); // rewrite hashbang url <> html5 url if ($location.absUrl() != initialUrl) { $browser.url($location.absUrl(), true); } // update $location when $browser url changes $browser.onUrlChange(function(newUrl) { if ($location.absUrl() != newUrl) { $rootScope.$evalAsync(function() { var oldUrl = $location.absUrl(); $location.$$parse(newUrl); if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl).defaultPrevented) { $location.$$parse(oldUrl); $browser.url(oldUrl); } else { afterLocationChange(oldUrl); } }); if (!$rootScope.$$phase) $rootScope.$digest(); } }); // update browser var changeCounter = 0; $rootScope.$watch(function $locationWatch() { var oldUrl = $browser.url(); var currentReplace = $location.$$replace; if (!changeCounter || oldUrl != $location.absUrl()) { changeCounter++; $rootScope.$evalAsync(function() { if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). defaultPrevented) { $location.$$parse(oldUrl); } else { $browser.url($location.absUrl(), currentReplace); afterLocationChange(oldUrl); } }); } $location.$$replace = false; return changeCounter; }); return $location; function afterLocationChange(oldUrl) { $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); } }]; } /** * @ngdoc service * @name $log * @requires $window * * @description * Simple service for logging. Default implementation safely writes the message * into the browser's console (if present). * * The main purpose of this service is to simplify debugging and troubleshooting. * * The default is to log `debug` messages. You can use * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this. * * @example angular.module('logExample', []) .controller('LogController', ['$scope', '$log', function($scope, $log) { $scope.$log = $log; $scope.message = 'Hello World!'; }]);

    Reload this page with open console, enter text and hit the log button...

    Message:
    */ /** * @ngdoc provider * @name $logProvider * @description * Use the `$logProvider` to configure how the application logs messages */ function $LogProvider(){ var debug = true, self = this; /** * @ngdoc method * @name $logProvider#debugEnabled * @description * @param {boolean=} flag enable or disable debug level messages * @returns {*} current value if used as getter or itself (chaining) if used as setter */ this.debugEnabled = function(flag) { if (isDefined(flag)) { debug = flag; return this; } else { return debug; } }; this.$get = ['$window', function($window){ return { /** * @ngdoc method * @name $log#log * * @description * Write a log message */ log: consoleLog('log'), /** * @ngdoc method * @name $log#info * * @description * Write an information message */ info: consoleLog('info'), /** * @ngdoc method * @name $log#warn * * @description * Write a warning message */ warn: consoleLog('warn'), /** * @ngdoc method * @name $log#error * * @description * Write an error message */ error: consoleLog('error'), /** * @ngdoc method * @name $log#debug * * @description * Write a debug message */ debug: (function () { var fn = consoleLog('debug'); return function() { if (debug) { fn.apply(self, arguments); } }; }()) }; function formatError(arg) { if (arg instanceof Error) { if (arg.stack) { arg = (arg.message && arg.stack.indexOf(arg.message) === -1) ? 'Error: ' + arg.message + '\n' + arg.stack : arg.stack; } else if (arg.sourceURL) { arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; } } return arg; } function consoleLog(type) { var console = $window.console || {}, logFn = console[type] || console.log || noop, hasApply = false; // Note: reading logFn.apply throws an error in IE11 in IE8 document mode. // The reason behind this is that console.log has type "object" in IE8... try { hasApply = !!logFn.apply; } catch (e) {} if (hasApply) { return function() { var args = []; forEach(arguments, function(arg) { args.push(formatError(arg)); }); return logFn.apply(console, args); }; } // we are IE which either doesn't have window.console => this is noop and we do nothing, // or we are IE where console.log doesn't have apply so we log at least first 2 args return function(arg1, arg2) { logFn(arg1, arg2 == null ? '' : arg2); }; } }]; } var $parseMinErr = minErr('$parse'); var promiseWarningCache = {}; var promiseWarning; // Sandboxing Angular Expressions // ------------------------------ // Angular expressions are generally considered safe because these expressions only have direct // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by // obtaining a reference to native JS functions such as the Function constructor. // // As an example, consider the following Angular expression: // // {}.toString.constructor('alert("evil JS code")') // // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits // against the expression language, but not to prevent exploits that were enabled by exposing // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good // practice and therefore we are not even trying to protect against interaction with an object // explicitly exposed in this way. // // In general, it is not possible to access a Window object from an angular expression unless a // window or some DOM object that has a reference to window is published onto a Scope. // Similarly we prevent invocations of function known to be dangerous, as well as assignments to // native objects. // // See https://docs.angularjs.org/guide/security function ensureSafeMemberName(name, fullExpression) { if (name === "__defineGetter__" || name === "__defineSetter__" || name === "__lookupGetter__" || name === "__lookupSetter__" || name === "__proto__") { throw $parseMinErr('isecfld', 'Attempting to access a disallowed field in Angular expressions! ' +'Expression: {0}', fullExpression); } return name; } function ensureSafeObject(obj, fullExpression) { // nifty check if obj is Function that is fast and works across iframes and other contexts if (obj) { if (obj.constructor === obj) { throw $parseMinErr('isecfn', 'Referencing Function in Angular expressions is disallowed! Expression: {0}', fullExpression); } else if (// isWindow(obj) obj.document && obj.location && obj.alert && obj.setInterval) { throw $parseMinErr('isecwindow', 'Referencing the Window in Angular expressions is disallowed! Expression: {0}', fullExpression); } else if (// isElement(obj) obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) { throw $parseMinErr('isecdom', 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}', fullExpression); } else if (// block Object so that we can't get hold of dangerous Object.* methods obj === Object) { throw $parseMinErr('isecobj', 'Referencing Object in Angular expressions is disallowed! Expression: {0}', fullExpression); } } return obj; } var CALL = Function.prototype.call; var APPLY = Function.prototype.apply; var BIND = Function.prototype.bind; function ensureSafeFunction(obj, fullExpression) { if (obj) { if (obj.constructor === obj) { throw $parseMinErr('isecfn', 'Referencing Function in Angular expressions is disallowed! Expression: {0}', fullExpression); } else if (obj === CALL || obj === APPLY || (BIND && obj === BIND)) { throw $parseMinErr('isecff', 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}', fullExpression); } } } var OPERATORS = { /* jshint bitwise : false */ 'null':function(){return null;}, 'true':function(){return true;}, 'false':function(){return false;}, undefined:noop, '+':function(self, locals, a,b){ a=a(self, locals); b=b(self, locals); if (isDefined(a)) { if (isDefined(b)) { return a + b; } return a; } return isDefined(b)?b:undefined;}, '-':function(self, locals, a,b){ a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0); }, '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, '=':noop, '===':function(self, locals, a, b){return a(self, locals)===b(self, locals);}, '!==':function(self, locals, a, b){return a(self, locals)!==b(self, locals);}, '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, '<':function(self, locals, a,b){return a(self, locals)':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, // '|':function(self, locals, a,b){return a|b;}, '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, '!':function(self, locals, a){return !a(self, locals);} }; /* jshint bitwise: true */ var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; ///////////////////////////////////////// /** * @constructor */ var Lexer = function (options) { this.options = options; }; Lexer.prototype = { constructor: Lexer, lex: function (text) { this.text = text; this.index = 0; this.ch = undefined; this.lastCh = ':'; // can start regexp this.tokens = []; while (this.index < this.text.length) { this.ch = this.text.charAt(this.index); if (this.is('"\'')) { this.readString(this.ch); } else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) { this.readNumber(); } else if (this.isIdent(this.ch)) { this.readIdent(); } else if (this.is('(){}[].,;:?')) { this.tokens.push({ index: this.index, text: this.ch }); this.index++; } else if (this.isWhitespace(this.ch)) { this.index++; continue; } else { var ch2 = this.ch + this.peek(); var ch3 = ch2 + this.peek(2); var fn = OPERATORS[this.ch]; var fn2 = OPERATORS[ch2]; var fn3 = OPERATORS[ch3]; if (fn3) { this.tokens.push({index: this.index, text: ch3, fn: fn3}); this.index += 3; } else if (fn2) { this.tokens.push({index: this.index, text: ch2, fn: fn2}); this.index += 2; } else if (fn) { this.tokens.push({ index: this.index, text: this.ch, fn: fn }); this.index += 1; } else { this.throwError('Unexpected next character ', this.index, this.index + 1); } } this.lastCh = this.ch; } return this.tokens; }, is: function(chars) { return chars.indexOf(this.ch) !== -1; }, was: function(chars) { return chars.indexOf(this.lastCh) !== -1; }, peek: function(i) { var num = i || 1; return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false; }, isNumber: function(ch) { return ('0' <= ch && ch <= '9'); }, isWhitespace: function(ch) { // IE treats non-breaking space as \u00A0 return (ch === ' ' || ch === '\r' || ch === '\t' || ch === '\n' || ch === '\v' || ch === '\u00A0'); }, isIdent: function(ch) { return ('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || '_' === ch || ch === '$'); }, isExpOperator: function(ch) { return (ch === '-' || ch === '+' || this.isNumber(ch)); }, throwError: function(error, start, end) { end = end || this.index; var colStr = (isDefined(start) ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']' : ' ' + end); throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].', error, colStr, this.text); }, readNumber: function() { var number = ''; var start = this.index; while (this.index < this.text.length) { var ch = lowercase(this.text.charAt(this.index)); if (ch == '.' || this.isNumber(ch)) { number += ch; } else { var peekCh = this.peek(); if (ch == 'e' && this.isExpOperator(peekCh)) { number += ch; } else if (this.isExpOperator(ch) && peekCh && this.isNumber(peekCh) && number.charAt(number.length - 1) == 'e') { number += ch; } else if (this.isExpOperator(ch) && (!peekCh || !this.isNumber(peekCh)) && number.charAt(number.length - 1) == 'e') { this.throwError('Invalid exponent'); } else { break; } } this.index++; } number = 1 * number; this.tokens.push({ index: start, text: number, literal: true, constant: true, fn: function() { return number; } }); }, readIdent: function() { var parser = this; var ident = ''; var start = this.index; var lastDot, peekIndex, methodName, ch; while (this.index < this.text.length) { ch = this.text.charAt(this.index); if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) { if (ch === '.') lastDot = this.index; ident += ch; } else { break; } this.index++; } //check if this is not a method invocation and if it is back out to last dot if (lastDot) { peekIndex = this.index; while (peekIndex < this.text.length) { ch = this.text.charAt(peekIndex); if (ch === '(') { methodName = ident.substr(lastDot - start + 1); ident = ident.substr(0, lastDot - start); this.index = peekIndex; break; } if (this.isWhitespace(ch)) { peekIndex++; } else { break; } } } var token = { index: start, text: ident }; // OPERATORS is our own object so we don't need to use special hasOwnPropertyFn if (OPERATORS.hasOwnProperty(ident)) { token.fn = OPERATORS[ident]; token.literal = true; token.constant = true; } else { var getter = getterFn(ident, this.options, this.text); token.fn = extend(function(self, locals) { return (getter(self, locals)); }, { assign: function(self, value) { return setter(self, ident, value, parser.text, parser.options); } }); } this.tokens.push(token); if (methodName) { this.tokens.push({ index:lastDot, text: '.' }); this.tokens.push({ index: lastDot + 1, text: methodName }); } }, readString: function(quote) { var start = this.index; this.index++; var string = ''; var rawString = quote; var escape = false; while (this.index < this.text.length) { var ch = this.text.charAt(this.index); rawString += ch; if (escape) { if (ch === 'u') { var hex = this.text.substring(this.index + 1, this.index + 5); if (!hex.match(/[\da-f]{4}/i)) this.throwError('Invalid unicode escape [\\u' + hex + ']'); this.index += 4; string += String.fromCharCode(parseInt(hex, 16)); } else { var rep = ESCAPE[ch]; string = string + (rep || ch); } escape = false; } else if (ch === '\\') { escape = true; } else if (ch === quote) { this.index++; this.tokens.push({ index: start, text: rawString, string: string, literal: true, constant: true, fn: function() { return string; } }); return; } else { string += ch; } this.index++; } this.throwError('Unterminated quote', start); } }; /** * @constructor */ var Parser = function (lexer, $filter, options) { this.lexer = lexer; this.$filter = $filter; this.options = options; }; Parser.ZERO = extend(function () { return 0; }, { constant: true }); Parser.prototype = { constructor: Parser, parse: function (text) { this.text = text; this.tokens = this.lexer.lex(text); var value = this.statements(); if (this.tokens.length !== 0) { this.throwError('is an unexpected token', this.tokens[0]); } value.literal = !!value.literal; value.constant = !!value.constant; return value; }, primary: function () { var primary; if (this.expect('(')) { primary = this.filterChain(); this.consume(')'); } else if (this.expect('[')) { primary = this.arrayDeclaration(); } else if (this.expect('{')) { primary = this.object(); } else { var token = this.expect(); primary = token.fn; if (!primary) { this.throwError('not a primary expression', token); } primary.literal = !!token.literal; primary.constant = !!token.constant; } var next, context; while ((next = this.expect('(', '[', '.'))) { if (next.text === '(') { primary = this.functionCall(primary, context); context = null; } else if (next.text === '[') { context = primary; primary = this.objectIndex(primary); } else if (next.text === '.') { context = primary; primary = this.fieldAccess(primary); } else { this.throwError('IMPOSSIBLE'); } } return primary; }, throwError: function(msg, token) { throw $parseMinErr('syntax', 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].', token.text, msg, (token.index + 1), this.text, this.text.substring(token.index)); }, peekToken: function() { if (this.tokens.length === 0) throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text); return this.tokens[0]; }, peek: function(e1, e2, e3, e4) { if (this.tokens.length > 0) { var token = this.tokens[0]; var t = token.text; if (t === e1 || t === e2 || t === e3 || t === e4 || (!e1 && !e2 && !e3 && !e4)) { return token; } } return false; }, expect: function(e1, e2, e3, e4){ var token = this.peek(e1, e2, e3, e4); if (token) { this.tokens.shift(); return token; } return false; }, consume: function(e1){ if (!this.expect(e1)) { this.throwError('is unexpected, expecting [' + e1 + ']', this.peek()); } }, unaryFn: function(fn, right) { return extend(function(self, locals) { return fn(self, locals, right); }, { constant:right.constant }); }, ternaryFn: function(left, middle, right){ return extend(function(self, locals){ return left(self, locals) ? middle(self, locals) : right(self, locals); }, { constant: left.constant && middle.constant && right.constant }); }, binaryFn: function(left, fn, right) { return extend(function(self, locals) { return fn(self, locals, left, right); }, { constant:left.constant && right.constant }); }, statements: function() { var statements = []; while (true) { if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) statements.push(this.filterChain()); if (!this.expect(';')) { // optimize for the common case where there is only one statement. // TODO(size): maybe we should not support multiple statements? return (statements.length === 1) ? statements[0] : function(self, locals) { var value; for (var i = 0; i < statements.length; i++) { var statement = statements[i]; if (statement) { value = statement(self, locals); } } return value; }; } } }, filterChain: function() { var left = this.expression(); var token; while (true) { if ((token = this.expect('|'))) { left = this.binaryFn(left, token.fn, this.filter()); } else { return left; } } }, filter: function() { var token = this.expect(); var fn = this.$filter(token.text); var argsFn = []; while (true) { if ((token = this.expect(':'))) { argsFn.push(this.expression()); } else { var fnInvoke = function(self, locals, input) { var args = [input]; for (var i = 0; i < argsFn.length; i++) { args.push(argsFn[i](self, locals)); } return fn.apply(self, args); }; return function() { return fnInvoke; }; } } }, expression: function() { return this.assignment(); }, assignment: function() { var left = this.ternary(); var right; var token; if ((token = this.expect('='))) { if (!left.assign) { this.throwError('implies assignment but [' + this.text.substring(0, token.index) + '] can not be assigned to', token); } right = this.ternary(); return function(scope, locals) { return left.assign(scope, right(scope, locals), locals); }; } return left; }, ternary: function() { var left = this.logicalOR(); var middle; var token; if ((token = this.expect('?'))) { middle = this.assignment(); if ((token = this.expect(':'))) { return this.ternaryFn(left, middle, this.assignment()); } else { this.throwError('expected :', token); } } else { return left; } }, logicalOR: function() { var left = this.logicalAND(); var token; while (true) { if ((token = this.expect('||'))) { left = this.binaryFn(left, token.fn, this.logicalAND()); } else { return left; } } }, logicalAND: function() { var left = this.equality(); var token; if ((token = this.expect('&&'))) { left = this.binaryFn(left, token.fn, this.logicalAND()); } return left; }, equality: function() { var left = this.relational(); var token; if ((token = this.expect('==','!=','===','!=='))) { left = this.binaryFn(left, token.fn, this.equality()); } return left; }, relational: function() { var left = this.additive(); var token; if ((token = this.expect('<', '>', '<=', '>='))) { left = this.binaryFn(left, token.fn, this.relational()); } return left; }, additive: function() { var left = this.multiplicative(); var token; while ((token = this.expect('+','-'))) { left = this.binaryFn(left, token.fn, this.multiplicative()); } return left; }, multiplicative: function() { var left = this.unary(); var token; while ((token = this.expect('*','/','%'))) { left = this.binaryFn(left, token.fn, this.unary()); } return left; }, unary: function() { var token; if (this.expect('+')) { return this.primary(); } else if ((token = this.expect('-'))) { return this.binaryFn(Parser.ZERO, token.fn, this.unary()); } else if ((token = this.expect('!'))) { return this.unaryFn(token.fn, this.unary()); } else { return this.primary(); } }, fieldAccess: function(object) { var parser = this; var field = this.expect().text; var getter = getterFn(field, this.options, this.text); return extend(function(scope, locals, self) { return getter(self || object(scope, locals)); }, { assign: function(scope, value, locals) { var o = object(scope, locals); if (!o) object.assign(scope, o = {}); return setter(o, field, value, parser.text, parser.options); } }); }, objectIndex: function(obj) { var parser = this; var indexFn = this.expression(); this.consume(']'); return extend(function(self, locals) { var o = obj(self, locals), i = indexFn(self, locals), v, p; ensureSafeMemberName(i, parser.text); if (!o) return undefined; v = ensureSafeObject(o[i], parser.text); if (v && v.then && parser.options.unwrapPromises) { p = v; if (!('$$v' in v)) { p.$$v = undefined; p.then(function(val) { p.$$v = val; }); } v = v.$$v; } return v; }, { assign: function(self, value, locals) { var key = ensureSafeMemberName(indexFn(self, locals), parser.text); // prevent overwriting of Function.constructor which would break ensureSafeObject check var o = ensureSafeObject(obj(self, locals), parser.text); if (!o) obj.assign(self, o = {}); return o[key] = value; } }); }, functionCall: function(fn, contextGetter) { var argsFn = []; if (this.peekToken().text !== ')') { do { argsFn.push(this.expression()); } while (this.expect(',')); } this.consume(')'); var parser = this; return function(scope, locals) { var args = []; var context = contextGetter ? contextGetter(scope, locals) : scope; for (var i = 0; i < argsFn.length; i++) { args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text)); } var fnPtr = fn(scope, locals, context) || noop; ensureSafeObject(context, parser.text); ensureSafeFunction(fnPtr, parser.text); // IE doesn't have apply for some native functions var v = fnPtr.apply ? fnPtr.apply(context, args) : fnPtr(args[0], args[1], args[2], args[3], args[4]); return ensureSafeObject(v, parser.text); }; }, // This is used with json array declaration arrayDeclaration: function () { var elementFns = []; var allConstant = true; if (this.peekToken().text !== ']') { do { if (this.peek(']')) { // Support trailing commas per ES5.1. break; } var elementFn = this.expression(); elementFns.push(elementFn); if (!elementFn.constant) { allConstant = false; } } while (this.expect(',')); } this.consume(']'); return extend(function(self, locals) { var array = []; for (var i = 0; i < elementFns.length; i++) { array.push(elementFns[i](self, locals)); } return array; }, { literal: true, constant: allConstant }); }, object: function () { var keyValues = []; var allConstant = true; if (this.peekToken().text !== '}') { do { if (this.peek('}')) { // Support trailing commas per ES5.1. break; } var token = this.expect(), key = token.string || token.text; this.consume(':'); var value = this.expression(); keyValues.push({key: key, value: value}); if (!value.constant) { allConstant = false; } } while (this.expect(',')); } this.consume('}'); return extend(function(self, locals) { var object = {}; for (var i = 0; i < keyValues.length; i++) { var keyValue = keyValues[i]; object[keyValue.key] = keyValue.value(self, locals); } return object; }, { literal: true, constant: allConstant }); } }; ////////////////////////////////////////////////// // Parser helper functions ////////////////////////////////////////////////// function setter(obj, path, setValue, fullExp, options) { ensureSafeObject(obj, fullExp); //needed? options = options || {}; var element = path.split('.'), key; for (var i = 0; element.length > 1; i++) { key = ensureSafeMemberName(element.shift(), fullExp); var propertyObj = ensureSafeObject(obj[key], fullExp); if (!propertyObj) { propertyObj = {}; obj[key] = propertyObj; } obj = propertyObj; if (obj.then && options.unwrapPromises) { promiseWarning(fullExp); if (!("$$v" in obj)) { (function(promise) { promise.then(function(val) { promise.$$v = val; }); } )(obj); } if (obj.$$v === undefined) { obj.$$v = {}; } obj = obj.$$v; } } key = ensureSafeMemberName(element.shift(), fullExp); ensureSafeObject(obj[key], fullExp); obj[key] = setValue; return setValue; } var getterFnCacheDefault = {}; var getterFnCacheExpensive = {}; function isPossiblyDangerousMemberName(name) { return name == 'constructor'; } /** * Implementation of the "Black Hole" variant from: * - http://jsperf.com/angularjs-parse-getter/4 * - http://jsperf.com/path-evaluation-simplified/7 */ function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, options) { ensureSafeMemberName(key0, fullExp); ensureSafeMemberName(key1, fullExp); ensureSafeMemberName(key2, fullExp); ensureSafeMemberName(key3, fullExp); ensureSafeMemberName(key4, fullExp); var eso = function(o) { return ensureSafeObject(o, fullExp); }; var expensiveChecks = options.expensiveChecks; var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity; var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity; var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity; var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity; var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity; return !options.unwrapPromises ? function cspSafeGetter(scope, locals) { var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope; if (pathVal == null) return pathVal; pathVal = eso0(pathVal[key0]); if (!key1) return pathVal; if (pathVal == null) return undefined; pathVal = eso1(pathVal[key1]); if (!key2) return pathVal; if (pathVal == null) return undefined; pathVal = eso2(pathVal[key2]); if (!key3) return pathVal; if (pathVal == null) return undefined; pathVal = eso3(pathVal[key3]); if (!key4) return pathVal; if (pathVal == null) return undefined; pathVal = eso4(pathVal[key4]); return pathVal; } : function cspSafePromiseEnabledGetter(scope, locals) { var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, promise; if (pathVal == null) return pathVal; pathVal = eso0(pathVal[key0]); if (pathVal && pathVal.then) { promiseWarning(fullExp); if (!("$$v" in pathVal)) { promise = pathVal; promise.$$v = undefined; promise.then(function(val) { promise.$$v = eso0(val); }); } pathVal = eso0(pathVal.$$v); } if (!key1) return pathVal; if (pathVal == null) return undefined; pathVal = eso1(pathVal[key1]); if (pathVal && pathVal.then) { promiseWarning(fullExp); if (!("$$v" in pathVal)) { promise = pathVal; promise.$$v = undefined; promise.then(function(val) { promise.$$v = eso1(val); }); } pathVal = eso1(pathVal.$$v); } if (!key2) return pathVal; if (pathVal == null) return undefined; pathVal = eso2(pathVal[key2]); if (pathVal && pathVal.then) { promiseWarning(fullExp); if (!("$$v" in pathVal)) { promise = pathVal; promise.$$v = undefined; promise.then(function(val) { promise.$$v = eso2(val); }); } pathVal = eso2(pathVal.$$v); } if (!key3) return pathVal; if (pathVal == null) return undefined; pathVal = eso3(pathVal[key3]); if (pathVal && pathVal.then) { promiseWarning(fullExp); if (!("$$v" in pathVal)) { promise = pathVal; promise.$$v = undefined; promise.then(function(val) { promise.$$v = eso3(val); }); } pathVal = eso3(pathVal.$$v); } if (!key4) return pathVal; if (pathVal == null) return undefined; pathVal = eso4(pathVal[key4]); if (pathVal && pathVal.then) { promiseWarning(fullExp); if (!("$$v" in pathVal)) { promise = pathVal; promise.$$v = undefined; promise.then(function(val) { promise.$$v = eso4(val); }); } pathVal = eso4(pathVal.$$v); } return pathVal; }; } function getterFnWithExtraArgs(fn, fullExpression) { return function(s, l) { return fn(s, l, promiseWarning, ensureSafeObject, fullExpression); }; } function getterFn(path, options, fullExp) { var expensiveChecks = options.expensiveChecks; var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault); // Check whether the cache has this getter already. // We can use hasOwnProperty directly on the cache because we ensure, // see below, that the cache never stores a path called 'hasOwnProperty' if (getterFnCache.hasOwnProperty(path)) { return getterFnCache[path]; } var pathKeys = path.split('.'), pathKeysLength = pathKeys.length, fn; // http://jsperf.com/angularjs-parse-getter/6 if (options.csp) { if (pathKeysLength < 6) { fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, options); } else { fn = function(scope, locals) { var i = 0, val; do { val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], fullExp, options)(scope, locals); locals = undefined; // clear after first iteration scope = val; } while (i < pathKeysLength); return val; }; } } else { var code = 'var p;\n'; if (expensiveChecks) { code += 's = eso(s, fe);\nl = eso(l, fe);\n'; } var needsEnsureSafeObject = expensiveChecks; forEach(pathKeys, function(key, index) { ensureSafeMemberName(key, fullExp); var lookupJs = (index // we simply dereference 's' on any .dot notation ? 's' // but if we are first then we check locals first, and if so read it first : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '["' + key + '"]'; var wrapWithEso = expensiveChecks || isPossiblyDangerousMemberName(key); if (wrapWithEso) { lookupJs = 'eso(' + lookupJs + ', fe)'; needsEnsureSafeObject = true; } code += 'if(s == null) return undefined;\n' + 's=' + lookupJs + ';\n'; if (options.unwrapPromises) { code += 'if (s && s.then) {\n' + ' pw("' + fullExp.replace(/(["\r\n])/g, '\\$1') + '");\n' + ' if (!("$$v" in s)) {\n' + ' p=s;\n' + ' p.$$v = undefined;\n' + ' p.then(function(v) {p.$$v=' + (wrapWithEso ? 'eso(v)' : 'v') + ';});\n' + '}\n' + ' s=' + (wrapWithEso ? 'eso(s.$$v)' : 's.$$v') + '\n' + '}\n'; } }); code += 'return s;'; /* jshint -W054 */ // s=scope, l=locals, pw=promiseWarning, eso=ensureSafeObject, fe=fullExpression var evaledFnGetter = new Function('s', 'l', 'pw', 'eso', 'fe', code); /* jshint +W054 */ evaledFnGetter.toString = valueFn(code); if (needsEnsureSafeObject || options.unwrapPromises) { evaledFnGetter = getterFnWithExtraArgs(evaledFnGetter, fullExp); } fn = evaledFnGetter; } // Only cache the value if it's not going to mess up the cache object // This is more performant that using Object.prototype.hasOwnProperty.call if (path !== 'hasOwnProperty') { getterFnCache[path] = fn; } return fn; } /////////////////////////////////// /** * @ngdoc service * @name $parse * @kind function * * @description * * Converts Angular {@link guide/expression expression} into a function. * * ```js * var getter = $parse('user.name'); * var setter = getter.assign; * var context = {user:{name:'angular'}}; * var locals = {user:{name:'local'}}; * * expect(getter(context)).toEqual('angular'); * setter(context, 'newValue'); * expect(context.user.name).toEqual('newValue'); * expect(getter(context, locals)).toEqual('local'); * ``` * * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. * * The returned function also has the following properties: * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript * literal. * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript * constant literals. * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be * set to a function to change its value on the given context. * */ /** * @ngdoc provider * @name $parseProvider * @kind function * * @description * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse} * service. */ function $ParseProvider() { var cacheDefault = {}; var cacheExpensive = {}; var $parseOptions = { csp: false, unwrapPromises: false, logPromiseWarnings: true, expensiveChecks: false }; /** * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. * * @ngdoc method * @name $parseProvider#unwrapPromises * @description * * **This feature is deprecated, see deprecation notes below for more info** * * If set to true (default is false), $parse will unwrap promises automatically when a promise is * found at any part of the expression. In other words, if set to true, the expression will always * result in a non-promise value. * * While the promise is unresolved, it's treated as undefined, but once resolved and fulfilled, * the fulfillment value is used in place of the promise while evaluating the expression. * * **Deprecation notice** * * This is a feature that didn't prove to be wildly useful or popular, primarily because of the * dichotomy between data access in templates (accessed as raw values) and controller code * (accessed as promises). * * In most code we ended up resolving promises manually in controllers anyway and thus unifying * the model access there. * * Other downsides of automatic promise unwrapping: * * - when building components it's often desirable to receive the raw promises * - adds complexity and slows down expression evaluation * - makes expression code pre-generation unattractive due to the amount of code that needs to be * generated * - makes IDE auto-completion and tool support hard * * **Warning Logs** * * If the unwrapping is enabled, Angular will log a warning about each expression that unwraps a * promise (to reduce the noise, each expression is logged only once). To disable this logging use * `$parseProvider.logPromiseWarnings(false)` api. * * * @param {boolean=} value New value. * @returns {boolean|self} Returns the current setting when used as getter and self if used as * setter. */ this.unwrapPromises = function(value) { if (isDefined(value)) { $parseOptions.unwrapPromises = !!value; return this; } else { return $parseOptions.unwrapPromises; } }; /** * @deprecated Promise unwrapping via $parse is deprecated and will be removed in the future. * * @ngdoc method * @name $parseProvider#logPromiseWarnings * @description * * Controls whether Angular should log a warning on any encounter of a promise in an expression. * * The default is set to `true`. * * This setting applies only if `$parseProvider.unwrapPromises` setting is set to true as well. * * @param {boolean=} value New value. * @returns {boolean|self} Returns the current setting when used as getter and self if used as * setter. */ this.logPromiseWarnings = function(value) { if (isDefined(value)) { $parseOptions.logPromiseWarnings = value; return this; } else { return $parseOptions.logPromiseWarnings; } }; this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) { $parseOptions.csp = $sniffer.csp; var $parseOptionsExpensive = { csp: $parseOptions.csp, unwrapPromises: $parseOptions.unwrapPromises, logPromiseWarnings: $parseOptions.logPromiseWarnings, expensiveChecks: true }; promiseWarning = function promiseWarningFn(fullExp) { if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return; promiseWarningCache[fullExp] = true; $log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' + 'Automatic unwrapping of promises in Angular expressions is deprecated.'); }; return function(exp, expensiveChecks) { var parsedExpression; switch (typeof exp) { case 'string': var cache = (expensiveChecks ? cacheExpensive : cacheDefault); if (cache.hasOwnProperty(exp)) { return cache[exp]; } var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions; var lexer = new Lexer(parseOptions); var parser = new Parser(lexer, $filter, parseOptions); parsedExpression = parser.parse(exp); if (exp !== 'hasOwnProperty') { // Only cache the value if it's not going to mess up the cache object // This is more performant that using Object.prototype.hasOwnProperty.call cache[exp] = parsedExpression; } return parsedExpression; case 'function': return exp; default: return noop; } }; }]; } /** * @ngdoc service * @name $q * @requires $rootScope * * @description * A service that helps you run functions asynchronously, and use their return values (or exceptions) * when they are done processing. * * This is an implementation of promises/deferred objects inspired by * [Kris Kowal's Q](https://github.com/kriskowal/q). * * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an * interface for interacting with an object that represents the result of an action that is * performed asynchronously, and may or may not be finished at any given point in time. * * From the perspective of dealing with error handling, deferred and promise APIs are to * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming. * * ```js * // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet` * // are available in the current lexical scope (they could have been injected or passed in). * * function asyncGreet(name) { * var deferred = $q.defer(); * * setTimeout(function() { * deferred.notify('About to greet ' + name + '.'); * * if (okToGreet(name)) { * deferred.resolve('Hello, ' + name + '!'); * } else { * deferred.reject('Greeting ' + name + ' is not allowed.'); * } * }, 1000); * * return deferred.promise; * } * * var promise = asyncGreet('Robin Hood'); * promise.then(function(greeting) { * alert('Success: ' + greeting); * }, function(reason) { * alert('Failed: ' + reason); * }, function(update) { * alert('Got notification: ' + update); * }); * ``` * * At first it might not be obvious why this extra complexity is worth the trouble. The payoff * comes in the way of guarantees that promise and deferred APIs make, see * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md. * * Additionally the promise api allows for composition that is very hard to do with the * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the * section on serial or parallel joining of promises. * * * # The Deferred API * * A new instance of deferred is constructed by calling `$q.defer()`. * * The purpose of the deferred object is to expose the associated Promise instance as well as APIs * that can be used for signaling the successful or unsuccessful completion, as well as the status * of the task. * * **Methods** * * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection * constructed via `$q.reject`, the promise will be rejected instead. * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to * resolving it with a rejection constructed via `$q.reject`. * - `notify(value)` - provides updates on the status of the promise's execution. This may be called * multiple times before the promise is either resolved or rejected. * * **Properties** * * - promise – `{Promise}` – promise object associated with this deferred. * * * # The Promise API * * A new promise instance is created when a deferred instance is created and can be retrieved by * calling `deferred.promise`. * * The purpose of the promise object is to allow for interested parties to get access to the result * of the deferred task when it completes. * * **Methods** * * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously * as soon as the result is available. The callbacks are called with a single argument: the result * or rejection reason. Additionally, the notify callback may be called zero or more times to * provide a progress indication, before the promise is resolved or rejected. * * This method *returns a new promise* which is resolved or rejected via the return value of the * `successCallback`, `errorCallback`. It also notifies via the return value of the * `notifyCallback` method. The promise can not be resolved or rejected from the notifyCallback * method. * * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)` * * Because `catch` is a reserved word in JavaScript and reserved keywords are not supported as * property names by ES3, you'll need to invoke the method like `promise['catch'](callback)` or * `promise.then(null, errorCallback)` to make your code IE8 and Android 2.x compatible. * * - `finally(callback)` – allows you to observe either the fulfillment or rejection of a promise, * but to do so without modifying the final value. This is useful to release resources or do some * clean-up that needs to be done whether the promise was rejected or resolved. See the [full * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for * more information. * * Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as * property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to * make your code IE8 and Android 2.x compatible. * * # Chaining promises * * Because calling the `then` method of a promise returns a new derived promise, it is easily * possible to create a chain of promises: * * ```js * promiseB = promiseA.then(function(result) { * return result + 1; * }); * * // promiseB will be resolved immediately after promiseA is resolved and its value * // will be the result of promiseA incremented by 1 * ``` * * It is possible to create chains of any length and since a promise can be resolved with another * promise (which will defer its resolution further), it is possible to pause/defer resolution of * the promises at any point in the chain. This makes it possible to implement powerful APIs like * $http's response interceptors. * * * # Differences between Kris Kowal's Q and $q * * There are two main differences: * * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation * mechanism in angular, which means faster propagation of resolution or rejection into your * models and avoiding unnecessary browser repaints, which would result in flickering UI. * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains * all the important functionality needed for common async tasks. * * # Testing * * ```js * it('should simulate promise', inject(function($q, $rootScope) { * var deferred = $q.defer(); * var promise = deferred.promise; * var resolvedValue; * * promise.then(function(value) { resolvedValue = value; }); * expect(resolvedValue).toBeUndefined(); * * // Simulate resolving of promise * deferred.resolve(123); * // Note that the 'then' function does not get called synchronously. * // This is because we want the promise API to always be async, whether or not * // it got called synchronously or asynchronously. * expect(resolvedValue).toBeUndefined(); * * // Propagate promise resolution to 'then' functions using $apply(). * $rootScope.$apply(); * expect(resolvedValue).toEqual(123); * })); * ``` */ function $QProvider() { this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { return qFactory(function(callback) { $rootScope.$evalAsync(callback); }, $exceptionHandler); }]; } /** * Constructs a promise manager. * * @param {function(Function)} nextTick Function for executing functions in the next turn. * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for * debugging purposes. * @returns {object} Promise manager. */ function qFactory(nextTick, exceptionHandler) { /** * @ngdoc method * @name $q#defer * @kind function * * @description * Creates a `Deferred` object which represents a task which will finish in the future. * * @returns {Deferred} Returns a new instance of deferred. */ var defer = function() { var pending = [], value, deferred; deferred = { resolve: function(val) { if (pending) { var callbacks = pending; pending = undefined; value = ref(val); if (callbacks.length) { nextTick(function() { var callback; for (var i = 0, ii = callbacks.length; i < ii; i++) { callback = callbacks[i]; value.then(callback[0], callback[1], callback[2]); } }); } } }, reject: function(reason) { deferred.resolve(createInternalRejectedPromise(reason)); }, notify: function(progress) { if (pending) { var callbacks = pending; if (pending.length) { nextTick(function() { var callback; for (var i = 0, ii = callbacks.length; i < ii; i++) { callback = callbacks[i]; callback[2](progress); } }); } } }, promise: { then: function(callback, errback, progressback) { var result = defer(); var wrappedCallback = function(value) { try { result.resolve((isFunction(callback) ? callback : defaultCallback)(value)); } catch(e) { result.reject(e); exceptionHandler(e); } }; var wrappedErrback = function(reason) { try { result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); } catch(e) { result.reject(e); exceptionHandler(e); } }; var wrappedProgressback = function(progress) { try { result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress)); } catch(e) { exceptionHandler(e); } }; if (pending) { pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]); } else { value.then(wrappedCallback, wrappedErrback, wrappedProgressback); } return result.promise; }, "catch": function(callback) { return this.then(null, callback); }, "finally": function(callback) { function makePromise(value, resolved) { var result = defer(); if (resolved) { result.resolve(value); } else { result.reject(value); } return result.promise; } function handleCallback(value, isResolved) { var callbackOutput = null; try { callbackOutput = (callback ||defaultCallback)(); } catch(e) { return makePromise(e, false); } if (isPromiseLike(callbackOutput)) { return callbackOutput.then(function() { return makePromise(value, isResolved); }, function(error) { return makePromise(error, false); }); } else { return makePromise(value, isResolved); } } return this.then(function(value) { return handleCallback(value, true); }, function(error) { return handleCallback(error, false); }); } } }; return deferred; }; var ref = function(value) { if (isPromiseLike(value)) return value; return { then: function(callback) { var result = defer(); nextTick(function() { result.resolve(callback(value)); }); return result.promise; } }; }; /** * @ngdoc method * @name $q#reject * @kind function * * @description * Creates a promise that is resolved as rejected with the specified `reason`. This api should be * used to forward rejection in a chain of promises. If you are dealing with the last promise in * a promise chain, you don't need to worry about it. * * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via * a promise error callback and you want to forward the error to the promise derived from the * current promise, you have to "rethrow" the error by returning a rejection constructed via * `reject`. * * ```js * promiseB = promiseA.then(function(result) { * // success: do something and resolve promiseB * // with the old or a new result * return result; * }, function(reason) { * // error: handle the error if possible and * // resolve promiseB with newPromiseOrValue, * // otherwise forward the rejection to promiseB * if (canHandle(reason)) { * // handle the error and recover * return newPromiseOrValue; * } * return $q.reject(reason); * }); * ``` * * @param {*} reason Constant, message, exception or an object representing the rejection reason. * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. */ var reject = function(reason) { var result = defer(); result.reject(reason); return result.promise; }; var createInternalRejectedPromise = function(reason) { return { then: function(callback, errback) { var result = defer(); nextTick(function() { try { result.resolve((isFunction(errback) ? errback : defaultErrback)(reason)); } catch(e) { result.reject(e); exceptionHandler(e); } }); return result.promise; } }; }; /** * @ngdoc method * @name $q#when * @kind function * * @description * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. * This is useful when you are dealing with an object that might or might not be a promise, or if * the promise comes from a source that can't be trusted. * * @param {*} value Value or a promise * @returns {Promise} Returns a promise of the passed value or promise */ var when = function(value, callback, errback, progressback) { var result = defer(), done; var wrappedCallback = function(value) { try { return (isFunction(callback) ? callback : defaultCallback)(value); } catch (e) { exceptionHandler(e); return reject(e); } }; var wrappedErrback = function(reason) { try { return (isFunction(errback) ? errback : defaultErrback)(reason); } catch (e) { exceptionHandler(e); return reject(e); } }; var wrappedProgressback = function(progress) { try { return (isFunction(progressback) ? progressback : defaultCallback)(progress); } catch (e) { exceptionHandler(e); } }; nextTick(function() { ref(value).then(function(value) { if (done) return; done = true; result.resolve(ref(value).then(wrappedCallback, wrappedErrback, wrappedProgressback)); }, function(reason) { if (done) return; done = true; result.resolve(wrappedErrback(reason)); }, function(progress) { if (done) return; result.notify(wrappedProgressback(progress)); }); }); return result.promise; }; function defaultCallback(value) { return value; } function defaultErrback(reason) { return reject(reason); } /** * @ngdoc method * @name $q#all * @kind function * * @description * Combines multiple promises into a single promise that is resolved when all of the input * promises are resolved. * * @param {Array.|Object.} promises An array or hash of promises. * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values, * each value corresponding to the promise at the same index/key in the `promises` array/hash. * If any of the promises is resolved with a rejection, this resulting promise will be rejected * with the same rejection value. */ function all(promises) { var deferred = defer(), counter = 0, results = isArray(promises) ? [] : {}; forEach(promises, function(promise, key) { counter++; ref(promise).then(function(value) { if (results.hasOwnProperty(key)) return; results[key] = value; if (!(--counter)) deferred.resolve(results); }, function(reason) { if (results.hasOwnProperty(key)) return; deferred.reject(reason); }); }); if (counter === 0) { deferred.resolve(results); } return deferred.promise; } return { defer: defer, reject: reject, when: when, all: all }; } function $$RAFProvider(){ //rAF this.$get = ['$window', '$timeout', function($window, $timeout) { var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame; var cancelAnimationFrame = $window.cancelAnimationFrame || $window.webkitCancelAnimationFrame || $window.mozCancelAnimationFrame || $window.webkitCancelRequestAnimationFrame; var rafSupported = !!requestAnimationFrame; var raf = rafSupported ? function(fn) { var id = requestAnimationFrame(fn); return function() { cancelAnimationFrame(id); }; } : function(fn) { var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666 return function() { $timeout.cancel(timer); }; }; raf.supported = rafSupported; return raf; }]; } /** * DESIGN NOTES * * The design decisions behind the scope are heavily favored for speed and memory consumption. * * The typical use of scope is to watch the expressions, which most of the time return the same * value as last time so we optimize the operation. * * Closures construction is expensive in terms of speed as well as memory: * - No closures, instead use prototypical inheritance for API * - Internal state needs to be stored on scope directly, which means that private state is * exposed as $$____ properties * * Loop operations are optimized by using while(count--) { ... } * - this means that in order to keep the same order of execution as addition we have to add * items to the array at the beginning (unshift) instead of at the end (push) * * Child scopes are created and removed often * - Using an array would be slow since inserts in middle are expensive so we use linked list * * There are few watches then a lot of observers. This is why you don't want the observer to be * implemented in the same way as watch. Watch requires return of initialization function which * are expensive to construct. */ /** * @ngdoc provider * @name $rootScopeProvider * @description * * Provider for the $rootScope service. */ /** * @ngdoc method * @name $rootScopeProvider#digestTtl * @description * * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and * assuming that the model is unstable. * * The current default is 10 iterations. * * In complex applications it's possible that the dependencies between `$watch`s will result in * several digest iterations. However if an application needs more than the default 10 digest * iterations for its model to stabilize then you should investigate what is causing the model to * continuously change during the digest. * * Increasing the TTL could have performance implications, so you should not change it without * proper justification. * * @param {number} limit The number of digest iterations. */ /** * @ngdoc service * @name $rootScope * @description * * Every application has a single root {@link ng.$rootScope.Scope scope}. * All other scopes are descendant scopes of the root scope. Scopes provide separation * between the model and the view, via a mechanism for watching the model for changes. * They also provide an event emission/broadcast and subscription facility. See the * {@link guide/scope developer guide on scopes}. */ function $RootScopeProvider(){ var TTL = 10; var $rootScopeMinErr = minErr('$rootScope'); var lastDirtyWatch = null; this.digestTtl = function(value) { if (arguments.length) { TTL = value; } return TTL; }; this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser', function( $injector, $exceptionHandler, $parse, $browser) { /** * @ngdoc type * @name $rootScope.Scope * * @description * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the * {@link auto.$injector $injector}. Child scopes are created using the * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when * compiled HTML template is executed.) * * Here is a simple scope snippet to show how you can interact with the scope. * ```html * * ``` * * # Inheritance * A scope can inherit from a parent scope, as in this example: * ```js var parent = $rootScope; var child = parent.$new(); parent.salutation = "Hello"; child.name = "World"; expect(child.salutation).toEqual('Hello'); child.salutation = "Welcome"; expect(child.salutation).toEqual('Welcome'); expect(parent.salutation).toEqual('Hello'); * ``` * * * @param {Object.=} providers Map of service factory which need to be * provided for the current scope. Defaults to {@link ng}. * @param {Object.=} instanceCache Provides pre-instantiated services which should * append/override services provided by `providers`. This is handy * when unit-testing and having the need to override a default * service. * @returns {Object} Newly created scope. * */ function Scope() { this.$id = nextUid(); this.$$phase = this.$parent = this.$$watchers = this.$$nextSibling = this.$$prevSibling = this.$$childHead = this.$$childTail = null; this['this'] = this.$root = this; this.$$destroyed = false; this.$$asyncQueue = []; this.$$postDigestQueue = []; this.$$listeners = {}; this.$$listenerCount = {}; this.$$isolateBindings = {}; } /** * @ngdoc property * @name $rootScope.Scope#$id * * @description * Unique scope ID (monotonically increasing) useful for debugging. */ /** * @ngdoc property * @name $rootScope.Scope#$parent * * @description * Reference to the parent scope. */ /** * @ngdoc property * @name $rootScope.Scope#$root * * @description * Reference to the root scope. */ Scope.prototype = { constructor: Scope, /** * @ngdoc method * @name $rootScope.Scope#$new * @kind function * * @description * Creates a new child {@link ng.$rootScope.Scope scope}. * * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event. * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. * * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is * desired for the scope and its child scopes to be permanently detached from the parent and * thus stop participating in model change detection and listener notification by invoking. * * @param {boolean} isolate If true, then the scope does not prototypically inherit from the * parent scope. The scope is isolated, as it can not see parent scope properties. * When creating widgets, it is useful for the widget to not accidentally read parent * state. * * @returns {Object} The newly created child scope. * */ $new: function(isolate) { var ChildScope, child; if (isolate) { child = new Scope(); child.$root = this.$root; // ensure that there is just one async queue per $rootScope and its children child.$$asyncQueue = this.$$asyncQueue; child.$$postDigestQueue = this.$$postDigestQueue; } else { // Only create a child scope class if somebody asks for one, // but cache it to allow the VM to optimize lookups. if (!this.$$childScopeClass) { this.$$childScopeClass = function() { this.$$watchers = this.$$nextSibling = this.$$childHead = this.$$childTail = null; this.$$listeners = {}; this.$$listenerCount = {}; this.$id = nextUid(); this.$$childScopeClass = null; }; this.$$childScopeClass.prototype = this; } child = new this.$$childScopeClass(); } child['this'] = child; child.$parent = this; child.$$prevSibling = this.$$childTail; if (this.$$childHead) { this.$$childTail.$$nextSibling = child; this.$$childTail = child; } else { this.$$childHead = this.$$childTail = child; } return child; }, /** * @ngdoc method * @name $rootScope.Scope#$watch * @kind function * * @description * Registers a `listener` callback to be executed whenever the `watchExpression` changes. * * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest * $digest()} and should return the value that will be watched. (Since * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the * `watchExpression` can execute multiple times per * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) * - The `listener` is called only when the value from the current `watchExpression` and the * previous call to `watchExpression` are not equal (with the exception of the initial run, * see below). Inequality is determined according to reference inequality, * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators) * via the `!==` Javascript operator, unless `objectEquality == true` * (see next point) * - When `objectEquality == true`, inequality of the `watchExpression` is determined * according to the {@link angular.equals} function. To save the value of the object for * later comparison, the {@link angular.copy} function is used. This therefore means that * watching complex objects will have adverse memory and performance implications. * - The watch `listener` may change the model, which may trigger other `listener`s to fire. * This is achieved by rerunning the watchers until no changes are detected. The rerun * iteration limit is 10 to prevent an infinite loop deadlock. * * * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a * change is detected, be prepared for multiple calls to your listener.) * * After a watcher is registered with the scope, the `listener` fn is called asynchronously * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the * watcher. In rare cases, this is undesirable because the listener is called when the result * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the * listener was called due to initialization. * * The example below contains an illustration of using a function as your $watch listener * * * # Example * ```js // let's assume that scope was dependency injected as the $rootScope var scope = $rootScope; scope.name = 'misko'; scope.counter = 0; expect(scope.counter).toEqual(0); scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; }); expect(scope.counter).toEqual(0); scope.$digest(); // the listener is always called during the first $digest loop after it was registered expect(scope.counter).toEqual(1); scope.$digest(); // but now it will not be called unless the value changes expect(scope.counter).toEqual(1); scope.name = 'adam'; scope.$digest(); expect(scope.counter).toEqual(2); // Using a listener function var food; scope.foodCounter = 0; expect(scope.foodCounter).toEqual(0); scope.$watch( // This is the listener function function() { return food; }, // This is the change handler function(newValue, oldValue) { if ( newValue !== oldValue ) { // Only increment the counter if the value changed scope.foodCounter = scope.foodCounter + 1; } } ); // No digest has been run so the counter will be zero expect(scope.foodCounter).toEqual(0); // Run the digest but since food has not changed count will still be zero scope.$digest(); expect(scope.foodCounter).toEqual(0); // Update food and run digest. Now the counter will increment food = 'cheeseburger'; scope.$digest(); expect(scope.foodCounter).toEqual(1); * ``` * * * * @param {(function()|string)} watchExpression Expression that is evaluated on each * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers * a call to the `listener`. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(scope)`: called with current `scope` as a parameter. * @param {(function()|string)=} listener Callback called whenever the return value of * the `watchExpression` changes. * * - `string`: Evaluated as {@link guide/expression expression} * - `function(newValue, oldValue, scope)`: called with current and previous values as * parameters. * * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of * comparing for reference equality. * @returns {function()} Returns a deregistration function for this listener. */ $watch: function(watchExp, listener, objectEquality) { var scope = this, get = compileToFn(watchExp, 'watch'), array = scope.$$watchers, watcher = { fn: listener, last: initWatchVal, get: get, exp: watchExp, eq: !!objectEquality }; lastDirtyWatch = null; // in the case user pass string, we need to compile it, do we really need this ? if (!isFunction(listener)) { var listenFn = compileToFn(listener || noop, 'listener'); watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; } if (typeof watchExp == 'string' && get.constant) { var originalFn = watcher.fn; watcher.fn = function(newVal, oldVal, scope) { originalFn.call(this, newVal, oldVal, scope); arrayRemove(array, watcher); }; } if (!array) { array = scope.$$watchers = []; } // we use unshift since we use a while loop in $digest for speed. // the while loop reads in reverse order. array.unshift(watcher); return function deregisterWatch() { arrayRemove(array, watcher); lastDirtyWatch = null; }; }, /** * @ngdoc method * @name $rootScope.Scope#$watchCollection * @kind function * * @description * Shallow watches the properties of an object and fires whenever any of the properties change * (for arrays, this implies watching the array items; for object maps, this implies watching * the properties). If a change is detected, the `listener` callback is fired. * * - The `obj` collection is observed via standard $watch operation and is examined on every * call to $digest() to see if any items have been added, removed, or moved. * - The `listener` is called whenever anything within the `obj` has changed. Examples include * adding, removing, and moving items belonging to an object or array. * * * # Example * ```js $scope.names = ['igor', 'matias', 'misko', 'james']; $scope.dataCount = 4; $scope.$watchCollection('names', function(newNames, oldNames) { $scope.dataCount = newNames.length; }); expect($scope.dataCount).toEqual(4); $scope.$digest(); //still at 4 ... no changes expect($scope.dataCount).toEqual(4); $scope.names.pop(); $scope.$digest(); //now there's been a change expect($scope.dataCount).toEqual(3); * ``` * * * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The * expression value should evaluate to an object or an array which is observed on each * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the * collection will trigger a call to the `listener`. * * @param {function(newCollection, oldCollection, scope)} listener a callback function called * when a change is detected. * - The `newCollection` object is the newly modified data obtained from the `obj` expression * - The `oldCollection` object is a copy of the former collection data. * Due to performance considerations, the`oldCollection` value is computed only if the * `listener` function declares two or more arguments. * - The `scope` argument refers to the current scope. * * @returns {function()} Returns a de-registration function for this listener. When the * de-registration function is executed, the internal watch operation is terminated. */ $watchCollection: function(obj, listener) { var self = this; // the current value, updated on each dirty-check run var newValue; // a shallow copy of the newValue from the last dirty-check run, // updated to match newValue during dirty-check run var oldValue; // a shallow copy of the newValue from when the last change happened var veryOldValue; // only track veryOldValue if the listener is asking for it var trackVeryOldValue = (listener.length > 1); var changeDetected = 0; var objGetter = $parse(obj); var internalArray = []; var internalObject = {}; var initRun = true; var oldLength = 0; function $watchCollectionWatch() { newValue = objGetter(self); var newLength, key, bothNaN; if (!isObject(newValue)) { // if primitive if (oldValue !== newValue) { oldValue = newValue; changeDetected++; } } else if (isArrayLike(newValue)) { if (oldValue !== internalArray) { // we are transitioning from something which was not an array into array. oldValue = internalArray; oldLength = oldValue.length = 0; changeDetected++; } newLength = newValue.length; if (oldLength !== newLength) { // if lengths do not match we need to trigger change notification changeDetected++; oldValue.length = oldLength = newLength; } // copy the items to oldValue and look for changes. for (var i = 0; i < newLength; i++) { bothNaN = (oldValue[i] !== oldValue[i]) && (newValue[i] !== newValue[i]); if (!bothNaN && (oldValue[i] !== newValue[i])) { changeDetected++; oldValue[i] = newValue[i]; } } } else { if (oldValue !== internalObject) { // we are transitioning from something which was not an object into object. oldValue = internalObject = {}; oldLength = 0; changeDetected++; } // copy the items to oldValue and look for changes. newLength = 0; for (key in newValue) { if (newValue.hasOwnProperty(key)) { newLength++; if (oldValue.hasOwnProperty(key)) { bothNaN = (oldValue[key] !== oldValue[key]) && (newValue[key] !== newValue[key]); if (!bothNaN && (oldValue[key] !== newValue[key])) { changeDetected++; oldValue[key] = newValue[key]; } } else { oldLength++; oldValue[key] = newValue[key]; changeDetected++; } } } if (oldLength > newLength) { // we used to have more keys, need to find them and destroy them. changeDetected++; for(key in oldValue) { if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) { oldLength--; delete oldValue[key]; } } } } return changeDetected; } function $watchCollectionAction() { if (initRun) { initRun = false; listener(newValue, newValue, self); } else { listener(newValue, veryOldValue, self); } // make a copy for the next time a collection is changed if (trackVeryOldValue) { if (!isObject(newValue)) { //primitive veryOldValue = newValue; } else if (isArrayLike(newValue)) { veryOldValue = new Array(newValue.length); for (var i = 0; i < newValue.length; i++) { veryOldValue[i] = newValue[i]; } } else { // if object veryOldValue = {}; for (var key in newValue) { if (hasOwnProperty.call(newValue, key)) { veryOldValue[key] = newValue[key]; } } } } } return this.$watch($watchCollectionWatch, $watchCollectionAction); }, /** * @ngdoc method * @name $rootScope.Scope#$digest * @kind function * * @description * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} * until no more listeners are firing. This means that it is possible to get into an infinite * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of * iterations exceeds 10. * * Usually, you don't call `$digest()` directly in * {@link ng.directive:ngController controllers} or in * {@link ng.$compileProvider#directive directives}. * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within * a {@link ng.$compileProvider#directive directives}), which will force a `$digest()`. * * If you want to be notified whenever `$digest()` is called, * you can register a `watchExpression` function with * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`. * * In unit tests, you may need to call `$digest()` to simulate the scope life cycle. * * # Example * ```js var scope = ...; scope.name = 'misko'; scope.counter = 0; expect(scope.counter).toEqual(0); scope.$watch('name', function(newValue, oldValue) { scope.counter = scope.counter + 1; }); expect(scope.counter).toEqual(0); scope.$digest(); // the listener is always called during the first $digest loop after it was registered expect(scope.counter).toEqual(1); scope.$digest(); // but now it will not be called unless the value changes expect(scope.counter).toEqual(1); scope.name = 'adam'; scope.$digest(); expect(scope.counter).toEqual(2); * ``` * */ $digest: function() { var watch, value, last, watchers, asyncQueue = this.$$asyncQueue, postDigestQueue = this.$$postDigestQueue, length, dirty, ttl = TTL, next, current, target = this, watchLog = [], logIdx, logMsg, asyncTask; beginPhase('$digest'); // Check for changes to browser url that happened in sync before the call to $digest $browser.$$checkUrlChange(); lastDirtyWatch = null; do { // "while dirty" loop dirty = false; current = target; while(asyncQueue.length) { try { asyncTask = asyncQueue.shift(); asyncTask.scope.$eval(asyncTask.expression); } catch (e) { clearPhase(); $exceptionHandler(e); } lastDirtyWatch = null; } traverseScopesLoop: do { // "traverse the scopes" loop if ((watchers = current.$$watchers)) { // process our watches length = watchers.length; while (length--) { try { watch = watchers[length]; // Most common watches are on primitives, in which case we can short // circuit it with === operator, only when === fails do we use .equals if (watch) { if ((value = watch.get(current)) !== (last = watch.last) && !(watch.eq ? equals(value, last) : (typeof value === 'number' && typeof last === 'number' && isNaN(value) && isNaN(last)))) { dirty = true; lastDirtyWatch = watch; watch.last = watch.eq ? copy(value, null) : value; watch.fn(value, ((last === initWatchVal) ? value : last), current); if (ttl < 5) { logIdx = 4 - ttl; if (!watchLog[logIdx]) watchLog[logIdx] = []; logMsg = (isFunction(watch.exp)) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp; logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); watchLog[logIdx].push(logMsg); } } else if (watch === lastDirtyWatch) { // If the most recently dirty watcher is now clean, short circuit since the remaining watchers // have already been tested. dirty = false; break traverseScopesLoop; } } } catch (e) { clearPhase(); $exceptionHandler(e); } } } // Insanity Warning: scope depth-first traversal // yes, this code is a bit crazy, but it works and we have tests to prove it! // this piece should be kept in sync with the traversal in $broadcast if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { while(current !== target && !(next = current.$$nextSibling)) { current = current.$parent; } } } while ((current = next)); // `break traverseScopesLoop;` takes us to here if((dirty || asyncQueue.length) && !(ttl--)) { clearPhase(); throw $rootScopeMinErr('infdig', '{0} $digest() iterations reached. Aborting!\n' + 'Watchers fired in the last 5 iterations: {1}', TTL, toJson(watchLog)); } } while (dirty || asyncQueue.length); clearPhase(); while(postDigestQueue.length) { try { postDigestQueue.shift()(); } catch (e) { $exceptionHandler(e); } } }, /** * @ngdoc event * @name $rootScope.Scope#$destroy * @eventType broadcast on scope being destroyed * * @description * Broadcasted when a scope and its children are being destroyed. * * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to * clean up DOM bindings before an element is removed from the DOM. */ /** * @ngdoc method * @name $rootScope.Scope#$destroy * @kind function * * @description * Removes the current scope (and all of its children) from the parent scope. Removal implies * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer * propagate to the current scope and its children. Removal also implies that the current * scope is eligible for garbage collection. * * The `$destroy()` is usually used by directives such as * {@link ng.directive:ngRepeat ngRepeat} for managing the * unrolling of the loop. * * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope. * Application code can register a `$destroy` event handler that will give it a chance to * perform any necessary cleanup. * * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to * clean up DOM bindings before an element is removed from the DOM. */ $destroy: function() { // we can't destroy the root scope or a scope that has been already destroyed if (this.$$destroyed) return; var parent = this.$parent; this.$broadcast('$destroy'); this.$$destroyed = true; if (this === $rootScope) return; forEach(this.$$listenerCount, bind(null, decrementListenerCount, this)); // sever all the references to parent scopes (after this cleanup, the current scope should // not be retained by any of our references and should be eligible for garbage collection) if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; // All of the code below is bogus code that works around V8's memory leak via optimized code // and inline caches. // // see: // - https://code.google.com/p/v8/issues/detail?id=2073#c26 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = this.$$childTail = this.$root = null; // don't reset these to null in case some async task tries to register a listener/watch/task this.$$listeners = {}; this.$$watchers = this.$$asyncQueue = this.$$postDigestQueue = []; // prevent NPEs since these methods have references to properties we nulled out this.$destroy = this.$digest = this.$apply = noop; this.$on = this.$watch = function() { return noop; }; }, /** * @ngdoc method * @name $rootScope.Scope#$eval * @kind function * * @description * Executes the `expression` on the current scope and returns the result. Any exceptions in * the expression are propagated (uncaught). This is useful when evaluating Angular * expressions. * * # Example * ```js var scope = ng.$rootScope.Scope(); scope.a = 1; scope.b = 2; expect(scope.$eval('a+b')).toEqual(3); expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); * ``` * * @param {(string|function())=} expression An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * * @param {(object)=} locals Local variables object, useful for overriding values in scope. * @returns {*} The result of evaluating the expression. */ $eval: function(expr, locals) { return $parse(expr)(this, locals); }, /** * @ngdoc method * @name $rootScope.Scope#$evalAsync * @kind function * * @description * Executes the expression on the current scope at a later point in time. * * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only * that: * * - it will execute after the function that scheduled the evaluation (preferably before DOM * rendering). * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after * `expression` execution. * * Any exceptions from the execution of the expression are forwarded to the * {@link ng.$exceptionHandler $exceptionHandler} service. * * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle * will be scheduled. However, it is encouraged to always call code that changes the model * from within an `$apply` call. That includes code evaluated via `$evalAsync`. * * @param {(string|function())=} expression An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with the current `scope` parameter. * */ $evalAsync: function(expr) { // if we are outside of an $digest loop and this is the first time we are scheduling async // task also schedule async auto-flush if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { $browser.defer(function() { if ($rootScope.$$asyncQueue.length) { $rootScope.$digest(); } }); } this.$$asyncQueue.push({scope: this, expression: expr}); }, $$postDigest : function(fn) { this.$$postDigestQueue.push(fn); }, /** * @ngdoc method * @name $rootScope.Scope#$apply * @kind function * * @description * `$apply()` is used to execute an expression in angular from outside of the angular * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). * Because we are calling into the angular framework we need to perform proper scope life * cycle of {@link ng.$exceptionHandler exception handling}, * {@link ng.$rootScope.Scope#$digest executing watches}. * * ## Life cycle * * # Pseudo-Code of `$apply()` * ```js function $apply(expr) { try { return $eval(expr); } catch (e) { $exceptionHandler(e); } finally { $root.$digest(); } } * ``` * * * Scope's `$apply()` method transitions through the following stages: * * 1. The {@link guide/expression expression} is executed using the * {@link ng.$rootScope.Scope#$eval $eval()} method. * 2. Any exceptions from the execution of the expression are forwarded to the * {@link ng.$exceptionHandler $exceptionHandler} service. * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. * * * @param {(string|function())=} exp An angular expression to be executed. * * - `string`: execute using the rules as defined in {@link guide/expression expression}. * - `function(scope)`: execute the function with current `scope` parameter. * * @returns {*} The result of evaluating the expression. */ $apply: function(expr) { try { beginPhase('$apply'); return this.$eval(expr); } catch (e) { $exceptionHandler(e); } finally { clearPhase(); try { $rootScope.$digest(); } catch (e) { $exceptionHandler(e); throw e; } } }, /** * @ngdoc method * @name $rootScope.Scope#$on * @kind function * * @description * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for * discussion of event life cycle. * * The event listener function format is: `function(event, args...)`. The `event` object * passed into the listener has the following attributes: * * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or * `$broadcast`-ed. * - `currentScope` - `{Scope}`: the current scope which is handling the event. * - `name` - `{string}`: name of the event. * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel * further event propagation (available only for events that were `$emit`-ed). * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag * to true. * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. * * @param {string} name Event name to listen on. * @param {function(event, ...args)} listener Function to call when the event is emitted. * @returns {function()} Returns a deregistration function for this listener. */ $on: function(name, listener) { var namedListeners = this.$$listeners[name]; if (!namedListeners) { this.$$listeners[name] = namedListeners = []; } namedListeners.push(listener); var current = this; do { if (!current.$$listenerCount[name]) { current.$$listenerCount[name] = 0; } current.$$listenerCount[name]++; } while ((current = current.$parent)); var self = this; return function() { var indexOfListener = indexOf(namedListeners, listener); if (indexOfListener !== -1) { namedListeners[indexOfListener] = null; decrementListenerCount(self, 1, name); } }; }, /** * @ngdoc method * @name $rootScope.Scope#$emit * @kind function * * @description * Dispatches an event `name` upwards through the scope hierarchy notifying the * registered {@link ng.$rootScope.Scope#$on} listeners. * * The event life cycle starts at the scope on which `$emit` was called. All * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get * notified. Afterwards, the event traverses upwards toward the root scope and calls all * registered listeners along the way. The event will stop propagating if one of the listeners * cancels it. * * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed * onto the {@link ng.$exceptionHandler $exceptionHandler} service. * * @param {string} name Event name to emit. * @param {...*} args Optional one or more arguments which will be passed onto the event listeners. * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}). */ $emit: function(name, args) { var empty = [], namedListeners, scope = this, stopPropagation = false, event = { name: name, targetScope: scope, stopPropagation: function() {stopPropagation = true;}, preventDefault: function() { event.defaultPrevented = true; }, defaultPrevented: false }, listenerArgs = concat([event], arguments, 1), i, length; do { namedListeners = scope.$$listeners[name] || empty; event.currentScope = scope; for (i=0, length=namedListeners.length; i= 8 ) { normalizedVal = urlResolve(uri).href; if (normalizedVal !== '' && !normalizedVal.match(regex)) { return 'unsafe:'+normalizedVal; } } return uri; }; }; } var $sceMinErr = minErr('$sce'); var SCE_CONTEXTS = { HTML: 'html', CSS: 'css', URL: 'url', // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a // url. (e.g. ng-include, script src, templateUrl) RESOURCE_URL: 'resourceUrl', JS: 'js' }; // Helper functions follow. // Copied from: // http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962 // Prereq: s is a string. function escapeForRegexp(s) { return s.replace(/([-()\[\]{}+?*.$\^|,:# -1) { throw $sceMinErr('iwcard', 'Illegal sequence *** in string matcher. String: {0}', matcher); } matcher = escapeForRegexp(matcher). replace('\\*\\*', '.*'). replace('\\*', '[^:/.?&;]*'); return new RegExp('^' + matcher + '$'); } else if (isRegExp(matcher)) { // The only other type of matcher allowed is a Regexp. // Match entire URL / disallow partial matches. // Flags are reset (i.e. no global, ignoreCase or multiline) return new RegExp('^' + matcher.source + '$'); } else { throw $sceMinErr('imatcher', 'Matchers may only be "self", string patterns or RegExp objects'); } } function adjustMatchers(matchers) { var adjustedMatchers = []; if (isDefined(matchers)) { forEach(matchers, function(matcher) { adjustedMatchers.push(adjustMatcher(matcher)); }); } return adjustedMatchers; } /** * @ngdoc service * @name $sceDelegate * @kind function * * @description * * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict * Contextual Escaping (SCE)} services to AngularJS. * * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things * work because `$sce` delegates to `$sceDelegate` for these operations. * * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service. * * The default instance of `$sceDelegate` should work out of the box with little pain. While you * can override it completely to change the behavior of `$sce`, the common case would * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist * $sceDelegateProvider.resourceUrlWhitelist} and {@link * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} */ /** * @ngdoc provider * @name $sceDelegateProvider * @description * * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure * that the URLs used for sourcing Angular templates are safe. Refer {@link * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} * * For the general details about this service in Angular, read the main page for {@link ng.$sce * Strict Contextual Escaping (SCE)}. * * **Example**: Consider the following case. * * - your app is hosted at url `http://myapp.example.com/` * - but some of your templates are hosted on other domains you control such as * `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc. * - and you have an open redirect at `http://myapp.example.com/clickThru?...`. * * Here is what a secure configuration for this scenario might look like: * * ``` * angular.module('myApp', []).config(function($sceDelegateProvider) { * $sceDelegateProvider.resourceUrlWhitelist([ * // Allow same origin resource loads. * 'self', * // Allow loading from our assets domain. Notice the difference between * and **. * 'http://srv*.assets.example.com/**' * ]); * * // The blacklist overrides the whitelist so the open redirect here is blocked. * $sceDelegateProvider.resourceUrlBlacklist([ * 'http://myapp.example.com/clickThru**' * ]); * }); * ``` */ function $SceDelegateProvider() { this.SCE_CONTEXTS = SCE_CONTEXTS; // Resource URLs can also be trusted by policy. var resourceUrlWhitelist = ['self'], resourceUrlBlacklist = []; /** * @ngdoc method * @name $sceDelegateProvider#resourceUrlWhitelist * @kind function * * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value * provided. This must be an array or null. A snapshot of this array is used so further * changes to the array are ignored. * * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items * allowed in this array. * * Note: **an empty whitelist array will block all URLs**! * * @return {Array} the currently set whitelist array. * * The **default value** when no whitelist has been explicitly set is `['self']` allowing only * same origin resource requests. * * @description * Sets/Gets the whitelist of trusted resource URLs. */ this.resourceUrlWhitelist = function (value) { if (arguments.length) { resourceUrlWhitelist = adjustMatchers(value); } return resourceUrlWhitelist; }; /** * @ngdoc method * @name $sceDelegateProvider#resourceUrlBlacklist * @kind function * * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value * provided. This must be an array or null. A snapshot of this array is used so further * changes to the array are ignored. * * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items * allowed in this array. * * The typical usage for the blacklist is to **block * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as * these would otherwise be trusted but actually return content from the redirected domain. * * Finally, **the blacklist overrides the whitelist** and has the final say. * * @return {Array} the currently set blacklist array. * * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there * is no blacklist.) * * @description * Sets/Gets the blacklist of trusted resource URLs. */ this.resourceUrlBlacklist = function (value) { if (arguments.length) { resourceUrlBlacklist = adjustMatchers(value); } return resourceUrlBlacklist; }; this.$get = ['$injector', function($injector) { var htmlSanitizer = function htmlSanitizer(html) { throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); }; if ($injector.has('$sanitize')) { htmlSanitizer = $injector.get('$sanitize'); } function matchUrl(matcher, parsedUrl) { if (matcher === 'self') { return urlIsSameOrigin(parsedUrl); } else { // definitely a regex. See adjustMatchers() return !!matcher.exec(parsedUrl.href); } } function isResourceUrlAllowedByPolicy(url) { var parsedUrl = urlResolve(url.toString()); var i, n, allowed = false; // Ensure that at least one item from the whitelist allows this url. for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) { if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) { allowed = true; break; } } if (allowed) { // Ensure that no item from the blacklist blocked this url. for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) { if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) { allowed = false; break; } } } return allowed; } function generateHolderType(Base) { var holderType = function TrustedValueHolderType(trustedValue) { this.$$unwrapTrustedValue = function() { return trustedValue; }; }; if (Base) { holderType.prototype = new Base(); } holderType.prototype.valueOf = function sceValueOf() { return this.$$unwrapTrustedValue(); }; holderType.prototype.toString = function sceToString() { return this.$$unwrapTrustedValue().toString(); }; return holderType; } var trustedValueHolderBase = generateHolderType(), byType = {}; byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase); byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase); byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase); byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase); byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]); /** * @ngdoc method * @name $sceDelegate#trustAs * * @description * Returns an object that is trusted by angular for use in specified strict * contextual escaping contexts (such as ng-bind-html, ng-include, any src * attribute interpolation, any dom event binding attribute interpolation * such as for onclick, etc.) that uses the provided value. * See {@link ng.$sce $sce} for enabling strict contextual escaping. * * @param {string} type The kind of context in which this value is safe for use. e.g. url, * resourceUrl, html, js and css. * @param {*} value The value that that should be considered trusted/safe. * @returns {*} A value that can be used to stand in for the provided `value` in places * where Angular expects a $sce.trustAs() return value. */ function trustAs(type, trustedValue) { var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null); if (!Constructor) { throw $sceMinErr('icontext', 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}', type, trustedValue); } if (trustedValue === null || trustedValue === undefined || trustedValue === '') { return trustedValue; } // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting // mutable objects, we ensure here that the value passed in is actually a string. if (typeof trustedValue !== 'string') { throw $sceMinErr('itype', 'Attempted to trust a non-string value in a content requiring a string: Context: {0}', type); } return new Constructor(trustedValue); } /** * @ngdoc method * @name $sceDelegate#valueOf * * @description * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. * * If the passed parameter is not a value that had been returned by {@link * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is. * * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} * call or anything else. * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns * `value` unchanged. */ function valueOf(maybeTrusted) { if (maybeTrusted instanceof trustedValueHolderBase) { return maybeTrusted.$$unwrapTrustedValue(); } else { return maybeTrusted; } } /** * @ngdoc method * @name $sceDelegate#getTrusted * * @description * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and * returns the originally supplied value if the queried context type is a supertype of the * created type. If this condition isn't satisfied, throws an exception. * * @param {string} type The kind of context in which this value is to be used. * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs * `$sceDelegate.trustAs`} call. * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception. */ function getTrusted(type, maybeTrusted) { if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') { return maybeTrusted; } var constructor = (byType.hasOwnProperty(type) ? byType[type] : null); if (constructor && maybeTrusted instanceof constructor) { return maybeTrusted.$$unwrapTrustedValue(); } // If we get here, then we may only take one of two actions. // 1. sanitize the value for the requested type, or // 2. throw an exception. if (type === SCE_CONTEXTS.RESOURCE_URL) { if (isResourceUrlAllowedByPolicy(maybeTrusted)) { return maybeTrusted; } else { throw $sceMinErr('insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}', maybeTrusted.toString()); } } else if (type === SCE_CONTEXTS.HTML) { return htmlSanitizer(maybeTrusted); } throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.'); } return { trustAs: trustAs, getTrusted: getTrusted, valueOf: valueOf }; }]; } /** * @ngdoc provider * @name $sceProvider * @description * * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service. * - enable/disable Strict Contextual Escaping (SCE) in a module * - override the default implementation with a custom delegate * * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}. */ /* jshint maxlen: false*/ /** * @ngdoc service * @name $sce * @kind function * * @description * * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS. * * # Strict Contextual Escaping * * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain * contexts to result in a value that is marked as safe to use for that context. One example of * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer * to these contexts as privileged or SCE contexts. * * As of version 1.2, Angular ships with SCE enabled by default. * * Note: When enabled (the default), IE8 in quirks mode is not supported. In this mode, IE8 allows * one to execute arbitrary javascript by the use of the expression() syntax. Refer * to learn more about them. * You can ensure your document is in standards mode and not quirks mode by adding `` * to the top of your HTML document. * * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for * security vulnerabilities such as XSS, clickjacking, etc. a lot easier. * * Here's an example of a binding in a privileged context: * * ``` * *
    * ``` * * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE * disabled, this application allows the user to render arbitrary HTML into the DIV. * In a more realistic example, one may be rendering user comments, blog articles, etc. via * bindings. (HTML is just one example of a context where rendering user controlled input creates * security vulnerabilities.) * * For the case of HTML, you might use a library, either on the client side, or on the server side, * to sanitize unsafe HTML before binding to the value and rendering it in the document. * * How would you ensure that every place that used these types of bindings was bound to a value that * was sanitized by your library (or returned as safe for rendering by your server?) How can you * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some * properties/fields and forgot to update the binding to the sanitized value? * * To be secure by default, you want to ensure that any such bindings are disallowed unless you can * determine that something explicitly says it's safe to use a value for binding in that * context. You can then audit your code (a simple grep would do) to ensure that this is only done * for those values that you can easily tell are safe - because they were received from your server, * sanitized by your library, etc. You can organize your codebase to help with this - perhaps * allowing only the files in a specific directory to do this. Ensuring that the internal API * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task. * * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs} * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to * obtain values that will be accepted by SCE / privileged contexts. * * * ## How does it work? * * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link * ng.$sce#parse $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals. * * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly * simplified): * * ``` * var ngBindHtmlDirective = ['$sce', function($sce) { * return function(scope, element, attr) { * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) { * element.html(value || ''); * }); * }; * }]; * ``` * * ## Impact on loading templates * * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as * `templateUrl`'s specified by {@link guide/directive directives}. * * By default, Angular only loads templates from the same domain and protocol as the application * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value. * * *Please note*: * The browser's * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) * policy apply in addition to this and may further restrict whether the template is successfully * loaded. This means that without the right CORS policy, loading templates from a different domain * won't work on all browsers. Also, loading templates from `file://` URL does not work on some * browsers. * * ## This feels like too much overhead for the developer? * * It's important to remember that SCE only applies to interpolation expressions. * * If your expressions are constant literals, they're automatically trusted and you don't need to * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g. * `
    `) just works. * * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here. * * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load * templates in `ng-include` from your application's domain without having to even know about SCE. * It blocks loading templates from other domains or loading templates over http from an https * served document. You can change these by setting your own custom {@link * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs. * * This significantly reduces the overhead. It is far easier to pay the small overhead and have an * application that's secure and can be audited to verify that with much more ease than bolting * security onto an application later. * * * ## What trusted context types are supported? * * | Context | Notes | * |---------------------|----------------| * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. | * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. | * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`
    Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. | * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. | * * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist}
    * * Each element in these arrays must be one of the following: * * - **'self'** * - The special **string**, `'self'`, can be used to match against all URLs of the **same * domain** as the application document using the **same protocol**. * - **String** (except the special value `'self'`) * - The string is matched against the full *normalized / absolute URL* of the resource * being tested (substring matches are not good enough.) * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters * match themselves. * - `*`: matches zero or more occurrences of any character other than one of the following 6 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use * in a whitelist. * - `**`: matches zero or more occurrences of *any* character. As such, it's not * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g. * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might * not have been the intention.) Its usage at the very end of the path is ok. (e.g. * http://foo.example.com/templates/**). * - **RegExp** (*see caveat below*) * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to * accidentally introduce a bug when one updates a complex expression (imho, all regexes should * have good test coverage.). For instance, the use of `.` in the regex is correct only in a * small number of cases. A `.` character in the regex used when matching the scheme or a * subdomain could be matched against a `:` or literal `.` that was likely not intended. It * is highly recommended to use the string patterns and only fall back to regular expressions * if they as a last resort. * - The regular expression must be an instance of RegExp (i.e. not a string.) It is * matched against the **entire** *normalized / absolute URL* of the resource being tested * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags * present on the RegExp (such as multiline, global, ignoreCase) are ignored. * - If you are generating your JavaScript from some other templating engine (not * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)), * remember to escape your regular expression (and be aware that you might need more than * one level of escaping depending on your templating engine and the way you interpolated * the value.) Do make use of your platform's escaping mechanism as it might be good * enough before coding your own. e.g. Ruby has * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape) * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape). * Javascript lacks a similar built in function for escaping. Take a look at Google * Closure library's [goog.string.regExpEscape(s)]( * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962). * * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example. * * ## Show me an example using SCE. * * * *
    *

    * User comments
    * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when * $sanitize is available. If $sanitize isn't available, this results in an error instead of an * exploit. *
    *
    * {{userComment.name}}: * *
    *
    *
    *
    *
    * * * var mySceApp = angular.module('mySceApp', ['ngSanitize']); * * mySceApp.controller("myAppController", function myAppController($http, $templateCache, $sce) { * var self = this; * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) { * self.userComments = userComments; * }); * self.explicitlyTrustedHtml = $sce.trustAsHtml( * 'Hover over this text.'); * }); * * * * [ * { "name": "Alice", * "htmlComment": * "Is anyone reading this?" * }, * { "name": "Bob", * "htmlComment": "Yes! Am I the only other one?" * } * ] * * * * describe('SCE doc demo', function() { * it('should sanitize untrusted values', function() { * expect(element.all(by.css('.htmlComment')).first().getInnerHtml()) * .toBe('Is anyone reading this?'); * }); * * it('should NOT sanitize explicitly trusted values', function() { * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe( * 'Hover over this text.'); * }); * }); * *
    * * * * ## Can I disable SCE completely? * * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits * for little coding overhead. It will be much harder to take an SCE disabled application and * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE * for cases where you have a lot of existing code that was written before SCE was introduced and * you're migrating them a module at a time. * * That said, here's how you can completely disable SCE: * * ``` * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) { * // Completely disable SCE. For demonstration purposes only! * // Do not use in new projects. * $sceProvider.enabled(false); * }); * ``` * */ /* jshint maxlen: 100 */ function $SceProvider() { var enabled = true; /** * @ngdoc method * @name $sceProvider#enabled * @kind function * * @param {boolean=} value If provided, then enables/disables SCE. * @return {boolean} true if SCE is enabled, false otherwise. * * @description * Enables/disables SCE and returns the current value. */ this.enabled = function (value) { if (arguments.length) { enabled = !!value; } return enabled; }; /* Design notes on the default implementation for SCE. * * The API contract for the SCE delegate * ------------------------------------- * The SCE delegate object must provide the following 3 methods: * * - trustAs(contextEnum, value) * This method is used to tell the SCE service that the provided value is OK to use in the * contexts specified by contextEnum. It must return an object that will be accepted by * getTrusted() for a compatible contextEnum and return this value. * * - valueOf(value) * For values that were not produced by trustAs(), return them as is. For values that were * produced by trustAs(), return the corresponding input value to trustAs. Basically, if * trustAs is wrapping the given values into some type, this operation unwraps it when given * such a value. * * - getTrusted(contextEnum, value) * This function should return the a value that is safe to use in the context specified by * contextEnum or throw and exception otherwise. * * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be * opaque or wrapped in some holder object. That happens to be an implementation detail. For * instance, an implementation could maintain a registry of all trusted objects by context. In * such a case, trustAs() would return the same object that was passed in. getTrusted() would * return the same object passed in if it was found in the registry under a compatible context or * throw an exception otherwise. An implementation might only wrap values some of the time based * on some criteria. getTrusted() might return a value and not throw an exception for special * constants or objects even if not wrapped. All such implementations fulfill this contract. * * * A note on the inheritance model for SCE contexts * ------------------------------------------------ * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This * is purely an implementation details. * * The contract is simply this: * * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value) * will also succeed. * * Inheritance happens to capture this in a natural way. In some future, we * may not use inheritance anymore. That is OK because no code outside of * sce.js and sceSpecs.js would need to be aware of this detail. */ this.$get = ['$parse', '$sniffer', '$sceDelegate', function( $parse, $sniffer, $sceDelegate) { // Prereq: Ensure that we're not running in IE8 quirks mode. In that mode, IE allows // the "expression(javascript expression)" syntax which is insecure. if (enabled && $sniffer.msie && $sniffer.msieDocumentMode < 8) { throw $sceMinErr('iequirks', 'Strict Contextual Escaping does not support Internet Explorer version < 9 in quirks ' + 'mode. You can fix this by adding the text to the top of your HTML ' + 'document. See http://docs.angularjs.org/api/ng.$sce for more information.'); } var sce = shallowCopy(SCE_CONTEXTS); /** * @ngdoc method * @name $sce#isEnabled * @kind function * * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you * have to do it at module config time on {@link ng.$sceProvider $sceProvider}. * * @description * Returns a boolean indicating if SCE is enabled. */ sce.isEnabled = function () { return enabled; }; sce.trustAs = $sceDelegate.trustAs; sce.getTrusted = $sceDelegate.getTrusted; sce.valueOf = $sceDelegate.valueOf; if (!enabled) { sce.trustAs = sce.getTrusted = function(type, value) { return value; }; sce.valueOf = identity; } /** * @ngdoc method * @name $sce#parseAs * * @description * Converts Angular {@link guide/expression expression} into a function. This is like {@link * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*, * *result*)} * * @param {string} type The kind of SCE context in which this result will be used. * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ sce.parseAs = function sceParseAs(type, expr) { var parsed = $parse(expr); if (parsed.literal && parsed.constant) { return parsed; } else { return function sceParseAsTrusted(self, locals) { return sce.getTrusted(type, parsed(self, locals)); }; } }; /** * @ngdoc method * @name $sce#trustAs * * @description * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such, * returns an object that is trusted by angular for use in specified strict contextual * escaping contexts (such as ng-bind-html, ng-include, any src attribute * interpolation, any dom event binding attribute interpolation such as for onclick, etc.) * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual * escaping. * * @param {string} type The kind of context in which this value is safe for use. e.g. url, * resource_url, html, js and css. * @param {*} value The value that that should be considered trusted/safe. * @returns {*} A value that can be used to stand in for the provided `value` in places * where Angular expects a $sce.trustAs() return value. */ /** * @ngdoc method * @name $sce#trustAsHtml * * @description * Shorthand method. `$sce.trustAsHtml(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`} * * @param {*} value The value to trustAs. * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the * return value of {@link ng.$sce#trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name $sce#trustAsUrl * * @description * Shorthand method. `$sce.trustAsUrl(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`} * * @param {*} value The value to trustAs. * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the * return value of {@link ng.$sce#trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name $sce#trustAsResourceUrl * * @description * Shorthand method. `$sce.trustAsResourceUrl(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`} * * @param {*} value The value to trustAs. * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the return * value of {@link ng.$sce#trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name $sce#trustAsJs * * @description * Shorthand method. `$sce.trustAsJs(value)` → * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`} * * @param {*} value The value to trustAs. * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives * only accept expressions that are either literal constants or are the * return value of {@link ng.$sce#trustAs $sce.trustAs}.) */ /** * @ngdoc method * @name $sce#getTrusted * * @description * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such, * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the * originally supplied value if the queried context type is a supertype of the created type. * If this condition isn't satisfied, throws an exception. * * @param {string} type The kind of context in which this value is to be used. * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`} * call. * @returns {*} The value the was originally provided to * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context. * Otherwise, throws an exception. */ /** * @ngdoc method * @name $sce#getTrustedHtml * * @description * Shorthand method. `$sce.getTrustedHtml(value)` → * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)` */ /** * @ngdoc method * @name $sce#getTrustedCss * * @description * Shorthand method. `$sce.getTrustedCss(value)` → * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)` */ /** * @ngdoc method * @name $sce#getTrustedUrl * * @description * Shorthand method. `$sce.getTrustedUrl(value)` → * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)` */ /** * @ngdoc method * @name $sce#getTrustedResourceUrl * * @description * Shorthand method. `$sce.getTrustedResourceUrl(value)` → * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`} * * @param {*} value The value to pass to `$sceDelegate.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)` */ /** * @ngdoc method * @name $sce#getTrustedJs * * @description * Shorthand method. `$sce.getTrustedJs(value)` → * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`} * * @param {*} value The value to pass to `$sce.getTrusted`. * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)` */ /** * @ngdoc method * @name $sce#parseAsHtml * * @description * Shorthand method. `$sce.parseAsHtml(expression string)` → * {@link ng.$sce#parse `$sce.parseAs($sce.HTML, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ /** * @ngdoc method * @name $sce#parseAsCss * * @description * Shorthand method. `$sce.parseAsCss(value)` → * {@link ng.$sce#parse `$sce.parseAs($sce.CSS, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ /** * @ngdoc method * @name $sce#parseAsUrl * * @description * Shorthand method. `$sce.parseAsUrl(value)` → * {@link ng.$sce#parse `$sce.parseAs($sce.URL, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ /** * @ngdoc method * @name $sce#parseAsResourceUrl * * @description * Shorthand method. `$sce.parseAsResourceUrl(value)` → * {@link ng.$sce#parse `$sce.parseAs($sce.RESOURCE_URL, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ /** * @ngdoc method * @name $sce#parseAsJs * * @description * Shorthand method. `$sce.parseAsJs(value)` → * {@link ng.$sce#parse `$sce.parseAs($sce.JS, value)`} * * @param {string} expression String expression to compile. * @returns {function(context, locals)} a function which represents the compiled expression: * * * `context` – `{object}` – an object against which any expressions embedded in the strings * are evaluated against (typically a scope object). * * `locals` – `{object=}` – local variables context object, useful for overriding values in * `context`. */ // Shorthand delegations. var parse = sce.parseAs, getTrusted = sce.getTrusted, trustAs = sce.trustAs; forEach(SCE_CONTEXTS, function (enumValue, name) { var lName = lowercase(name); sce[camelCase("parse_as_" + lName)] = function (expr) { return parse(enumValue, expr); }; sce[camelCase("get_trusted_" + lName)] = function (value) { return getTrusted(enumValue, value); }; sce[camelCase("trust_as_" + lName)] = function (value) { return trustAs(enumValue, value); }; }); return sce; }]; } /** * !!! This is an undocumented "private" service !!! * * @name $sniffer * @requires $window * @requires $document * * @property {boolean} history Does the browser support html5 history api ? * @property {boolean} hashchange Does the browser support hashchange event ? * @property {boolean} transitions Does the browser support CSS transition events ? * @property {boolean} animations Does the browser support CSS animation events ? * * @description * This is very simple implementation of testing browser's features. */ function $SnifferProvider() { this.$get = ['$window', '$document', function($window, $document) { var eventSupport = {}, android = int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]), boxee = /Boxee/i.test(($window.navigator || {}).userAgent), document = $document[0] || {}, documentMode = document.documentMode, vendorPrefix, vendorRegex = /^(Moz|webkit|O|ms)(?=[A-Z])/, bodyStyle = document.body && document.body.style, transitions = false, animations = false, match; if (bodyStyle) { for(var prop in bodyStyle) { if(match = vendorRegex.exec(prop)) { vendorPrefix = match[0]; vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1); break; } } if(!vendorPrefix) { vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit'; } transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle)); animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle)); if (android && (!transitions||!animations)) { transitions = isString(document.body.style.webkitTransition); animations = isString(document.body.style.webkitAnimation); } } return { // Android has history.pushState, but it does not update location correctly // so let's not use the history API at all. // http://code.google.com/p/android/issues/detail?id=17471 // https://github.com/angular/angular.js/issues/904 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has // so let's not use the history API also // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined // jshint -W018 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee), // jshint +W018 hashchange: 'onhashchange' in $window && // IE8 compatible mode lies (!documentMode || documentMode > 7), hasEvent: function(event) { // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have // it. In particular the event is not fired when backspace or delete key are pressed or // when cut operation is performed. if (event == 'input' && msie == 9) return false; if (isUndefined(eventSupport[event])) { var divElm = document.createElement('div'); eventSupport[event] = 'on' + event in divElm; } return eventSupport[event]; }, csp: csp(), vendorPrefix: vendorPrefix, transitions : transitions, animations : animations, android: android, msie : msie, msieDocumentMode: documentMode }; }]; } function $TimeoutProvider() { this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', function($rootScope, $browser, $q, $exceptionHandler) { var deferreds = {}; /** * @ngdoc service * @name $timeout * * @description * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch * block and delegates any exceptions to * {@link ng.$exceptionHandler $exceptionHandler} service. * * The return value of registering a timeout function is a promise, which will be resolved when * the timeout is reached and the timeout function is executed. * * To cancel a timeout request, call `$timeout.cancel(promise)`. * * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to * synchronously flush the queue of deferred functions. * * @param {function()} fn A function, whose execution should be delayed. * @param {number=} [delay=0] Delay in milliseconds. * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this * promise will be resolved with is the return value of the `fn` function. * */ function timeout(fn, delay, invokeApply) { var deferred = $q.defer(), promise = deferred.promise, skipApply = (isDefined(invokeApply) && !invokeApply), timeoutId; timeoutId = $browser.defer(function() { try { deferred.resolve(fn()); } catch(e) { deferred.reject(e); $exceptionHandler(e); } finally { delete deferreds[promise.$$timeoutId]; } if (!skipApply) $rootScope.$apply(); }, delay); promise.$$timeoutId = timeoutId; deferreds[timeoutId] = deferred; return promise; } /** * @ngdoc method * @name $timeout#cancel * * @description * Cancels a task associated with the `promise`. As a result of this, the promise will be * resolved with a rejection. * * @param {Promise=} promise Promise returned by the `$timeout` function. * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully * canceled. */ timeout.cancel = function(promise) { if (promise && promise.$$timeoutId in deferreds) { deferreds[promise.$$timeoutId].reject('canceled'); delete deferreds[promise.$$timeoutId]; return $browser.defer.cancel(promise.$$timeoutId); } return false; }; return timeout; }]; } // NOTE: The usage of window and document instead of $window and $document here is // deliberate. This service depends on the specific behavior of anchor nodes created by the // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and // cause us to break tests. In addition, when the browser resolves a URL for XHR, it // doesn't know about mocked locations and resolves URLs to the real document - which is // exactly the behavior needed here. There is little value is mocking these out for this // service. var urlParsingNode = document.createElement("a"); var originUrl = urlResolve(window.location.href, true); /** * * Implementation Notes for non-IE browsers * ---------------------------------------- * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM, * results both in the normalizing and parsing of the URL. Normalizing means that a relative * URL will be resolved into an absolute URL in the context of the application document. * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related * properties are all populated to reflect the normalized URL. This approach has wide * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html * * Implementation Notes for IE * --------------------------- * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other * browsers. However, the parsed components will not be set if the URL assigned did not specify * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We * work around that by performing the parsing in a 2nd step by taking a previously normalized * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the * properties such as protocol, hostname, port, etc. * * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one * uses the inner HTML approach to assign the URL as part of an HTML snippet - * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL. * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception. * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that * method and IE < 8 is unsupported. * * References: * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html * http://url.spec.whatwg.org/#urlutils * https://github.com/angular/angular.js/pull/2902 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/ * * @kind function * @param {string} url The URL to be parsed. * @description Normalizes and parses a URL. * @returns {object} Returns the normalized URL as a dictionary. * * | member name | Description | * |---------------|----------------| * | href | A normalized version of the provided URL if it was not an absolute URL | * | protocol | The protocol including the trailing colon | * | host | The host and port (if the port is non-default) of the normalizedUrl | * | search | The search params, minus the question mark | * | hash | The hash string, minus the hash symbol * | hostname | The hostname * | port | The port, without ":" * | pathname | The pathname, beginning with "/" * */ function urlResolve(url, base) { var href = url; if (msie) { // Normalize before parse. Refer Implementation Notes on why this is // done in two steps on IE. urlParsingNode.setAttribute("href", href); href = urlParsingNode.href; } urlParsingNode.setAttribute('href', href); // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils return { href: urlParsingNode.href, protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '', host: urlParsingNode.host, search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '', hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '', hostname: urlParsingNode.hostname, port: urlParsingNode.port, pathname: (urlParsingNode.pathname.charAt(0) === '/') ? urlParsingNode.pathname : '/' + urlParsingNode.pathname }; } /** * Parse a request URL and determine whether this is a same-origin request as the application document. * * @param {string|object} requestUrl The url of the request as a string that will be resolved * or a parsed URL object. * @returns {boolean} Whether the request is for the same origin as the application document. */ function urlIsSameOrigin(requestUrl) { var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl; return (parsed.protocol === originUrl.protocol && parsed.host === originUrl.host); } /** * @ngdoc service * @name $window * * @description * A reference to the browser's `window` object. While `window` * is globally available in JavaScript, it causes testability problems, because * it is a global variable. In angular we always refer to it through the * `$window` service, so it may be overridden, removed or mocked for testing. * * Expressions, like the one defined for the `ngClick` directive in the example * below, are evaluated with respect to the current scope. Therefore, there is * no risk of inadvertently coding in a dependency on a global value in such an * expression. * * @example
    it('should display the greeting in the input box', function() { element(by.model('greeting')).sendKeys('Hello, E2E Tests'); // If we click the button it will block the test runner // element(':button').click(); });
    */ function $WindowProvider(){ this.$get = valueFn(window); } /* global currencyFilter: true, dateFilter: true, filterFilter: true, jsonFilter: true, limitToFilter: true, lowercaseFilter: true, numberFilter: true, orderByFilter: true, uppercaseFilter: true, */ /** * @ngdoc provider * @name $filterProvider * @description * * Filters are just functions which transform input to an output. However filters need to be * Dependency Injected. To achieve this a filter definition consists of a factory function which is * annotated with dependencies and is responsible for creating a filter function. * * ```js * // Filter registration * function MyModule($provide, $filterProvider) { * // create a service to demonstrate injection (not always needed) * $provide.value('greet', function(name){ * return 'Hello ' + name + '!'; * }); * * // register a filter factory which uses the * // greet service to demonstrate DI. * $filterProvider.register('greet', function(greet){ * // return the filter function which uses the greet service * // to generate salutation * return function(text) { * // filters need to be forgiving so check input validity * return text && greet(text) || text; * }; * }); * } * ``` * * The filter function is registered with the `$injector` under the filter name suffix with * `Filter`. * * ```js * it('should be the same instance', inject( * function($filterProvider) { * $filterProvider.register('reverse', function(){ * return ...; * }); * }, * function($filter, reverseFilter) { * expect($filter('reverse')).toBe(reverseFilter); * }); * ``` * * * For more information about how angular filters work, and how to create your own filters, see * {@link guide/filter Filters} in the Angular Developer Guide. */ /** * @ngdoc service * @name $filter * @kind function * @description * Filters are used for formatting data displayed to the user. * * The general syntax in templates is as follows: * * {{ expression [| filter_name[:parameter_value] ... ] }} * * @param {String} name Name of the filter function to retrieve * @return {Function} the filter function * @example

    {{ originalText }}

    {{ filteredText }}

    angular.module('filterExample', []) .controller('MainCtrl', function($scope, $filter) { $scope.originalText = 'hello'; $scope.filteredText = $filter('uppercase')($scope.originalText); });
    */ $FilterProvider.$inject = ['$provide']; function $FilterProvider($provide) { var suffix = 'Filter'; /** * @ngdoc method * @name $filterProvider#register * @param {string|Object} name Name of the filter function, or an object map of filters where * the keys are the filter names and the values are the filter factories. * @returns {Object} Registered filter instance, or if a map of filters was provided then a map * of the registered filter instances. */ function register(name, factory) { if(isObject(name)) { var filters = {}; forEach(name, function(filter, key) { filters[key] = register(key, filter); }); return filters; } else { return $provide.factory(name + suffix, factory); } } this.register = register; this.$get = ['$injector', function($injector) { return function(name) { return $injector.get(name + suffix); }; }]; //////////////////////////////////////// /* global currencyFilter: false, dateFilter: false, filterFilter: false, jsonFilter: false, limitToFilter: false, lowercaseFilter: false, numberFilter: false, orderByFilter: false, uppercaseFilter: false, */ register('currency', currencyFilter); register('date', dateFilter); register('filter', filterFilter); register('json', jsonFilter); register('limitTo', limitToFilter); register('lowercase', lowercaseFilter); register('number', numberFilter); register('orderBy', orderByFilter); register('uppercase', uppercaseFilter); } /** * @ngdoc filter * @name filter * @kind function * * @description * Selects a subset of items from `array` and returns it as a new array. * * @param {Array} array The source array. * @param {string|Object|function()} expression The predicate to be used for selecting items from * `array`. * * Can be one of: * * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against * the contents of the `array`. All strings or objects with string properties in `array` that contain this string * will be returned. The predicate can be negated by prefixing the string with `!`. * * - `Object`: A pattern object can be used to filter specific properties on objects contained * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items * which have property `name` containing "M" and property `phone` containing "1". A special * property name `$` can be used (as in `{$:"text"}`) to accept a match against any * property of the object. That's equivalent to the simple substring match with a `string` * as described above. The predicate can be negated by prefixing the string with `!`. * For Example `{name: "!M"}` predicate will return an array of items which have property `name` * not containing "M". * * - `function(value)`: A predicate function can be used to write arbitrary filters. The function is * called for each element of `array`. The final result is an array of those elements that * the predicate returned true for. * * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in * determining if the expected value (from the filter expression) and actual value (from * the object in the array) should be considered a match. * * Can be one of: * * - `function(actual, expected)`: * The function will be given the object value and the predicate value to compare and * should return true if the item should be included in filtered result. * * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`. * this is essentially strict comparison of expected and actual. * * - `false|undefined`: A short hand for a function which will look for a substring match in case * insensitive way. * * @example
    Search:
    NamePhone
    {{friend.name}} {{friend.phone}}

    Any:
    Name only
    Phone only
    Equality
    NamePhone
    {{friendObj.name}} {{friendObj.phone}}
    var expectFriendNames = function(expectedNames, key) { element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) { arr.forEach(function(wd, i) { expect(wd.getText()).toMatch(expectedNames[i]); }); }); }; it('should search across all fields when filtering with a string', function() { var searchText = element(by.model('searchText')); searchText.clear(); searchText.sendKeys('m'); expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend'); searchText.clear(); searchText.sendKeys('76'); expectFriendNames(['John', 'Julie'], 'friend'); }); it('should search in specific fields when filtering with a predicate object', function() { var searchAny = element(by.model('search.$')); searchAny.clear(); searchAny.sendKeys('i'); expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj'); }); it('should use a equal comparison when comparator is true', function() { var searchName = element(by.model('search.name')); var strict = element(by.model('strict')); searchName.clear(); searchName.sendKeys('Julie'); strict.click(); expectFriendNames(['Julie'], 'friendObj'); });
    */ function filterFilter() { return function(array, expression, comparator) { if (!isArray(array)) return array; var comparatorType = typeof(comparator), predicates = []; predicates.check = function(value) { for (var j = 0; j < predicates.length; j++) { if(!predicates[j](value)) { return false; } } return true; }; if (comparatorType !== 'function') { if (comparatorType === 'boolean' && comparator) { comparator = function(obj, text) { return angular.equals(obj, text); }; } else { comparator = function(obj, text) { if (obj && text && typeof obj === 'object' && typeof text === 'object') { for (var objKey in obj) { if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) && comparator(obj[objKey], text[objKey])) { return true; } } return false; } text = (''+text).toLowerCase(); return (''+obj).toLowerCase().indexOf(text) > -1; }; } } var search = function(obj, text){ if (typeof text === 'string' && text.charAt(0) === '!') { return !search(obj, text.substr(1)); } switch (typeof obj) { case 'boolean': case 'number': case 'string': return comparator(obj, text); case 'object': switch (typeof text) { case 'object': return comparator(obj, text); default: for ( var objKey in obj) { if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { return true; } } break; } return false; case 'array': for ( var i = 0; i < obj.length; i++) { if (search(obj[i], text)) { return true; } } return false; default: return false; } }; switch (typeof expression) { case 'boolean': case 'number': case 'string': // Set up expression object and fall through expression = {$:expression}; // jshint -W086 case 'object': // jshint +W086 for (var key in expression) { (function(path) { if (typeof expression[path] === 'undefined') return; predicates.push(function(value) { return search(path == '$' ? value : (value && value[path]), expression[path]); }); })(key); } break; case 'function': predicates.push(expression); break; default: return array; } var filtered = []; for ( var j = 0; j < array.length; j++) { var value = array[j]; if (predicates.check(value)) { filtered.push(value); } } return filtered; }; } /** * @ngdoc filter * @name currency * @kind function * * @description * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default * symbol for current locale is used. * * @param {number} amount Input to filter. * @param {string=} symbol Currency symbol or identifier to be displayed. * @returns {string} Formatted number. * * * @example

    default currency symbol ($): {{amount | currency}}
    custom currency identifier (USD$): {{amount | currency:"USD$"}}
    it('should init with 1234.56', function() { expect(element(by.id('currency-default')).getText()).toBe('$1,234.56'); expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56'); }); it('should update', function() { if (browser.params.browser == 'safari') { // Safari does not understand the minus key. See // https://github.com/angular/protractor/issues/481 return; } element(by.model('amount')).clear(); element(by.model('amount')).sendKeys('-1234'); expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)'); expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)'); });
    */ currencyFilter.$inject = ['$locale']; function currencyFilter($locale) { var formats = $locale.NUMBER_FORMATS; return function(amount, currencySymbol){ if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). replace(/\u00A4/g, currencySymbol); }; } /** * @ngdoc filter * @name number * @kind function * * @description * Formats a number as text. * * If the input is not a number an empty string is returned. * * @param {number|string} number Number to format. * @param {(number|string)=} fractionSize Number of decimal places to round the number to. * If this is not provided then the fraction size is computed from the current locale's number * formatting pattern. In the case of the default locale, it will be 3. * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. * * @example
    Enter number:
    Default formatting: {{val | number}}
    No fractions: {{val | number:0}}
    Negative number: {{-val | number:4}}
    it('should format numbers', function() { expect(element(by.id('number-default')).getText()).toBe('1,234.568'); expect(element(by.binding('val | number:0')).getText()).toBe('1,235'); expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679'); }); it('should update', function() { element(by.model('val')).clear(); element(by.model('val')).sendKeys('3374.333'); expect(element(by.id('number-default')).getText()).toBe('3,374.333'); expect(element(by.binding('val | number:0')).getText()).toBe('3,374'); expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330'); });
    */ numberFilter.$inject = ['$locale']; function numberFilter($locale) { var formats = $locale.NUMBER_FORMATS; return function(number, fractionSize) { return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize); }; } var DECIMAL_SEP = '.'; function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { if (number == null || !isFinite(number) || isObject(number)) return ''; var isNegative = number < 0; number = Math.abs(number); var numStr = number + '', formatedText = '', parts = []; var hasExponent = false; if (numStr.indexOf('e') !== -1) { var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); if (match && match[2] == '-' && match[3] > fractionSize + 1) { numStr = '0'; number = 0; } else { formatedText = numStr; hasExponent = true; } } if (!hasExponent) { var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; // determine fractionSize if it is not specified if (isUndefined(fractionSize)) { fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); } // safely round numbers in JS without hitting imprecisions of floating-point arithmetics // inspired by: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize); if (number === 0) { isNegative = false; } var fraction = ('' + number).split(DECIMAL_SEP); var whole = fraction[0]; fraction = fraction[1] || ''; var i, pos = 0, lgroup = pattern.lgSize, group = pattern.gSize; if (whole.length >= (lgroup + group)) { pos = whole.length - lgroup; for (i = 0; i < pos; i++) { if ((pos - i)%group === 0 && i !== 0) { formatedText += groupSep; } formatedText += whole.charAt(i); } } for (i = pos; i < whole.length; i++) { if ((whole.length - i)%lgroup === 0 && i !== 0) { formatedText += groupSep; } formatedText += whole.charAt(i); } // format fraction part. while(fraction.length < fractionSize) { fraction += '0'; } if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize); } else { if (fractionSize > 0 && number > -1 && number < 1) { formatedText = number.toFixed(fractionSize); } } parts.push(isNegative ? pattern.negPre : pattern.posPre); parts.push(formatedText); parts.push(isNegative ? pattern.negSuf : pattern.posSuf); return parts.join(''); } function padNumber(num, digits, trim) { var neg = ''; if (num < 0) { neg = '-'; num = -num; } num = '' + num; while(num.length < digits) num = '0' + num; if (trim) num = num.substr(num.length - digits); return neg + num; } function dateGetter(name, size, offset, trim) { offset = offset || 0; return function(date) { var value = date['get' + name](); if (offset > 0 || value > -offset) value += offset; if (value === 0 && offset == -12 ) value = 12; return padNumber(value, size, trim); }; } function dateStrGetter(name, shortForm) { return function(date, formats) { var value = date['get' + name](); var get = uppercase(shortForm ? ('SHORT' + name) : name); return formats[get][value]; }; } function timeZoneGetter(date) { var zone = -1 * date.getTimezoneOffset(); var paddedZone = (zone >= 0) ? "+" : ""; paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2); return paddedZone; } function ampmGetter(date, formats) { return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; } var DATE_FORMATS = { yyyy: dateGetter('FullYear', 4), yy: dateGetter('FullYear', 2, 0, true), y: dateGetter('FullYear', 1), MMMM: dateStrGetter('Month'), MMM: dateStrGetter('Month', true), MM: dateGetter('Month', 2, 1), M: dateGetter('Month', 1, 1), dd: dateGetter('Date', 2), d: dateGetter('Date', 1), HH: dateGetter('Hours', 2), H: dateGetter('Hours', 1), hh: dateGetter('Hours', 2, -12), h: dateGetter('Hours', 1, -12), mm: dateGetter('Minutes', 2), m: dateGetter('Minutes', 1), ss: dateGetter('Seconds', 2), s: dateGetter('Seconds', 1), // while ISO 8601 requires fractions to be prefixed with `.` or `,` // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions sss: dateGetter('Milliseconds', 3), EEEE: dateStrGetter('Day'), EEE: dateStrGetter('Day', true), a: ampmGetter, Z: timeZoneGetter }; var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, NUMBER_STRING = /^\-?\d+$/; /** * @ngdoc filter * @name date * @kind function * * @description * Formats `date` to a string based on the requested `format`. * * `format` string can be composed of the following elements: * * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) * * `'MMMM'`: Month in year (January-December) * * `'MMM'`: Month in year (Jan-Dec) * * `'MM'`: Month in year, padded (01-12) * * `'M'`: Month in year (1-12) * * `'dd'`: Day in month, padded (01-31) * * `'d'`: Day in month (1-31) * * `'EEEE'`: Day in Week,(Sunday-Saturday) * * `'EEE'`: Day in Week, (Sun-Sat) * * `'HH'`: Hour in day, padded (00-23) * * `'H'`: Hour in day (0-23) * * `'hh'`: Hour in am/pm, padded (01-12) * * `'h'`: Hour in am/pm, (1-12) * * `'mm'`: Minute in hour, padded (00-59) * * `'m'`: Minute in hour (0-59) * * `'ss'`: Second in minute, padded (00-59) * * `'s'`: Second in minute (0-59) * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999) * * `'a'`: am/pm marker * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200) * * `format` string can also be one of the following predefined * {@link guide/i18n localizable formats}: * * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale * (e.g. Sep 3, 2010 12:05:08 pm) * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale * (e.g. Friday, September 3, 2010) * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010) * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) * * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g. * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence * (e.g. `"h 'o''clock'"`). * * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is * specified in the string input, the time is considered to be in the local timezone. * @param {string=} format Formatting rules (see Description). If not specified, * `mediumDate` is used. * @returns {string} Formatted string or the input if input is not recognized as date/millis. * * @example {{1288323623006 | date:'medium'}}: {{1288323623006 | date:'medium'}}
    {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}: {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}
    {{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}: {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}
    {{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}: {{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}
    it('should format date', function() { expect(element(by.binding("1288323623006 | date:'medium'")).getText()). toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()). toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/); expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()). toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()). toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/); });
    */ dateFilter.$inject = ['$locale']; function dateFilter($locale) { var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; // 1 2 3 4 5 6 7 8 9 10 11 function jsonStringToDate(string) { var match; if (match = string.match(R_ISO8601_STR)) { var date = new Date(0), tzHour = 0, tzMin = 0, dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear, timeSetter = match[8] ? date.setUTCHours : date.setHours; if (match[9]) { tzHour = int(match[9] + match[10]); tzMin = int(match[9] + match[11]); } dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3])); var h = int(match[4]||0) - tzHour; var m = int(match[5]||0) - tzMin; var s = int(match[6]||0); var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000); timeSetter.call(date, h, m, s, ms); return date; } return string; } return function(date, format) { var text = '', parts = [], fn, match; format = format || 'mediumDate'; format = $locale.DATETIME_FORMATS[format] || format; if (isString(date)) { date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date); } if (isNumber(date)) { date = new Date(date); } if (!isDate(date)) { return date; } while(format) { match = DATE_FORMATS_SPLIT.exec(format); if (match) { parts = concat(parts, match, 1); format = parts.pop(); } else { parts.push(format); format = null; } } forEach(parts, function(value){ fn = DATE_FORMATS[value]; text += fn ? fn(date, $locale.DATETIME_FORMATS) : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); }); return text; }; } /** * @ngdoc filter * @name json * @kind function * * @description * Allows you to convert a JavaScript object into JSON string. * * This filter is mostly useful for debugging. When using the double curly {{value}} notation * the binding is automatically converted to JSON. * * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. * @returns {string} JSON string. * * * @example
    {{ {'name':'value'} | json }}
    it('should jsonify filtered objects', function() { expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/); });
    * */ function jsonFilter() { return function(object) { return toJson(object, true); }; } /** * @ngdoc filter * @name lowercase * @kind function * @description * Converts string to lowercase. * @see angular.lowercase */ var lowercaseFilter = valueFn(lowercase); /** * @ngdoc filter * @name uppercase * @kind function * @description * Converts string to uppercase. * @see angular.uppercase */ var uppercaseFilter = valueFn(uppercase); /** * @ngdoc filter * @name limitTo * @kind function * * @description * Creates a new array or string containing only a specified number of elements. The elements * are taken from either the beginning or the end of the source array or string, as specified by * the value and sign (positive or negative) of `limit`. * * @param {Array|string} input Source array or string to be limited. * @param {string|number} limit The length of the returned array or string. If the `limit` number * is positive, `limit` number of items from the beginning of the source array/string are copied. * If the number is negative, `limit` number of items from the end of the source array/string * are copied. The `limit` will be trimmed if it exceeds `array.length` * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array * had less than `limit` elements. * * @example
    Limit {{numbers}} to:

    Output numbers: {{ numbers | limitTo:numLimit }}

    Limit {{letters}} to:

    Output letters: {{ letters | limitTo:letterLimit }}

    var numLimitInput = element(by.model('numLimit')); var letterLimitInput = element(by.model('letterLimit')); var limitedNumbers = element(by.binding('numbers | limitTo:numLimit')); var limitedLetters = element(by.binding('letters | limitTo:letterLimit')); it('should limit the number array to first three items', function() { expect(numLimitInput.getAttribute('value')).toBe('3'); expect(letterLimitInput.getAttribute('value')).toBe('3'); expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]'); expect(limitedLetters.getText()).toEqual('Output letters: abc'); }); // There is a bug in safari and protractor that doesn't like the minus key // it('should update the output when -3 is entered', function() { // numLimitInput.clear(); // numLimitInput.sendKeys('-3'); // letterLimitInput.clear(); // letterLimitInput.sendKeys('-3'); // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]'); // expect(limitedLetters.getText()).toEqual('Output letters: ghi'); // }); it('should not exceed the maximum size of input array', function() { numLimitInput.clear(); numLimitInput.sendKeys('100'); letterLimitInput.clear(); letterLimitInput.sendKeys('100'); expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]'); expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi'); });
    */ function limitToFilter(){ return function(input, limit) { if (!isArray(input) && !isString(input)) return input; if (Math.abs(Number(limit)) === Infinity) { limit = Number(limit); } else { limit = int(limit); } if (isString(input)) { //NaN check on limit if (limit) { return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length); } else { return ""; } } var out = [], i, n; // if abs(limit) exceeds maximum length, trim it if (limit > input.length) limit = input.length; else if (limit < -input.length) limit = -input.length; if (limit > 0) { i = 0; n = limit; } else { i = input.length + limit; n = input.length; } for (; i=} expression A predicate to be * used by the comparator to determine the order of elements. * * Can be one of: * * - `function`: Getter function. The result of this function will be sorted using the * `<`, `=`, `>` operator. * - `string`: An Angular expression. The result of this expression is used to compare elements * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by * 3 first characters of a property called `name`). The result of a constant expression * is interpreted as a property name to be used in comparisons (for example `"special name"` * to sort object by the value of their `special name` property). An expression can be * optionally prefixed with `+` or `-` to control ascending or descending sort order * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array * element itself is used to compare where sorting. * - `Array`: An array of function or string predicates. The first predicate in the array * is used for sorting, but when two items are equivalent, the next predicate is used. * * If the predicate is missing or empty then it defaults to `'+'`. * * @param {boolean=} reverse Reverse the order of the array. * @returns {Array} Sorted copy of the source array. * * @example
    Sorting predicate = {{predicate}}; reverse = {{reverse}}

    [ unsorted ]
    Name (^) Phone Number Age
    {{friend.name}} {{friend.phone}} {{friend.age}}
    * * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the * desired parameters. * * Example: * * @example
    Name (^) Phone Number Age
    {{friend.name}} {{friend.phone}} {{friend.age}}
    angular.module('orderByExample', []) .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) { var orderBy = $filter('orderBy'); $scope.friends = [ { name: 'John', phone: '555-1212', age: 10 }, { name: 'Mary', phone: '555-9876', age: 19 }, { name: 'Mike', phone: '555-4321', age: 21 }, { name: 'Adam', phone: '555-5678', age: 35 }, { name: 'Julie', phone: '555-8765', age: 29 } ]; $scope.order = function(predicate, reverse) { $scope.friends = orderBy($scope.friends, predicate, reverse); }; $scope.order('-age',false); }]);
    */ orderByFilter.$inject = ['$parse']; function orderByFilter($parse){ return function(array, sortPredicate, reverseOrder) { if (!(isArrayLike(array))) return array; sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; if (sortPredicate.length === 0) { sortPredicate = ['+']; } sortPredicate = map(sortPredicate, function(predicate){ var descending = false, get = predicate || identity; if (isString(predicate)) { if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { descending = predicate.charAt(0) == '-'; predicate = predicate.substring(1); } if ( predicate === '' ) { // Effectively no predicate was passed so we compare identity return reverseComparator(function(a,b) { return compare(a, b); }, descending); } get = $parse(predicate); if (get.constant) { var key = get(); return reverseComparator(function(a,b) { return compare(a[key], b[key]); }, descending); } } return reverseComparator(function(a,b){ return compare(get(a),get(b)); }, descending); }); return slice.call(array).sort(reverseComparator(comparator, reverseOrder)); function comparator(o1, o2){ for ( var i = 0; i < sortPredicate.length; i++) { var comp = sortPredicate[i](o1, o2); if (comp !== 0) return comp; } return 0; } function reverseComparator(comp, descending) { return toBoolean(descending) ? function(a,b){return comp(b,a);} : comp; } function compare(v1, v2){ var t1 = typeof v1; var t2 = typeof v2; if (t1 == t2) { if (isDate(v1) && isDate(v2)) { v1 = v1.valueOf(); v2 = v2.valueOf(); } if (t1 == "string") { v1 = v1.toLowerCase(); v2 = v2.toLowerCase(); } if (v1 === v2) return 0; return v1 < v2 ? -1 : 1; } else { return t1 < t2 ? -1 : 1; } } }; } function ngDirective(directive) { if (isFunction(directive)) { directive = { link: directive }; } directive.restrict = directive.restrict || 'AC'; return valueFn(directive); } /** * @ngdoc directive * @name a * @restrict E * * @description * Modifies the default behavior of the html A tag so that the default action is prevented when * the href attribute is empty. * * This change permits the easy creation of action links with the `ngClick` directive * without changing the location or causing page reloads, e.g.: * `Add Item` */ var htmlAnchorDirective = valueFn({ restrict: 'E', compile: function(element, attr) { if (msie <= 8) { // turn link into a stylable link in IE // but only if it doesn't have name attribute, in which case it's an anchor if (!attr.href && !attr.name) { attr.$set('href', ''); } // add a comment node to anchors to workaround IE bug that causes element content to be reset // to new attribute content if attribute is updated with value containing @ and element also // contains value with @ // see issue #1949 element.append(document.createComment('IE fix')); } if (!attr.href && !attr.xlinkHref && !attr.name) { return function(scope, element) { // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute. var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ? 'xlink:href' : 'href'; element.on('click', function(event){ // if we have no href url, then don't navigate anywhere. if (!element.attr(href)) { event.preventDefault(); } }); }; } } }); /** * @ngdoc directive * @name ngHref * @restrict A * @priority 99 * * @description * Using Angular markup like `{{hash}}` in an href attribute will * make the link go to the wrong URL if the user clicks it before * Angular has a chance to replace the `{{hash}}` markup with its * value. Until Angular replaces the markup the link will be broken * and will most likely return a 404 error. The `ngHref` directive * solves this problem. * * The wrong way to write it: * ```html * * ``` * * The correct way to write it: * ```html * * ``` * * @element A * @param {template} ngHref any string which can contain `{{}}` markup. * * @example * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes * in links and their different behaviors:
    link 1 (link, don't reload)
    link 2 (link, don't reload)
    link 3 (link, reload!)
    anchor (link, don't reload)
    anchor (no link)
    link (link, change location)
    it('should execute ng-click but not reload when href without value', function() { element(by.id('link-1')).click(); expect(element(by.model('value')).getAttribute('value')).toEqual('1'); expect(element(by.id('link-1')).getAttribute('href')).toBe(''); }); it('should execute ng-click but not reload when href empty string', function() { element(by.id('link-2')).click(); expect(element(by.model('value')).getAttribute('value')).toEqual('2'); expect(element(by.id('link-2')).getAttribute('href')).toBe(''); }); it('should execute ng-click and change url when ng-href specified', function() { expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/); element(by.id('link-3')).click(); // At this point, we navigate away from an Angular page, so we need // to use browser.driver to get the base webdriver. browser.wait(function() { return browser.driver.getCurrentUrl().then(function(url) { return url.match(/\/123$/); }); }, 5000, 'page should navigate to /123'); }); xit('should execute ng-click but not reload when href empty string and name specified', function() { element(by.id('link-4')).click(); expect(element(by.model('value')).getAttribute('value')).toEqual('4'); expect(element(by.id('link-4')).getAttribute('href')).toBe(''); }); it('should execute ng-click but not reload when no href but name specified', function() { element(by.id('link-5')).click(); expect(element(by.model('value')).getAttribute('value')).toEqual('5'); expect(element(by.id('link-5')).getAttribute('href')).toBe(null); }); it('should only change url when only ng-href', function() { element(by.model('value')).clear(); element(by.model('value')).sendKeys('6'); expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/); element(by.id('link-6')).click(); // At this point, we navigate away from an Angular page, so we need // to use browser.driver to get the base webdriver. browser.wait(function() { return browser.driver.getCurrentUrl().then(function(url) { return url.match(/\/6$/); }); }, 5000, 'page should navigate to /6'); }); */ /** * @ngdoc directive * @name ngSrc * @restrict A * @priority 99 * * @description * Using Angular markup like `{{hash}}` in a `src` attribute doesn't * work right: The browser will fetch from the URL with the literal * text `{{hash}}` until Angular replaces the expression inside * `{{hash}}`. The `ngSrc` directive solves this problem. * * The buggy way to write it: * ```html * * ``` * * The correct way to write it: * ```html * * ``` * * @element IMG * @param {template} ngSrc any string which can contain `{{}}` markup. */ /** * @ngdoc directive * @name ngSrcset * @restrict A * @priority 99 * * @description * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't * work right: The browser will fetch from the URL with the literal * text `{{hash}}` until Angular replaces the expression inside * `{{hash}}`. The `ngSrcset` directive solves this problem. * * The buggy way to write it: * ```html * * ``` * * The correct way to write it: * ```html * * ``` * * @element IMG * @param {template} ngSrcset any string which can contain `{{}}` markup. */ /** * @ngdoc directive * @name ngDisabled * @restrict A * @priority 100 * * @description * * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: * ```html *
    * *
    * ``` * * The HTML specification does not require browsers to preserve the values of boolean attributes * such as disabled. (Their presence means true and their absence means false.) * If we put an Angular interpolation expression into such an attribute then the * binding information would be lost when the browser removes the attribute. * The `ngDisabled` directive solves this problem for the `disabled` attribute. * This complementary directive is not removed by the browser and so provides * a permanent reliable place to store the binding information. * * @example Click me to toggle:
    it('should toggle button', function() { expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy(); element(by.model('checked')).click(); expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy(); });
    * * @element INPUT * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy, * then special attribute "disabled" will be set on the element */ /** * @ngdoc directive * @name ngChecked * @restrict A * @priority 100 * * @description * The HTML specification does not require browsers to preserve the values of boolean attributes * such as checked. (Their presence means true and their absence means false.) * If we put an Angular interpolation expression into such an attribute then the * binding information would be lost when the browser removes the attribute. * The `ngChecked` directive solves this problem for the `checked` attribute. * This complementary directive is not removed by the browser and so provides * a permanent reliable place to store the binding information. * @example Check me to check both:
    it('should check both checkBoxes', function() { expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy(); element(by.model('master')).click(); expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy(); });
    * * @element INPUT * @param {expression} ngChecked If the {@link guide/expression expression} is truthy, * then special attribute "checked" will be set on the element */ /** * @ngdoc directive * @name ngReadonly * @restrict A * @priority 100 * * @description * The HTML specification does not require browsers to preserve the values of boolean attributes * such as readonly. (Their presence means true and their absence means false.) * If we put an Angular interpolation expression into such an attribute then the * binding information would be lost when the browser removes the attribute. * The `ngReadonly` directive solves this problem for the `readonly` attribute. * This complementary directive is not removed by the browser and so provides * a permanent reliable place to store the binding information. * @example Check me to make text readonly:
    it('should toggle readonly attr', function() { expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy(); element(by.model('checked')).click(); expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy(); });
    * * @element INPUT * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy, * then special attribute "readonly" will be set on the element */ /** * @ngdoc directive * @name ngSelected * @restrict A * @priority 100 * * @description * The HTML specification does not require browsers to preserve the values of boolean attributes * such as selected. (Their presence means true and their absence means false.) * If we put an Angular interpolation expression into such an attribute then the * binding information would be lost when the browser removes the attribute. * The `ngSelected` directive solves this problem for the `selected` attribute. * This complementary directive is not removed by the browser and so provides * a permanent reliable place to store the binding information. * * @example Check me to select:
    it('should select Greetings!', function() { expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy(); element(by.model('selected')).click(); expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy(); });
    * * @element OPTION * @param {expression} ngSelected If the {@link guide/expression expression} is truthy, * then special attribute "selected" will be set on the element */ /** * @ngdoc directive * @name ngOpen * @restrict A * @priority 100 * * @description * The HTML specification does not require browsers to preserve the values of boolean attributes * such as open. (Their presence means true and their absence means false.) * If we put an Angular interpolation expression into such an attribute then the * binding information would be lost when the browser removes the attribute. * The `ngOpen` directive solves this problem for the `open` attribute. * This complementary directive is not removed by the browser and so provides * a permanent reliable place to store the binding information. * @example Check me check multiple:
    Show/Hide me
    it('should toggle open', function() { expect(element(by.id('details')).getAttribute('open')).toBeFalsy(); element(by.model('open')).click(); expect(element(by.id('details')).getAttribute('open')).toBeTruthy(); });
    * * @element DETAILS * @param {expression} ngOpen If the {@link guide/expression expression} is truthy, * then special attribute "open" will be set on the element */ var ngAttributeAliasDirectives = {}; // boolean attrs are evaluated forEach(BOOLEAN_ATTR, function(propName, attrName) { // binding to multiple is not supported if (propName == "multiple") return; var normalized = directiveNormalize('ng-' + attrName); ngAttributeAliasDirectives[normalized] = function() { return { priority: 100, link: function(scope, element, attr) { scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { attr.$set(attrName, !!value); }); } }; }; }); // ng-src, ng-srcset, ng-href are interpolated forEach(['src', 'srcset', 'href'], function(attrName) { var normalized = directiveNormalize('ng-' + attrName); ngAttributeAliasDirectives[normalized] = function() { return { priority: 99, // it needs to run after the attributes are interpolated link: function(scope, element, attr) { var propName = attrName, name = attrName; if (attrName === 'href' && toString.call(element.prop('href')) === '[object SVGAnimatedString]') { name = 'xlinkHref'; attr.$attr[name] = 'xlink:href'; propName = null; } attr.$observe(normalized, function(value) { if (!value) { if (attrName === 'href') { attr.$set(name, null); } return; } attr.$set(name, value); // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need // to set the property as well to achieve the desired effect. // we use attr[attrName] value since $set can sanitize the url. if (msie && propName) element.prop(propName, attr[name]); }); } }; }; }); /* global -nullFormCtrl */ var nullFormCtrl = { $addControl: noop, $removeControl: noop, $setValidity: noop, $setDirty: noop, $setPristine: noop }; /** * @ngdoc type * @name form.FormController * * @property {boolean} $pristine True if user has not interacted with the form yet. * @property {boolean} $dirty True if user has already interacted with the form. * @property {boolean} $valid True if all of the containing forms and controls are valid. * @property {boolean} $invalid True if at least one containing control or form is invalid. * * @property {Object} $error Is an object hash, containing references to all invalid controls or * forms, where: * * - keys are validation tokens (error names), * - values are arrays of controls or forms that are invalid for given error name. * * * Built-in validation tokens: * * - `email` * - `max` * - `maxlength` * - `min` * - `minlength` * - `number` * - `pattern` * - `required` * - `url` * * @description * `FormController` keeps track of all its controls and nested forms as well as the state of them, * such as being valid/invalid or dirty/pristine. * * Each {@link ng.directive:form form} directive creates an instance * of `FormController`. * */ //asks for $scope to fool the BC controller module FormController.$inject = ['$element', '$attrs', '$scope', '$animate']; function FormController(element, attrs, $scope, $animate) { var form = this, parentForm = element.parent().controller('form') || nullFormCtrl, invalidCount = 0, // used to easily determine if we are valid errors = form.$error = {}, controls = []; // init state form.$name = attrs.name || attrs.ngForm; form.$dirty = false; form.$pristine = true; form.$valid = true; form.$invalid = false; parentForm.$addControl(form); // Setup initial state of the control element.addClass(PRISTINE_CLASS); toggleValidCss(true); // convenience method for easy toggling of classes function toggleValidCss(isValid, validationErrorKey) { validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; $animate.setClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); } /** * @ngdoc method * @name form.FormController#$addControl * * @description * Register a control with the form. * * Input elements using ngModelController do this automatically when they are linked. */ form.$addControl = function(control) { // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored // and not added to the scope. Now we throw an error. assertNotHasOwnProperty(control.$name, 'input'); controls.push(control); if (control.$name) { form[control.$name] = control; } }; /** * @ngdoc method * @name form.FormController#$removeControl * * @description * Deregister a control from the form. * * Input elements using ngModelController do this automatically when they are destroyed. */ form.$removeControl = function(control) { if (control.$name && form[control.$name] === control) { delete form[control.$name]; } forEach(errors, function(queue, validationToken) { form.$setValidity(validationToken, true, control); }); arrayRemove(controls, control); }; /** * @ngdoc method * @name form.FormController#$setValidity * * @description * Sets the validity of a form control. * * This method will also propagate to parent forms. */ form.$setValidity = function(validationToken, isValid, control) { var queue = errors[validationToken]; if (isValid) { if (queue) { arrayRemove(queue, control); if (!queue.length) { invalidCount--; if (!invalidCount) { toggleValidCss(isValid); form.$valid = true; form.$invalid = false; } errors[validationToken] = false; toggleValidCss(true, validationToken); parentForm.$setValidity(validationToken, true, form); } } } else { if (!invalidCount) { toggleValidCss(isValid); } if (queue) { if (includes(queue, control)) return; } else { errors[validationToken] = queue = []; invalidCount++; toggleValidCss(false, validationToken); parentForm.$setValidity(validationToken, false, form); } queue.push(control); form.$valid = false; form.$invalid = true; } }; /** * @ngdoc method * @name form.FormController#$setDirty * * @description * Sets the form to a dirty state. * * This method can be called to add the 'ng-dirty' class and set the form to a dirty * state (ng-dirty class). This method will also propagate to parent forms. */ form.$setDirty = function() { $animate.removeClass(element, PRISTINE_CLASS); $animate.addClass(element, DIRTY_CLASS); form.$dirty = true; form.$pristine = false; parentForm.$setDirty(); }; /** * @ngdoc method * @name form.FormController#$setPristine * * @description * Sets the form to its pristine state. * * This method can be called to remove the 'ng-dirty' class and set the form to its pristine * state (ng-pristine class). This method will also propagate to all the controls contained * in this form. * * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after * saving or resetting it. */ form.$setPristine = function () { $animate.removeClass(element, DIRTY_CLASS); $animate.addClass(element, PRISTINE_CLASS); form.$dirty = false; form.$pristine = true; forEach(controls, function(control) { control.$setPristine(); }); }; } /** * @ngdoc directive * @name ngForm * @restrict EAC * * @description * Nestable alias of {@link ng.directive:form `form`} directive. HTML * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a * sub-group of controls needs to be determined. * * Note: the purpose of `ngForm` is to group controls, * but not to be a replacement for the `
    ` tag with all of its capabilities * (e.g. posting to the server, ...). * * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into * related scope, under this name. * */ /** * @ngdoc directive * @name form * @restrict E * * @description * Directive that instantiates * {@link form.FormController FormController}. * * If the `name` attribute is specified, the form controller is published onto the current scope under * this name. * * # Alias: {@link ng.directive:ngForm `ngForm`} * * In Angular forms can be nested. This means that the outer form is valid when all of the child * forms are valid as well. However, browsers do not allow nesting of `` elements, so * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to * `` but can be nested. This allows you to have nested forms, which is very useful when * using Angular validation directives in forms that are dynamically generated using the * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name` * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an * `ngForm` directive and nest these in an outer `form` element. * * * # CSS classes * - `ng-valid` is set if the form is valid. * - `ng-invalid` is set if the form is invalid. * - `ng-pristine` is set if the form is pristine. * - `ng-dirty` is set if the form is dirty. * * Keep in mind that ngAnimate can detect each of these classes when added and removed. * * * # Submitting a form and preventing the default action * * Since the role of forms in client-side Angular applications is different than in classical * roundtrip apps, it is desirable for the browser not to translate the form submission into a full * page reload that sends the data to the server. Instead some javascript logic should be triggered * to handle the form submission in an application-specific way. * * For this reason, Angular prevents the default action (form submission to the server) unless the * `` element has an `action` attribute specified. * * You can use one of the following two ways to specify what javascript method should be called when * a form is submitted: * * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element * - {@link ng.directive:ngClick ngClick} directive on the first * button or input field of type submit (input[type=submit]) * * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit} * or {@link ng.directive:ngClick ngClick} directives. * This is because of the following form submission rules in the HTML specification: * * - If a form has only one input field then hitting enter in this field triggers form submit * (`ngSubmit`) * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter * doesn't trigger submit * - if a form has one or more input fields and one or more buttons or input[type=submit] then * hitting enter in any of the input fields will trigger the click handler on the *first* button or * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) * * * ## Animation Hooks * * Animations in ngForm are triggered when any of the associated CSS classes are added and removed. * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any * other validations that are performed within the form. Animations in ngForm are similar to how * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well * as JS animations. * * The following example shows a simple way to utilize CSS transitions to style a form element * that has been rendered as invalid after it has been validated: * *
     * //be sure to include ngAnimate as a module to hook into more
     * //advanced animations
     * .my-form {
     *   transition:0.5s linear all;
     *   background: white;
     * }
     * .my-form.ng-invalid {
     *   background: red;
     *   color:white;
     * }
     * 
    * * @example userType: Required!
    userType = {{userType}}
    myForm.input.$valid = {{myForm.input.$valid}}
    myForm.input.$error = {{myForm.input.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    it('should initialize to model', function() { var userType = element(by.binding('userType')); var valid = element(by.binding('myForm.input.$valid')); expect(userType.getText()).toContain('guest'); expect(valid.getText()).toContain('true'); }); it('should be invalid if empty', function() { var userType = element(by.binding('userType')); var valid = element(by.binding('myForm.input.$valid')); var userInput = element(by.model('userType')); userInput.clear(); userInput.sendKeys(''); expect(userType.getText()).toEqual('userType ='); expect(valid.getText()).toContain('false'); });
    * * @param {string=} name Name of the form. If specified, the form controller will be published into * related scope, under this name. */ var formDirectiveFactory = function(isNgForm) { return ['$timeout', function($timeout) { var formDirective = { name: 'form', restrict: isNgForm ? 'EAC' : 'E', controller: FormController, compile: function() { return { pre: function(scope, formElement, attr, controller) { if (!attr.action) { // we can't use jq events because if a form is destroyed during submission the default // action is not prevented. see #1238 // // IE 9 is not affected because it doesn't fire a submit event and try to do a full // page reload if the form was destroyed by submission of the form via a click handler // on a button in the form. Looks like an IE9 specific bug. var preventDefaultListener = function(event) { event.preventDefault ? event.preventDefault() : event.returnValue = false; // IE }; addEventListenerFn(formElement[0], 'submit', preventDefaultListener); // unregister the preventDefault listener so that we don't not leak memory but in a // way that will achieve the prevention of the default action. formElement.on('$destroy', function() { $timeout(function() { removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); }, 0, false); }); } var parentFormCtrl = formElement.parent().controller('form'), alias = attr.name || attr.ngForm; if (alias) { setter(scope, alias, controller, alias); } if (parentFormCtrl) { formElement.on('$destroy', function() { parentFormCtrl.$removeControl(controller); if (alias) { setter(scope, alias, undefined, alias); } extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards }); } } }; } }; return formDirective; }]; }; var formDirective = formDirectiveFactory(); var ngFormDirective = formDirectiveFactory(true); /* global VALID_CLASS: true, INVALID_CLASS: true, PRISTINE_CLASS: true, DIRTY_CLASS: true */ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i; var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; var inputType = { /** * @ngdoc input * @name input[text] * * @description * Standard HTML text input with angular data binding, inherited by most of the `input` elements. * * *NOTE* Not every feature offered is available for all input types. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Adds `required` validation error key if the value is not entered. * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. * This parameter is ignored for input[type=password] controls, which will never trim the * input. * * @example
    Single word: Required! Single word only! text = {{text}}
    myForm.input.$valid = {{myForm.input.$valid}}
    myForm.input.$error = {{myForm.input.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    var text = element(by.binding('text')); var valid = element(by.binding('myForm.input.$valid')); var input = element(by.model('text')); it('should initialize to model', function() { expect(text.getText()).toContain('guest'); expect(valid.getText()).toContain('true'); }); it('should be invalid if empty', function() { input.clear(); input.sendKeys(''); expect(text.getText()).toEqual('text ='); expect(valid.getText()).toContain('false'); }); it('should be invalid if multi word', function() { input.clear(); input.sendKeys('hello world'); expect(valid.getText()).toContain('false'); });
    */ 'text': textInputType, /** * @ngdoc input * @name input[number] * * @description * Text input with number validation and transformation. Sets the `number` validation * error if not a valid number. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. * @param {string=} required Sets `required` validation error key if the value is not entered. * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example
    Number: Required! Not valid number! value = {{value}}
    myForm.input.$valid = {{myForm.input.$valid}}
    myForm.input.$error = {{myForm.input.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    var value = element(by.binding('value')); var valid = element(by.binding('myForm.input.$valid')); var input = element(by.model('value')); it('should initialize to model', function() { expect(value.getText()).toContain('12'); expect(valid.getText()).toContain('true'); }); it('should be invalid if empty', function() { input.clear(); input.sendKeys(''); expect(value.getText()).toEqual('value ='); expect(valid.getText()).toContain('false'); }); it('should be invalid if over max', function() { input.clear(); input.sendKeys('123'); expect(value.getText()).toEqual('value ='); expect(valid.getText()).toContain('false'); });
    */ 'number': numberInputType, /** * @ngdoc input * @name input[url] * * @description * Text input with URL validation. Sets the `url` validation error key if the content is not a * valid URL. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example
    URL: Required! Not valid url! text = {{text}}
    myForm.input.$valid = {{myForm.input.$valid}}
    myForm.input.$error = {{myForm.input.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    myForm.$error.url = {{!!myForm.$error.url}}
    var text = element(by.binding('text')); var valid = element(by.binding('myForm.input.$valid')); var input = element(by.model('text')); it('should initialize to model', function() { expect(text.getText()).toContain('http://google.com'); expect(valid.getText()).toContain('true'); }); it('should be invalid if empty', function() { input.clear(); input.sendKeys(''); expect(text.getText()).toEqual('text ='); expect(valid.getText()).toContain('false'); }); it('should be invalid if not url', function() { input.clear(); input.sendKeys('box'); expect(valid.getText()).toContain('false'); });
    */ 'url': urlInputType, /** * @ngdoc input * @name input[email] * * @description * Text input with email validation. Sets the `email` validation error key if not a valid email * address. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example
    Email: Required! Not valid email! text = {{text}}
    myForm.input.$valid = {{myForm.input.$valid}}
    myForm.input.$error = {{myForm.input.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    myForm.$error.email = {{!!myForm.$error.email}}
    var text = element(by.binding('text')); var valid = element(by.binding('myForm.input.$valid')); var input = element(by.model('text')); it('should initialize to model', function() { expect(text.getText()).toContain('me@example.com'); expect(valid.getText()).toContain('true'); }); it('should be invalid if empty', function() { input.clear(); input.sendKeys(''); expect(text.getText()).toEqual('text ='); expect(valid.getText()).toContain('false'); }); it('should be invalid if not email', function() { input.clear(); input.sendKeys('xxx'); expect(valid.getText()).toContain('false'); });
    */ 'email': emailInputType, /** * @ngdoc input * @name input[radio] * * @description * HTML radio button. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string} value The value to which the expression should be set when selected. * @param {string=} name Property name of the form under which the control is published. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @param {string} ngValue Angular expression which sets the value to which the expression should * be set when selected. * * @example
    Red
    Green
    Blue
    color = {{color | json}}
    Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
    it('should change state', function() { var color = element(by.binding('color')); expect(color.getText()).toContain('blue'); element.all(by.model('color')).get(0).click(); expect(color.getText()).toContain('red'); });
    */ 'radio': radioInputType, /** * @ngdoc input * @name input[checkbox] * * @description * HTML checkbox. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} ngTrueValue The value to which the expression should be set when selected. * @param {string=} ngFalseValue The value to which the expression should be set when not selected. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * * @example
    Value1:
    Value2:
    value1 = {{value1}}
    value2 = {{value2}}
    it('should change state', function() { var value1 = element(by.binding('value1')); var value2 = element(by.binding('value2')); expect(value1.getText()).toContain('true'); expect(value2.getText()).toContain('YES'); element(by.model('value1')).click(); element(by.model('value2')).click(); expect(value1.getText()).toContain('false'); expect(value2.getText()).toContain('NO'); });
    */ 'checkbox': checkboxInputType, 'hidden': noop, 'button': noop, 'submit': noop, 'reset': noop, 'file': noop }; // A helper function to call $setValidity and return the value / undefined, // a pattern that is repeated a lot in the input validation logic. function validate(ctrl, validatorName, validity, value){ ctrl.$setValidity(validatorName, validity); return validity ? value : undefined; } function testFlags(validity, flags) { var i, flag; if (flags) { for (i=0; i= minlength, value); }; ctrl.$parsers.push(minLengthValidator); ctrl.$formatters.push(minLengthValidator); } // max length validator if (attr.ngMaxlength) { var maxlength = int(attr.ngMaxlength); var maxLengthValidator = function(value) { return validate(ctrl, 'maxlength', ctrl.$isEmpty(value) || value.length <= maxlength, value); }; ctrl.$parsers.push(maxLengthValidator); ctrl.$formatters.push(maxLengthValidator); } } var numberBadFlags = ['badInput']; function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); ctrl.$parsers.push(function(value) { var empty = ctrl.$isEmpty(value); if (empty || NUMBER_REGEXP.test(value)) { ctrl.$setValidity('number', true); return value === '' ? null : (empty ? value : parseFloat(value)); } else { ctrl.$setValidity('number', false); return undefined; } }); addNativeHtml5Validators(ctrl, 'number', numberBadFlags, null, ctrl.$$validityState); ctrl.$formatters.push(function(value) { return ctrl.$isEmpty(value) ? '' : '' + value; }); if (attr.min) { var minValidator = function(value) { var min = parseFloat(attr.min); return validate(ctrl, 'min', ctrl.$isEmpty(value) || value >= min, value); }; ctrl.$parsers.push(minValidator); ctrl.$formatters.push(minValidator); } if (attr.max) { var maxValidator = function(value) { var max = parseFloat(attr.max); return validate(ctrl, 'max', ctrl.$isEmpty(value) || value <= max, value); }; ctrl.$parsers.push(maxValidator); ctrl.$formatters.push(maxValidator); } ctrl.$formatters.push(function(value) { return validate(ctrl, 'number', ctrl.$isEmpty(value) || isNumber(value), value); }); } function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); var urlValidator = function(value) { return validate(ctrl, 'url', ctrl.$isEmpty(value) || URL_REGEXP.test(value), value); }; ctrl.$formatters.push(urlValidator); ctrl.$parsers.push(urlValidator); } function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { textInputType(scope, element, attr, ctrl, $sniffer, $browser); var emailValidator = function(value) { return validate(ctrl, 'email', ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value), value); }; ctrl.$formatters.push(emailValidator); ctrl.$parsers.push(emailValidator); } function radioInputType(scope, element, attr, ctrl) { // make the name unique, if not defined if (isUndefined(attr.name)) { element.attr('name', nextUid()); } element.on('click', function() { if (element[0].checked) { scope.$apply(function() { ctrl.$setViewValue(attr.value); }); } }); ctrl.$render = function() { var value = attr.value; element[0].checked = (value == ctrl.$viewValue); }; attr.$observe('value', ctrl.$render); } function checkboxInputType(scope, element, attr, ctrl) { var trueValue = attr.ngTrueValue, falseValue = attr.ngFalseValue; if (!isString(trueValue)) trueValue = true; if (!isString(falseValue)) falseValue = false; element.on('click', function() { scope.$apply(function() { ctrl.$setViewValue(element[0].checked); }); }); ctrl.$render = function() { element[0].checked = ctrl.$viewValue; }; // Override the standard `$isEmpty` because a value of `false` means empty in a checkbox. ctrl.$isEmpty = function(value) { return value !== trueValue; }; ctrl.$formatters.push(function(value) { return value === trueValue; }); ctrl.$parsers.push(function(value) { return value ? trueValue : falseValue; }); } /** * @ngdoc directive * @name textarea * @restrict E * * @description * HTML textarea element control with angular data-binding. The data-binding and validation * properties of this element are exactly the same as those of the * {@link ng.directive:input input element}. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of * `required` when you want to data-bind to the `required` attribute. * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. */ /** * @ngdoc directive * @name input * @restrict E * * @description * HTML input element control with angular data-binding. Input control follows HTML5 input types * and polyfills the HTML5 validation behavior for older browsers. * * *NOTE* Not every feature offered is available for all input types. * * @param {string} ngModel Assignable angular expression to data-bind to. * @param {string=} name Property name of the form under which the control is published. * @param {string=} required Sets `required` validation error key if the value is not entered. * @param {boolean=} ngRequired Sets `required` attribute if set to true * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than * minlength. * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than * maxlength. * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for * patterns defined as scope expressions. * @param {string=} ngChange Angular expression to be executed when input changes due to user * interaction with the input element. * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input. * This parameter is ignored for input[type=password] controls, which will never trim the * input. * * @example
    User name: Required!
    Last name: Too short! Too long!

    user = {{user}}
    myForm.userName.$valid = {{myForm.userName.$valid}}
    myForm.userName.$error = {{myForm.userName.$error}}
    myForm.lastName.$valid = {{myForm.lastName.$valid}}
    myForm.lastName.$error = {{myForm.lastName.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    myForm.$error.minlength = {{!!myForm.$error.minlength}}
    myForm.$error.maxlength = {{!!myForm.$error.maxlength}}
    var user = element(by.binding('{{user}}')); var userNameValid = element(by.binding('myForm.userName.$valid')); var lastNameValid = element(by.binding('myForm.lastName.$valid')); var lastNameError = element(by.binding('myForm.lastName.$error')); var formValid = element(by.binding('myForm.$valid')); var userNameInput = element(by.model('user.name')); var userLastInput = element(by.model('user.last')); it('should initialize to model', function() { expect(user.getText()).toContain('{"name":"guest","last":"visitor"}'); expect(userNameValid.getText()).toContain('true'); expect(formValid.getText()).toContain('true'); }); it('should be invalid if empty when required', function() { userNameInput.clear(); userNameInput.sendKeys(''); expect(user.getText()).toContain('{"last":"visitor"}'); expect(userNameValid.getText()).toContain('false'); expect(formValid.getText()).toContain('false'); }); it('should be valid if empty when min length is set', function() { userLastInput.clear(); userLastInput.sendKeys(''); expect(user.getText()).toContain('{"name":"guest","last":""}'); expect(lastNameValid.getText()).toContain('true'); expect(formValid.getText()).toContain('true'); }); it('should be invalid if less than required min length', function() { userLastInput.clear(); userLastInput.sendKeys('xx'); expect(user.getText()).toContain('{"name":"guest"}'); expect(lastNameValid.getText()).toContain('false'); expect(lastNameError.getText()).toContain('minlength'); expect(formValid.getText()).toContain('false'); }); it('should be invalid if longer than max length', function() { userLastInput.clear(); userLastInput.sendKeys('some ridiculously long name'); expect(user.getText()).toContain('{"name":"guest"}'); expect(lastNameValid.getText()).toContain('false'); expect(lastNameError.getText()).toContain('maxlength'); expect(formValid.getText()).toContain('false'); });
    */ var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { return { restrict: 'E', require: '?ngModel', link: function(scope, element, attr, ctrl) { if (ctrl) { (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, $browser); } } }; }]; var VALID_CLASS = 'ng-valid', INVALID_CLASS = 'ng-invalid', PRISTINE_CLASS = 'ng-pristine', DIRTY_CLASS = 'ng-dirty'; /** * @ngdoc type * @name ngModel.NgModelController * * @property {string} $viewValue Actual string value in the view. * @property {*} $modelValue The value in the model, that the control is bound to. * @property {Array.} $parsers Array of functions to execute, as a pipeline, whenever the control reads value from the DOM. Each function is called, in turn, passing the value through to the next. The last return value is used to populate the model. Used to sanitize / convert the value as well as validation. For validation, the parsers should update the validity state using {@link ngModel.NgModelController#$setValidity $setValidity()}, and return `undefined` for invalid values. * * @property {Array.} $formatters Array of functions to execute, as a pipeline, whenever the model value changes. Each function is called, in turn, passing the value through to the next. Used to format / convert values for display in the control and validation. * ```js * function formatter(value) { * if (value) { * return value.toUpperCase(); * } * } * ngModel.$formatters.push(formatter); * ``` * * @property {Array.} $viewChangeListeners Array of functions to execute whenever the * view value has changed. It is called with no arguments, and its return value is ignored. * This can be used in place of additional $watches against the model value. * * @property {Object} $error An object hash with all errors as keys. * * @property {boolean} $pristine True if user has not interacted with the control yet. * @property {boolean} $dirty True if user has already interacted with the control. * @property {boolean} $valid True if there is no error. * @property {boolean} $invalid True if at least one error on the control. * * @description * * `NgModelController` provides API for the `ng-model` directive. The controller contains * services for data-binding, validation, CSS updates, and value formatting and parsing. It * purposefully does not contain any logic which deals with DOM rendering or listening to * DOM events. Such DOM related logic should be provided by other directives which make use of * `NgModelController` for data-binding. * * ## Custom Control Example * This example shows how to use `NgModelController` with a custom control to achieve * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) * collaborate together to achieve the desired result. * * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element * contents be edited in place by the user. This will not work on older browsers. * * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize} * module to automatically remove "bad" content like inline event listener (e.g. ``). * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks * that content using the `$sce` service. * * [contenteditable] { border: 1px solid black; background-color: white; min-height: 20px; } .ng-invalid { border: 1px solid red; } angular.module('customControl', ['ngSanitize']). directive('contenteditable', ['$sce', function($sce) { return { restrict: 'A', // only activate on element attribute require: '?ngModel', // get a hold of NgModelController link: function(scope, element, attrs, ngModel) { if(!ngModel) return; // do nothing if no ng-model // Specify how UI should be updated ngModel.$render = function() { element.html($sce.getTrustedHtml(ngModel.$viewValue || '')); }; // Listen for change events to enable binding element.on('blur keyup change', function() { scope.$evalAsync(read); }); read(); // initialize // Write data to the model function read() { var html = element.html(); // When we clear the content editable the browser leaves a
    behind // If strip-br attribute is provided then we strip this out if( attrs.stripBr && html == '
    ' ) { html = ''; } ngModel.$setViewValue(html); } } }; }]);
    Change me!
    Required!
    it('should data-bind and become invalid', function() { if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') { // SafariDriver can't handle contenteditable // and Firefox driver can't clear contenteditables very well return; } var contentEditable = element(by.css('[contenteditable]')); var content = 'Change me!'; expect(contentEditable.getText()).toEqual(content); contentEditable.clear(); contentEditable.sendKeys(protractor.Key.BACK_SPACE); expect(contentEditable.getText()).toEqual(''); expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/); }); *
    * * */ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', function($scope, $exceptionHandler, $attr, $element, $parse, $animate) { this.$viewValue = Number.NaN; this.$modelValue = Number.NaN; this.$parsers = []; this.$formatters = []; this.$viewChangeListeners = []; this.$pristine = true; this.$dirty = false; this.$valid = true; this.$invalid = false; this.$name = $attr.name; var ngModelGet = $parse($attr.ngModel), ngModelSet = ngModelGet.assign; if (!ngModelSet) { throw minErr('ngModel')('nonassign', "Expression '{0}' is non-assignable. Element: {1}", $attr.ngModel, startingTag($element)); } /** * @ngdoc method * @name ngModel.NgModelController#$render * * @description * Called when the view needs to be updated. It is expected that the user of the ng-model * directive will implement this method. */ this.$render = noop; /** * @ngdoc method * @name ngModel.NgModelController#$isEmpty * * @description * This is called when we need to determine if the value of the input is empty. * * For instance, the required directive does this to work out if the input has data or not. * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`. * * You can override this for input directives whose concept of being empty is different to the * default. The `checkboxInputType` directive does this because in its case a value of `false` * implies empty. * * @param {*} value Reference to check. * @returns {boolean} True if `value` is empty. */ this.$isEmpty = function(value) { return isUndefined(value) || value === '' || value === null || value !== value; }; var parentForm = $element.inheritedData('$formController') || nullFormCtrl, invalidCount = 0, // used to easily determine if we are valid $error = this.$error = {}; // keep invalid keys here // Setup initial state of the control $element.addClass(PRISTINE_CLASS); toggleValidCss(true); // convenience method for easy toggling of classes function toggleValidCss(isValid, validationErrorKey) { validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; $animate.removeClass($element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey); $animate.addClass($element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); } /** * @ngdoc method * @name ngModel.NgModelController#$setValidity * * @description * Change the validity state, and notifies the form when the control changes validity. (i.e. it * does not notify form if given validator is already marked as invalid). * * This method should be called by validators - i.e. the parser or formatter functions. * * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign * to `$error[validationErrorKey]=!isValid` so that it is available for data-binding. * The `validationErrorKey` should be in camelCase and will get converted into dash-case * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` * class and can be bound to as `{{someForm.someControl.$error.myError}}` . * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). */ this.$setValidity = function(validationErrorKey, isValid) { // Purposeful use of ! here to cast isValid to boolean in case it is undefined // jshint -W018 if ($error[validationErrorKey] === !isValid) return; // jshint +W018 if (isValid) { if ($error[validationErrorKey]) invalidCount--; if (!invalidCount) { toggleValidCss(true); this.$valid = true; this.$invalid = false; } } else { toggleValidCss(false); this.$invalid = true; this.$valid = false; invalidCount++; } $error[validationErrorKey] = !isValid; toggleValidCss(isValid, validationErrorKey); parentForm.$setValidity(validationErrorKey, isValid, this); }; /** * @ngdoc method * @name ngModel.NgModelController#$setPristine * * @description * Sets the control to its pristine state. * * This method can be called to remove the 'ng-dirty' class and set the control to its pristine * state (ng-pristine class). */ this.$setPristine = function () { this.$dirty = false; this.$pristine = true; $animate.removeClass($element, DIRTY_CLASS); $animate.addClass($element, PRISTINE_CLASS); }; /** * @ngdoc method * @name ngModel.NgModelController#$setViewValue * * @description * Update the view value. * * This method should be called when the view value changes, typically from within a DOM event handler. * For example {@link ng.directive:input input} and * {@link ng.directive:select select} directives call it. * * It will update the $viewValue, then pass this value through each of the functions in `$parsers`, * which includes any validators. The value that comes out of this `$parsers` pipeline, be applied to * `$modelValue` and the **expression** specified in the `ng-model` attribute. * * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called. * * Note that calling this function does not trigger a `$digest`. * * @param {string} value Value from the view. */ this.$setViewValue = function(value) { this.$viewValue = value; // change to dirty if (this.$pristine) { this.$dirty = true; this.$pristine = false; $animate.removeClass($element, PRISTINE_CLASS); $animate.addClass($element, DIRTY_CLASS); parentForm.$setDirty(); } forEach(this.$parsers, function(fn) { value = fn(value); }); if (this.$modelValue !== value) { this.$modelValue = value; ngModelSet($scope, value); forEach(this.$viewChangeListeners, function(listener) { try { listener(); } catch(e) { $exceptionHandler(e); } }); } }; // model -> value var ctrl = this; $scope.$watch(function ngModelWatch() { var value = ngModelGet($scope); // if scope model value and ngModel value are out of sync if (ctrl.$modelValue !== value) { var formatters = ctrl.$formatters, idx = formatters.length; ctrl.$modelValue = value; while(idx--) { value = formatters[idx](value); } if (ctrl.$viewValue !== value) { ctrl.$viewValue = value; ctrl.$render(); } } return value; }); }]; /** * @ngdoc directive * @name ngModel * * @element input * * @description * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a * property on the scope using {@link ngModel.NgModelController NgModelController}, * which is created and exposed by this directive. * * `ngModel` is responsible for: * * - Binding the view into the model, which other directives such as `input`, `textarea` or `select` * require. * - Providing validation behavior (i.e. required, number, email, url). * - Keeping the state of the control (valid/invalid, dirty/pristine, validation errors). * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`) including animations. * - Registering the control with its parent {@link ng.directive:form form}. * * Note: `ngModel` will try to bind to the property given by evaluating the expression on the * current scope. If the property doesn't already exist on this scope, it will be created * implicitly and added to the scope. * * For best practices on using `ngModel`, see: * * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes) * * For basic examples, how to use `ngModel`, see: * * - {@link ng.directive:input input} * - {@link input[text] text} * - {@link input[checkbox] checkbox} * - {@link input[radio] radio} * - {@link input[number] number} * - {@link input[email] email} * - {@link input[url] url} * - {@link ng.directive:select select} * - {@link ng.directive:textarea textarea} * * # CSS classes * The following CSS classes are added and removed on the associated input/select/textarea element * depending on the validity of the model. * * - `ng-valid` is set if the model is valid. * - `ng-invalid` is set if the model is invalid. * - `ng-pristine` is set if the model is pristine. * - `ng-dirty` is set if the model is dirty. * * Keep in mind that ngAnimate can detect each of these classes when added and removed. * * ## Animation Hooks * * Animations within models are triggered when any of the associated CSS classes are added and removed * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`, * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself. * The animations that are triggered within ngModel are similar to how they work in ngClass and * animations can be hooked into using CSS transitions, keyframes as well as JS animations. * * The following example shows a simple way to utilize CSS transitions to style an input element * that has been rendered as invalid after it has been validated: * *
     * //be sure to include ngAnimate as a module to hook into more
     * //advanced animations
     * .my-input {
     *   transition:0.5s linear all;
     *   background: white;
     * }
     * .my-input.ng-invalid {
     *   background: red;
     *   color:white;
     * }
     * 
    * * @example * Update input to see transitions when valid/invalid. Integer is a valid value.
    *
    */ var ngModelDirective = function() { return { require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { // notify others, especially parent forms var modelCtrl = ctrls[0], formCtrl = ctrls[1] || nullFormCtrl; formCtrl.$addControl(modelCtrl); scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } }; }; /** * @ngdoc directive * @name ngChange * * @description * Evaluate the given expression when the user changes the input. * The expression is evaluated immediately, unlike the JavaScript onchange event * which only triggers at the end of a change (usually, when the user leaves the * form element or presses the return key). * The expression is not evaluated when the value change is coming from the model. * * Note, this directive requires `ngModel` to be present. * * @element input * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change * in input value. * * @example * * * *
    * * *
    * debug = {{confirmed}}
    * counter = {{counter}}
    *
    *
    * * var counter = element(by.binding('counter')); * var debug = element(by.binding('confirmed')); * * it('should evaluate the expression if changing from view', function() { * expect(counter.getText()).toContain('0'); * * element(by.id('ng-change-example1')).click(); * * expect(counter.getText()).toContain('1'); * expect(debug.getText()).toContain('true'); * }); * * it('should not evaluate the expression if changing from model', function() { * element(by.id('ng-change-example2')).click(); * expect(counter.getText()).toContain('0'); * expect(debug.getText()).toContain('true'); * }); * *
    */ var ngChangeDirective = valueFn({ require: 'ngModel', link: function(scope, element, attr, ctrl) { ctrl.$viewChangeListeners.push(function() { scope.$eval(attr.ngChange); }); } }); var requiredDirective = function() { return { require: '?ngModel', link: function(scope, elm, attr, ctrl) { if (!ctrl) return; attr.required = true; // force truthy in case we are on non input element var validator = function(value) { if (attr.required && ctrl.$isEmpty(value)) { ctrl.$setValidity('required', false); return; } else { ctrl.$setValidity('required', true); return value; } }; ctrl.$formatters.push(validator); ctrl.$parsers.unshift(validator); attr.$observe('required', function() { validator(ctrl.$viewValue); }); } }; }; /** * @ngdoc directive * @name ngList * * @description * Text input that converts between a delimited string and an array of strings. The delimiter * can be a fixed string (by default a comma) or a regular expression. * * @element input * @param {string=} ngList optional delimiter that should be used to split the value. If * specified in form `/something/` then the value will be converted into a regular expression. * * @example
    List: Required!
    names = {{names}}
    myForm.namesInput.$valid = {{myForm.namesInput.$valid}}
    myForm.namesInput.$error = {{myForm.namesInput.$error}}
    myForm.$valid = {{myForm.$valid}}
    myForm.$error.required = {{!!myForm.$error.required}}
    var listInput = element(by.model('names')); var names = element(by.binding('{{names}}')); var valid = element(by.binding('myForm.namesInput.$valid')); var error = element(by.css('span.error')); it('should initialize to model', function() { expect(names.getText()).toContain('["igor","misko","vojta"]'); expect(valid.getText()).toContain('true'); expect(error.getCssValue('display')).toBe('none'); }); it('should be invalid if empty', function() { listInput.clear(); listInput.sendKeys(''); expect(names.getText()).toContain(''); expect(valid.getText()).toContain('false'); expect(error.getCssValue('display')).not.toBe('none'); });
    */ var ngListDirective = function() { return { require: 'ngModel', link: function(scope, element, attr, ctrl) { var match = /\/(.*)\//.exec(attr.ngList), separator = match && new RegExp(match[1]) || attr.ngList || ','; var parse = function(viewValue) { // If the viewValue is invalid (say required but empty) it will be `undefined` if (isUndefined(viewValue)) return; var list = []; if (viewValue) { forEach(viewValue.split(separator), function(value) { if (value) list.push(trim(value)); }); } return list; }; ctrl.$parsers.push(parse); ctrl.$formatters.push(function(value) { if (isArray(value)) { return value.join(', '); } return undefined; }); // Override the standard $isEmpty because an empty array means the input is empty. ctrl.$isEmpty = function(value) { return !value || !value.length; }; } }; }; var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; /** * @ngdoc directive * @name ngValue * * @description * Binds the given expression to the value of `input[select]` or `input[radio]`, so * that when the element is selected, the `ngModel` of that element is set to the * bound value. * * `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as * shown below. * * @element input * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute * of the `input` element * * @example

    Which is your favorite?

    You chose {{my.favorite}}
    var favorite = element(by.binding('my.favorite')); it('should initialize to model', function() { expect(favorite.getText()).toContain('unicorns'); }); it('should bind the values to the inputs', function() { element.all(by.model('my.favorite')).get(0).click(); expect(favorite.getText()).toContain('pizza'); });
    */ var ngValueDirective = function() { return { priority: 100, compile: function(tpl, tplAttr) { if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { return function ngValueConstantLink(scope, elm, attr) { attr.$set('value', scope.$eval(attr.ngValue)); }; } else { return function ngValueLink(scope, elm, attr) { scope.$watch(attr.ngValue, function valueWatchAction(value) { attr.$set('value', value); }); }; } } }; }; /** * @ngdoc directive * @name ngBind * @restrict AC * * @description * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element * with the value of a given expression, and to update the text content when the value of that * expression changes. * * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like * `{{ expression }}` which is similar but less verbose. * * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an * element attribute, it makes the bindings invisible to the user while the page is loading. * * An alternative solution to this problem would be using the * {@link ng.directive:ngCloak ngCloak} directive. * * * @element ANY * @param {expression} ngBind {@link guide/expression Expression} to evaluate. * * @example * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
    Enter name:
    Hello !
    it('should check ng-bind', function() { var nameInput = element(by.model('name')); expect(element(by.binding('name')).getText()).toBe('Whirled'); nameInput.clear(); nameInput.sendKeys('world'); expect(element(by.binding('name')).getText()).toBe('world'); });
    */ var ngBindDirective = ngDirective({ compile: function(templateElement) { templateElement.addClass('ng-binding'); return function (scope, element, attr) { element.data('$binding', attr.ngBind); scope.$watch(attr.ngBind, function ngBindWatchAction(value) { // We are purposefully using == here rather than === because we want to // catch when value is "null or undefined" // jshint -W041 element.text(value == undefined ? '' : value); }); }; } }); /** * @ngdoc directive * @name ngBindTemplate * * @description * The `ngBindTemplate` directive specifies that the element * text content should be replaced with the interpolation of the template * in the `ngBindTemplate` attribute. * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}` * expressions. This directive is needed since some HTML elements * (such as TITLE and OPTION) cannot contain SPAN elements. * * @element ANY * @param {string} ngBindTemplate template of form * {{ expression }} to eval. * * @example * Try it here: enter text in text box and watch the greeting change.
    Salutation:
    Name:
    
           
    it('should check ng-bind', function() { var salutationElem = element(by.binding('salutation')); var salutationInput = element(by.model('salutation')); var nameInput = element(by.model('name')); expect(salutationElem.getText()).toBe('Hello World!'); salutationInput.clear(); salutationInput.sendKeys('Greetings'); nameInput.clear(); nameInput.sendKeys('user'); expect(salutationElem.getText()).toBe('Greetings user!'); });
    */ var ngBindTemplateDirective = ['$interpolate', function($interpolate) { return function(scope, element, attr) { // TODO: move this to scenario runner var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); element.addClass('ng-binding').data('$binding', interpolateFn); attr.$observe('ngBindTemplate', function(value) { element.text(value); }); }; }]; /** * @ngdoc directive * @name ngBindHtml * * @description * Creates a binding that will innerHTML the result of evaluating the `expression` into the current * element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link * ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize` * is available, for example, by including {@link ngSanitize} in your module's dependencies (not in * core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to * include "angular-sanitize.js" in your application. * * You may also bypass sanitization for values you know are safe. To do so, bind to * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example * under {@link ng.$sce#Example Strict Contextual Escaping (SCE)}. * * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you * will have an exception (instead of an exploit.) * * @element ANY * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate. * * @example

    angular.module('bindHtmlExample', ['ngSanitize']) .controller('ExampleController', ['$scope', function($scope) { $scope.myHTML = 'I am an HTMLstring with ' + 'links! and other stuff'; }]); it('should check ng-bind-html', function() { expect(element(by.binding('myHTML')).getText()).toBe( 'I am an HTMLstring with links! and other stuff'); });
    */ var ngBindHtmlDirective = ['$sce', '$parse', function($sce, $parse) { return { compile: function (tElement) { tElement.addClass('ng-binding'); return function (scope, element, attr) { element.data('$binding', attr.ngBindHtml); var parsed = $parse(attr.ngBindHtml); function getStringValue() { return (parsed(scope) || '').toString(); } scope.$watch(getStringValue, function ngBindHtmlWatchAction(value) { element.html($sce.getTrustedHtml(parsed(scope)) || ''); }); }; } }; }]; function classDirective(name, selector) { name = 'ngClass' + name; return ['$animate', function($animate) { return { restrict: 'AC', link: function(scope, element, attr) { var oldVal; scope.$watch(attr[name], ngClassWatchAction, true); attr.$observe('class', function(value) { ngClassWatchAction(scope.$eval(attr[name])); }); if (name !== 'ngClass') { scope.$watch('$index', function($index, old$index) { // jshint bitwise: false var mod = $index & 1; if (mod !== (old$index & 1)) { var classes = arrayClasses(scope.$eval(attr[name])); mod === selector ? addClasses(classes) : removeClasses(classes); } }); } function addClasses(classes) { var newClasses = digestClassCounts(classes, 1); attr.$addClass(newClasses); } function removeClasses(classes) { var newClasses = digestClassCounts(classes, -1); attr.$removeClass(newClasses); } function digestClassCounts (classes, count) { var classCounts = element.data('$classCounts') || {}; var classesToUpdate = []; forEach(classes, function (className) { if (count > 0 || classCounts[className]) { classCounts[className] = (classCounts[className] || 0) + count; if (classCounts[className] === +(count > 0)) { classesToUpdate.push(className); } } }); element.data('$classCounts', classCounts); return classesToUpdate.join(' '); } function updateClasses (oldClasses, newClasses) { var toAdd = arrayDifference(newClasses, oldClasses); var toRemove = arrayDifference(oldClasses, newClasses); toRemove = digestClassCounts(toRemove, -1); toAdd = digestClassCounts(toAdd, 1); if (toAdd.length === 0) { $animate.removeClass(element, toRemove); } else if (toRemove.length === 0) { $animate.addClass(element, toAdd); } else { $animate.setClass(element, toAdd, toRemove); } } function ngClassWatchAction(newVal) { if (selector === true || scope.$index % 2 === selector) { var newClasses = arrayClasses(newVal || []); if (!oldVal) { addClasses(newClasses); } else if (!equals(newVal,oldVal)) { var oldClasses = arrayClasses(oldVal); updateClasses(oldClasses, newClasses); } } oldVal = shallowCopy(newVal); } } }; function arrayDifference(tokens1, tokens2) { var values = []; outer: for(var i = 0; i < tokens1.length; i++) { var token = tokens1[i]; for(var j = 0; j < tokens2.length; j++) { if(token == tokens2[j]) continue outer; } values.push(token); } return values; } function arrayClasses (classVal) { if (isArray(classVal)) { return classVal; } else if (isString(classVal)) { return classVal.split(' '); } else if (isObject(classVal)) { var classes = [], i = 0; forEach(classVal, function(v, k) { if (v) { classes = classes.concat(k.split(' ')); } }); return classes; } return classVal; } }]; } /** * @ngdoc directive * @name ngClass * @restrict AC * * @description * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding * an expression that represents all classes to be added. * * The directive operates in three different ways, depending on which of three types the expression * evaluates to: * * 1. If the expression evaluates to a string, the string should be one or more space-delimited class * names. * * 2. If the expression evaluates to an array, each element of the array should be a string that is * one or more space-delimited class names. * * 3. If the expression evaluates to an object, then for each key-value pair of the * object with a truthy value the corresponding key is used as a class name. * * The directive won't add duplicate classes if a particular class was already set. * * When the expression changes, the previously added classes are removed and only then the * new classes are added. * * @animations * add - happens just before the class is applied to the element * remove - happens just before the class is removed from the element * * @element ANY * @param {expression} ngClass {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class * names, an array, or a map of class names to boolean values. In the case of a map, the * names of the properties whose values are truthy will be added as css classes to the * element. * * @example Example that demonstrates basic bindings via ngClass directive.

    Map Syntax Example

    deleted (apply "strike" class)
    important (apply "bold" class)
    error (apply "red" class)

    Using String Syntax


    Using Array Syntax




    .strike { text-decoration: line-through; } .bold { font-weight: bold; } .red { color: red; } var ps = element.all(by.css('p')); it('should let you toggle the class', function() { expect(ps.first().getAttribute('class')).not.toMatch(/bold/); expect(ps.first().getAttribute('class')).not.toMatch(/red/); element(by.model('important')).click(); expect(ps.first().getAttribute('class')).toMatch(/bold/); element(by.model('error')).click(); expect(ps.first().getAttribute('class')).toMatch(/red/); }); it('should let you toggle string example', function() { expect(ps.get(1).getAttribute('class')).toBe(''); element(by.model('style')).clear(); element(by.model('style')).sendKeys('red'); expect(ps.get(1).getAttribute('class')).toBe('red'); }); it('array example should have 3 classes', function() { expect(ps.last().getAttribute('class')).toBe(''); element(by.model('style1')).sendKeys('bold'); element(by.model('style2')).sendKeys('strike'); element(by.model('style3')).sendKeys('red'); expect(ps.last().getAttribute('class')).toBe('bold strike red'); });
    ## Animations The example below demonstrates how to perform animations using ngClass.
    Sample Text
    .base-class { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } .base-class.my-class { color: red; font-size:3em; } it('should check ng-class', function() { expect(element(by.css('.base-class')).getAttribute('class')).not. toMatch(/my-class/); element(by.id('setbtn')).click(); expect(element(by.css('.base-class')).getAttribute('class')). toMatch(/my-class/); element(by.id('clearbtn')).click(); expect(element(by.css('.base-class')).getAttribute('class')).not. toMatch(/my-class/); });
    ## ngClass and pre-existing CSS3 Transitions/Animations The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure. Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure to view the step by step details of {@link ngAnimate.$animate#addclass $animate.addClass} and {@link ngAnimate.$animate#removeclass $animate.removeClass}. */ var ngClassDirective = classDirective('', true); /** * @ngdoc directive * @name ngClassOdd * @restrict AC * * @description * The `ngClassOdd` and `ngClassEven` directives work exactly as * {@link ng.directive:ngClass ngClass}, except they work in * conjunction with `ngRepeat` and take effect only on odd (even) rows. * * This directive can be applied only within the scope of an * {@link ng.directive:ngRepeat ngRepeat}. * * @element ANY * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class names or an array. * * @example
    1. {{name}}
    .odd { color: red; } .even { color: blue; } it('should check ng-class-odd and ng-class-even', function() { expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). toMatch(/odd/); expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). toMatch(/even/); });
    */ var ngClassOddDirective = classDirective('Odd', 0); /** * @ngdoc directive * @name ngClassEven * @restrict AC * * @description * The `ngClassOdd` and `ngClassEven` directives work exactly as * {@link ng.directive:ngClass ngClass}, except they work in * conjunction with `ngRepeat` and take effect only on odd (even) rows. * * This directive can be applied only within the scope of an * {@link ng.directive:ngRepeat ngRepeat}. * * @element ANY * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The * result of the evaluation can be a string representing space delimited class names or an array. * * @example
    1. {{name}}      
    .odd { color: red; } .even { color: blue; } it('should check ng-class-odd and ng-class-even', function() { expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')). toMatch(/odd/); expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')). toMatch(/even/); });
    */ var ngClassEvenDirective = classDirective('Even', 1); /** * @ngdoc directive * @name ngCloak * @restrict AC * * @description * The `ngCloak` directive is used to prevent the Angular html template from being briefly * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this * directive to avoid the undesirable flicker effect caused by the html template display. * * The directive can be applied to the `` element, but the preferred usage is to apply * multiple `ngCloak` directives to small portions of the page to permit progressive rendering * of the browser view. * * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and * `angular.min.js`. * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). * * ```css * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { * display: none !important; * } * ``` * * When this css rule is loaded by the browser, all html elements (including their children) that * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive * during the compilation of the template it deletes the `ngCloak` element attribute, making * the compiled element visible. * * For the best result, the `angular.js` script must be loaded in the head section of the html * document; alternatively, the css rule above must be included in the external stylesheet of the * application. * * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below. * * @element ANY * * @example
    {{ 'hello' }}
    {{ 'hello IE7' }}
    it('should remove the template directive and css class', function() { expect($('#template1').getAttribute('ng-cloak')). toBeNull(); expect($('#template2').getAttribute('ng-cloak')). toBeNull(); });
    * */ var ngCloakDirective = ngDirective({ compile: function(element, attr) { attr.$set('ngCloak', undefined); element.removeClass('ng-cloak'); } }); /** * @ngdoc directive * @name ngController * * @description * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular * supports the principles behind the Model-View-Controller design pattern. * * MVC components in angular: * * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties * are accessed through bindings. * * View — The template (HTML with data bindings) that is rendered into the View. * * Controller — The `ngController` directive specifies a Controller class; the class contains business * logic behind the application to decorate the scope with functions and values * * Note that you can also attach controllers to the DOM by declaring it in a route definition * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller * again using `ng-controller` in the template itself. This will cause the controller to be attached * and executed twice. * * @element ANY * @scope * @priority 500 * @param {expression} ngController Name of a globally accessible constructor function or an * {@link guide/expression expression} that on the current scope evaluates to a * constructor function. The controller instance can be published into a scope property * by specifying `as propertyName`. * * @example * Here is a simple form for editing user contact information. Adding, removing, clearing, and * greeting are methods declared on the controller (see source tab). These methods can * easily be called from the angular markup. Any changes to the data are automatically reflected * in the View without the need for a manual update. * * Two different declaration styles are included below: * * * one binds methods and properties directly onto the controller using `this`: * `ng-controller="SettingsController1 as settings"` * * one injects `$scope` into the controller: * `ng-controller="SettingsController2"` * * The second option is more common in the Angular community, and is generally used in boilerplates * and in this guide. However, there are advantages to binding properties directly to the controller * and avoiding scope. * * * Using `controller as` makes it obvious which controller you are accessing in the template when * multiple controllers apply to an element. * * If you are writing your controllers as classes you have easier access to the properties and * methods, which will appear on the scope, from inside the controller code. * * Since there is always a `.` in the bindings, you don't have to worry about prototypal * inheritance masking primitives. * * This example demonstrates the `controller as` syntax. * * * *
    * Name: * [ greet ]
    * Contact: *
      *
    • * * * [ clear * | X ] *
    • *
    • [ add ]
    • *
    *
    *
    * * angular.module('controllerAsExample', []) * .controller('SettingsController1', SettingsController1); * * function SettingsController1() { * this.name = "John Smith"; * this.contacts = [ * {type: 'phone', value: '408 555 1212'}, * {type: 'email', value: 'john.smith@example.org'} ]; * } * * SettingsController1.prototype.greet = function() { * alert(this.name); * }; * * SettingsController1.prototype.addContact = function() { * this.contacts.push({type: 'email', value: 'yourname@example.org'}); * }; * * SettingsController1.prototype.removeContact = function(contactToRemove) { * var index = this.contacts.indexOf(contactToRemove); * this.contacts.splice(index, 1); * }; * * SettingsController1.prototype.clearContact = function(contact) { * contact.type = 'phone'; * contact.value = ''; * }; * * * it('should check controller as', function() { * var container = element(by.id('ctrl-as-exmpl')); * expect(container.element(by.model('settings.name')) * .getAttribute('value')).toBe('John Smith'); * * var firstRepeat = * container.element(by.repeater('contact in settings.contacts').row(0)); * var secondRepeat = * container.element(by.repeater('contact in settings.contacts').row(1)); * * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe('408 555 1212'); * * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe('john.smith@example.org'); * * firstRepeat.element(by.linkText('clear')).click(); * * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe(''); * * container.element(by.linkText('add')).click(); * * expect(container.element(by.repeater('contact in settings.contacts').row(2)) * .element(by.model('contact.value')) * .getAttribute('value')) * .toBe('yourname@example.org'); * }); * *
    * * This example demonstrates the "attach to `$scope`" style of controller. * * * *
    * Name: * [ greet ]
    * Contact: *
      *
    • * * * [ clear * | X ] *
    • *
    • [ add ]
    • *
    *
    *
    * * angular.module('controllerExample', []) * .controller('SettingsController2', ['$scope', SettingsController2]); * * function SettingsController2($scope) { * $scope.name = "John Smith"; * $scope.contacts = [ * {type:'phone', value:'408 555 1212'}, * {type:'email', value:'john.smith@example.org'} ]; * * $scope.greet = function() { * alert($scope.name); * }; * * $scope.addContact = function() { * $scope.contacts.push({type:'email', value:'yourname@example.org'}); * }; * * $scope.removeContact = function(contactToRemove) { * var index = $scope.contacts.indexOf(contactToRemove); * $scope.contacts.splice(index, 1); * }; * * $scope.clearContact = function(contact) { * contact.type = 'phone'; * contact.value = ''; * }; * } * * * it('should check controller', function() { * var container = element(by.id('ctrl-exmpl')); * * expect(container.element(by.model('name')) * .getAttribute('value')).toBe('John Smith'); * * var firstRepeat = * container.element(by.repeater('contact in contacts').row(0)); * var secondRepeat = * container.element(by.repeater('contact in contacts').row(1)); * * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe('408 555 1212'); * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe('john.smith@example.org'); * * firstRepeat.element(by.linkText('clear')).click(); * * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value')) * .toBe(''); * * container.element(by.linkText('add')).click(); * * expect(container.element(by.repeater('contact in contacts').row(2)) * .element(by.model('contact.value')) * .getAttribute('value')) * .toBe('yourname@example.org'); * }); * *
    */ var ngControllerDirective = [function() { return { scope: true, controller: '@', priority: 500 }; }]; /** * @ngdoc directive * @name ngCsp * * @element html * @description * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. * * This is necessary when developing things like Google Chrome Extensions. * * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things). * For Angular to be CSP compatible there are only two things that we need to do differently: * * - don't use `Function` constructor to generate optimized value getters * - don't inject custom stylesheet into the document * * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp` * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will * be raised. * * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}). * To make those directives work in CSP mode, include the `angular-csp.css` manually. * * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This * autodetection however triggers a CSP error to be logged in the console: * * ``` * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of * script in the following Content Security Policy directive: "default-src 'self'". Note that * 'script-src' was not explicitly set, so 'default-src' is used as a fallback. * ``` * * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp` * directive on the root element of the application or on the `angular.js` script tag, whichever * appears first in the html document. * * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.* * * @example * This example shows how to apply the `ngCsp` directive to the `html` tag. ```html ... ... ``` */ // ngCsp is not implemented as a proper directive any more, because we need it be processed while we // bootstrap the system (before $parse is instantiated), for this reason we just have // the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc /** * @ngdoc directive * @name ngClick * * @description * The ngClick directive allows you to specify custom behavior when * an element is clicked. * * @element ANY * @priority 0 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon * click. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} it('should check ng-click', function() { expect(element(by.binding('count')).getText()).toMatch('0'); element(by.css('button')).click(); expect(element(by.binding('count')).getText()).toMatch('1'); }); */ /* * A collection of directives that allows creation of custom event handlers that are defined as * angular expressions and are compiled and executed within the current scope. */ var ngEventDirectives = {}; // For events that might fire synchronously during DOM manipulation // we need to execute their event handlers asynchronously using $evalAsync, // so that they are not executed in an inconsistent state. var forceAsyncEvents = { 'blur': true, 'focus': true }; forEach( 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '), function(eventName) { var directiveName = directiveNormalize('ng-' + eventName); ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) { return { compile: function($element, attr) { // We expose the powerful $event object on the scope that provides access to the Window, // etc. that isn't protected by the fast paths in $parse. We explicitly request better // checks at the cost of speed since event handler expressions are not executed as // frequently as regular change detection. var fn = $parse(attr[directiveName], /* expensiveChecks */ true); return function ngEventHandler(scope, element) { element.on(eventName, function(event) { var callback = function() { fn(scope, {$event:event}); }; if (forceAsyncEvents[eventName] && $rootScope.$$phase) { scope.$evalAsync(callback); } else { scope.$apply(callback); } }); }; } }; }]; } ); /** * @ngdoc directive * @name ngDblclick * * @description * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event. * * @element ANY * @priority 0 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon * a dblclick. (The Event object is available as `$event`) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMousedown * * @description * The ngMousedown directive allows you to specify custom behavior on mousedown event. * * @element ANY * @priority 0 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon * mousedown. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMouseup * * @description * Specify custom behavior on mouseup event. * * @element ANY * @priority 0 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon * mouseup. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMouseover * * @description * Specify custom behavior on mouseover event. * * @element ANY * @priority 0 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon * mouseover. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMouseenter * * @description * Specify custom behavior on mouseenter event. * * @element ANY * @priority 0 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMouseleave * * @description * Specify custom behavior on mouseleave event. * * @element ANY * @priority 0 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngMousemove * * @description * Specify custom behavior on mousemove event. * * @element ANY * @priority 0 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon * mousemove. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example count: {{count}} */ /** * @ngdoc directive * @name ngKeydown * * @description * Specify custom behavior on keydown event. * * @element ANY * @priority 0 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) * * @example key down count: {{count}} */ /** * @ngdoc directive * @name ngKeyup * * @description * Specify custom behavior on keyup event. * * @element ANY * @priority 0 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.) * * @example

    Typing in the input box below updates the key count

    key up count: {{count}}

    Typing in the input box below updates the keycode

    event keyCode: {{ event.keyCode }}

    event altKey: {{ event.altKey }}

    */ /** * @ngdoc directive * @name ngKeypress * * @description * Specify custom behavior on keypress event. * * @element ANY * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon * keypress. ({@link guide/expression#-event- Event object is available as `$event`} * and can be interrogated for keyCode, altKey, etc.) * * @example key press count: {{count}} */ /** * @ngdoc directive * @name ngSubmit * * @description * Enables binding angular expressions to onsubmit events. * * Additionally it prevents the default action (which for form means sending the request to the * server and reloading the current page), but only if the form does not contain `action`, * `data-action`, or `x-action` attributes. * *
    * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and * `ngSubmit` handlers together. See the * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation} * for a detailed discussion of when `ngSubmit` may be triggered. *
    * * @element form * @priority 0 * @param {expression} ngSubmit {@link guide/expression Expression} to eval. * ({@link guide/expression#-event- Event object is available as `$event`}) * * @example
    Enter text and hit enter:
    list={{list}}
    it('should check ng-submit', function() { expect(element(by.binding('list')).getText()).toBe('list=[]'); element(by.css('#submit')).click(); expect(element(by.binding('list')).getText()).toContain('hello'); expect(element(by.model('text')).getAttribute('value')).toBe(''); }); it('should ignore empty strings', function() { expect(element(by.binding('list')).getText()).toBe('list=[]'); element(by.css('#submit')).click(); element(by.css('#submit')).click(); expect(element(by.binding('list')).getText()).toContain('hello'); });
    */ /** * @ngdoc directive * @name ngFocus * * @description * Specify custom behavior on focus event. * * Note: As the `focus` event is executed synchronously when calling `input.focus()` * AngularJS executes the expression using `scope.$evalAsync` if the event is fired * during an `$apply` to ensure a consistent state. * * @element window, input, select, textarea, a * @priority 0 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon * focus. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example * See {@link ng.directive:ngClick ngClick} */ /** * @ngdoc directive * @name ngBlur * * @description * Specify custom behavior on blur event. * * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when * an element has lost focus. * * Note: As the `blur` event is executed synchronously also during DOM manipulations * (e.g. removing a focussed input), * AngularJS executes the expression using `scope.$evalAsync` if the event is fired * during an `$apply` to ensure a consistent state. * * @element window, input, select, textarea, a * @priority 0 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon * blur. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example * See {@link ng.directive:ngClick ngClick} */ /** * @ngdoc directive * @name ngCopy * * @description * Specify custom behavior on copy event. * * @element window, input, select, textarea, a * @priority 0 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon * copy. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example copied: {{copied}} */ /** * @ngdoc directive * @name ngCut * * @description * Specify custom behavior on cut event. * * @element window, input, select, textarea, a * @priority 0 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon * cut. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example cut: {{cut}} */ /** * @ngdoc directive * @name ngPaste * * @description * Specify custom behavior on paste event. * * @element window, input, select, textarea, a * @priority 0 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon * paste. ({@link guide/expression#-event- Event object is available as `$event`}) * * @example pasted: {{paste}} */ /** * @ngdoc directive * @name ngIf * @restrict A * * @description * The `ngIf` directive removes or recreates a portion of the DOM tree based on an * {expression}. If the expression assigned to `ngIf` evaluates to a false * value then the element is removed from the DOM, otherwise a clone of the * element is reinserted into the DOM. * * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the * element in the DOM rather than changing its visibility via the `display` css property. A common * case when this difference is significant is when using css selectors that rely on an element's * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes. * * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope * is created when the element is restored. The scope created within `ngIf` inherits from * its parent scope using * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance). * An important implication of this is if `ngModel` is used within `ngIf` to bind to * a javascript primitive defined in the parent scope. In this case any modifications made to the * variable within the child scope will override (hide) the value in the parent scope. * * Also, `ngIf` recreates elements using their compiled state. An example of this behavior * is if an element's class attribute is directly modified after it's compiled, using something like * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element * the added class will be lost because the original compiled state is used to regenerate the element. * * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter` * and `leave` effects. * * @animations * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container * leave - happens just before the `ngIf` contents are removed from the DOM * * @element ANY * @scope * @priority 600 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then * the element is removed from the DOM tree. If it is truthy a copy of the compiled * element is added to the DOM tree. * * @example Click me:
    Show when checked: I'm removed when the checkbox is unchecked.
    .animate-if { background:white; border:1px solid black; padding:10px; } .animate-if.ng-enter, .animate-if.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } .animate-if.ng-enter, .animate-if.ng-leave.ng-leave-active { opacity:0; } .animate-if.ng-leave, .animate-if.ng-enter.ng-enter-active { opacity:1; }
    */ var ngIfDirective = ['$animate', function($animate) { return { transclude: 'element', priority: 600, terminal: true, restrict: 'A', $$tlb: true, link: function ($scope, $element, $attr, ctrl, $transclude) { var block, childScope, previousElements; $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { if (toBoolean(value)) { if (!childScope) { childScope = $scope.$new(); $transclude(childScope, function (clone) { clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); // Note: We only need the first/last node of the cloned nodes. // However, we need to keep the reference to the jqlite wrapper as it might be changed later // by a directive with templateUrl when its template arrives. block = { clone: clone }; $animate.enter(clone, $element.parent(), $element); }); } } else { if(previousElements) { previousElements.remove(); previousElements = null; } if(childScope) { childScope.$destroy(); childScope = null; } if(block) { previousElements = getBlockElements(block.clone); $animate.leave(previousElements, function() { previousElements = null; }); block = null; } } }); } }; }]; /** * @ngdoc directive * @name ngInclude * @restrict ECA * * @description * Fetches, compiles and includes an external HTML fragment. * * By default, the template URL is restricted to the same domain and protocol as the * application document. This is done by calling {@link ng.$sce#getTrustedResourceUrl * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or * [wrap them](ng.$sce#trustAsResourceUrl) as trusted values. Refer to Angular's {@link * ng.$sce Strict Contextual Escaping}. * * In addition, the browser's * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/) * policy may further restrict whether the template is successfully loaded. * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://` * access on some browsers. * * @animations * enter - animation is used to bring new content into the browser. * leave - animation is used to animate existing content away. * * The enter and leave animation occur concurrently. * * @scope * @priority 400 * * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`. * @param {string=} onload Expression to evaluate when a new partial is loaded. * * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll * $anchorScroll} to scroll the viewport after the content is loaded. * * - If the attribute is not set, disable scrolling. * - If the attribute is set without value, enable scrolling. * - Otherwise enable scrolling only if the expression evaluates to truthy value. * * @example
    url of the template: {{template.url}}
    angular.module('includeExample', ['ngAnimate']) .controller('ExampleController', ['$scope', function($scope) { $scope.templates = [ { name: 'template1.html', url: 'template1.html'}, { name: 'template2.html', url: 'template2.html'} ]; $scope.template = $scope.templates[0]; }]); Content of template1.html Content of template2.html .slide-animate-container { position:relative; background:white; border:1px solid black; height:40px; overflow:hidden; } .slide-animate { padding:10px; } .slide-animate.ng-enter, .slide-animate.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; position:absolute; top:0; left:0; right:0; bottom:0; display:block; padding:10px; } .slide-animate.ng-enter { top:-50px; } .slide-animate.ng-enter.ng-enter-active { top:0; } .slide-animate.ng-leave { top:0; } .slide-animate.ng-leave.ng-leave-active { top:50px; } var templateSelect = element(by.model('template')); var includeElem = element(by.css('[ng-include]')); it('should load template1.html', function() { expect(includeElem.getText()).toMatch(/Content of template1.html/); }); it('should load template2.html', function() { if (browser.params.browser == 'firefox') { // Firefox can't handle using selects // See https://github.com/angular/protractor/issues/480 return; } templateSelect.click(); templateSelect.all(by.css('option')).get(2).click(); expect(includeElem.getText()).toMatch(/Content of template2.html/); }); it('should change to blank', function() { if (browser.params.browser == 'firefox') { // Firefox can't handle using selects return; } templateSelect.click(); templateSelect.all(by.css('option')).get(0).click(); expect(includeElem.isPresent()).toBe(false); });
    */ /** * @ngdoc event * @name ngInclude#$includeContentRequested * @eventType emit on the scope ngInclude was declared in * @description * Emitted every time the ngInclude content is requested. */ /** * @ngdoc event * @name ngInclude#$includeContentLoaded * @eventType emit on the current ngInclude scope * @description * Emitted every time the ngInclude content is reloaded. */ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce', function($http, $templateCache, $anchorScroll, $animate, $sce) { return { restrict: 'ECA', priority: 400, terminal: true, transclude: 'element', controller: angular.noop, compile: function(element, attr) { var srcExp = attr.ngInclude || attr.src, onloadExp = attr.onload || '', autoScrollExp = attr.autoscroll; return function(scope, $element, $attr, ctrl, $transclude) { var changeCounter = 0, currentScope, previousElement, currentElement; var cleanupLastIncludeContent = function() { if(previousElement) { previousElement.remove(); previousElement = null; } if(currentScope) { currentScope.$destroy(); currentScope = null; } if(currentElement) { $animate.leave(currentElement, function() { previousElement = null; }); previousElement = currentElement; currentElement = null; } }; scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { var afterAnimation = function() { if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { $anchorScroll(); } }; var thisChangeId = ++changeCounter; if (src) { $http.get(src, {cache: $templateCache}).success(function(response) { if (thisChangeId !== changeCounter) return; var newScope = scope.$new(); ctrl.template = response; // Note: This will also link all children of ng-include that were contained in the original // html. If that content contains controllers, ... they could pollute/change the scope. // However, using ng-include on an element with additional content does not make sense... // Note: We can't remove them in the cloneAttchFn of $transclude as that // function is called before linking the content, which would apply child // directives to non existing elements. var clone = $transclude(newScope, function(clone) { cleanupLastIncludeContent(); $animate.enter(clone, null, $element, afterAnimation); }); currentScope = newScope; currentElement = clone; currentScope.$emit('$includeContentLoaded'); scope.$eval(onloadExp); }).error(function() { if (thisChangeId === changeCounter) cleanupLastIncludeContent(); }); scope.$emit('$includeContentRequested'); } else { cleanupLastIncludeContent(); ctrl.template = null; } }); }; } }; }]; // This directive is called during the $transclude call of the first `ngInclude` directive. // It will replace and compile the content of the element with the loaded template. // We need this directive so that the element content is already filled when // the link function of another directive on the same element as ngInclude // is called. var ngIncludeFillContentDirective = ['$compile', function($compile) { return { restrict: 'ECA', priority: -400, require: 'ngInclude', link: function(scope, $element, $attr, ctrl) { $element.html(ctrl.template); $compile($element.contents())(scope); } }; }]; /** * @ngdoc directive * @name ngInit * @restrict AC * * @description * The `ngInit` directive allows you to evaluate an expression in the * current scope. * *
    * The only appropriate use of `ngInit` is for aliasing special properties of * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you * should use {@link guide/controller controllers} rather than `ngInit` * to initialize values on a scope. *
    *
    * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make * sure you have parenthesis for correct precedence: *
     *   
    *
    *
    * * @priority 450 * * @element ANY * @param {expression} ngInit {@link guide/expression Expression} to eval. * * @example
    list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};
    it('should alias index positions', function() { var elements = element.all(by.css('.example-init')); expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;'); expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;'); expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;'); expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;'); });
    */ var ngInitDirective = ngDirective({ priority: 450, compile: function() { return { pre: function(scope, element, attrs) { scope.$eval(attrs.ngInit); } }; } }); /** * @ngdoc directive * @name ngNonBindable * @restrict AC * @priority 1000 * * @description * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current * DOM element. This is useful if the element contains what appears to be Angular directives and * bindings but which should be ignored by Angular. This could be the case if you have a site that * displays snippets of code, for instance. * * @element ANY * * @example * In this example there are two locations where a simple interpolation binding (`{{}}`) is present, * but the one wrapped in `ngNonBindable` is left alone. * * @example
    Normal: {{1 + 2}}
    Ignored: {{1 + 2}}
    it('should check ng-non-bindable', function() { expect(element(by.binding('1 + 2')).getText()).toContain('3'); expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/); });
    */ var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); /** * @ngdoc directive * @name ngPluralize * @restrict EA * * @description * `ngPluralize` is a directive that displays messages according to en-US localization rules. * These rules are bundled with angular.js, but can be overridden * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive * by specifying the mappings between * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) * and the strings to be displayed. * * # Plural categories and explicit number rules * There are two * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html) * in Angular's default en-US locale: "one" and "other". * * While a plural category may match many numbers (for example, in en-US locale, "other" can match * any number that is not 1), an explicit number rule can only match one number. For example, the * explicit number rule for "3" matches the number 3. There are examples of plural categories * and explicit number rules throughout the rest of this documentation. * * # Configuring ngPluralize * You configure ngPluralize by providing 2 attributes: `count` and `when`. * You can also provide an optional attribute, `offset`. * * The value of the `count` attribute can be either a string or an {@link guide/expression * Angular expression}; these are evaluated on the current scope for its bound value. * * The `when` attribute specifies the mappings between plural categories and the actual * string to be displayed. The value of the attribute should be a JSON object. * * The following example shows how to configure ngPluralize: * * ```html * * *``` * * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for * other numbers, for example 12, so that instead of showing "12 people are viewing", you can * show "a dozen people are viewing". * * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted * into pluralized strings. In the previous example, Angular will replace `{}` with * `{{personCount}}`. The closed braces `{}` is a placeholder * for {{numberExpression}}. * * # Configuring ngPluralize with offset * The `offset` attribute allows further customization of pluralized text, which can result in * a better user experience. For example, instead of the message "4 people are viewing this document", * you might display "John, Kate and 2 others are viewing this document". * The offset attribute allows you to offset a number by any desired value. * Let's take a look at an example: * * ```html * * * ``` * * Notice that we are still using two plural categories(one, other), but we added * three explicit number rules 0, 1 and 2. * When one person, perhaps John, views the document, "John is viewing" will be shown. * When three people view the document, no explicit number rule is found, so * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing" * is shown. * * Note that when you specify offsets, you must provide explicit number rules for * numbers from 0 up to and including the offset. If you use an offset of 3, for example, * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for * plural categories "one" and "other". * * @param {string|expression} count The variable to be bound to. * @param {string} when The mapping between plural category to its corresponding strings. * @param {number=} offset Offset to deduct from the total number. * * @example
    Person 1:
    Person 2:
    Number of People:
    Without Offset:
    With Offset(2):
    it('should show correct pluralized string', function() { var withoutOffset = element.all(by.css('ng-pluralize')).get(0); var withOffset = element.all(by.css('ng-pluralize')).get(1); var countInput = element(by.model('personCount')); expect(withoutOffset.getText()).toEqual('1 person is viewing.'); expect(withOffset.getText()).toEqual('Igor is viewing.'); countInput.clear(); countInput.sendKeys('0'); expect(withoutOffset.getText()).toEqual('Nobody is viewing.'); expect(withOffset.getText()).toEqual('Nobody is viewing.'); countInput.clear(); countInput.sendKeys('2'); expect(withoutOffset.getText()).toEqual('2 people are viewing.'); expect(withOffset.getText()).toEqual('Igor and Misko are viewing.'); countInput.clear(); countInput.sendKeys('3'); expect(withoutOffset.getText()).toEqual('3 people are viewing.'); expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.'); countInput.clear(); countInput.sendKeys('4'); expect(withoutOffset.getText()).toEqual('4 people are viewing.'); expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.'); }); it('should show data-bound names', function() { var withOffset = element.all(by.css('ng-pluralize')).get(1); var personCount = element(by.model('personCount')); var person1 = element(by.model('person1')); var person2 = element(by.model('person2')); personCount.clear(); personCount.sendKeys('4'); person1.clear(); person1.sendKeys('Di'); person2.clear(); person2.sendKeys('Vojta'); expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.'); });
    */ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { var BRACE = /{}/g; return { restrict: 'EA', link: function(scope, element, attr) { var numberExp = attr.count, whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs offset = attr.offset || 0, whens = scope.$eval(whenExp) || {}, whensExpFns = {}, startSymbol = $interpolate.startSymbol(), endSymbol = $interpolate.endSymbol(), isWhen = /^when(Minus)?(.+)$/; forEach(attr, function(expression, attributeName) { if (isWhen.test(attributeName)) { whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] = element.attr(attr.$attr[attributeName]); } }); forEach(whens, function(expression, key) { whensExpFns[key] = $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + offset + endSymbol)); }); scope.$watch(function ngPluralizeWatch() { var value = parseFloat(scope.$eval(numberExp)); if (!isNaN(value)) { //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, //check it against pluralization rules in $locale service if (!(value in whens)) value = $locale.pluralCat(value - offset); return whensExpFns[value](scope, element, true); } else { return ''; } }, function ngPluralizeWatchAction(newVal) { element.text(newVal); }); } }; }]; /** * @ngdoc directive * @name ngRepeat * * @description * The `ngRepeat` directive instantiates a template once per item from a collection. Each template * instance gets its own scope, where the given loop variable is set to the current collection item, * and `$index` is set to the item index or key. * * Special properties are exposed on the local scope of each template instance, including: * * | Variable | Type | Details | * |-----------|-----------------|-----------------------------------------------------------------------------| * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) | * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. | * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. | * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | * * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. * This may be useful when, for instance, nesting ngRepeats. * * # Special repeat start and end points * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively. * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on) * up to and including the ending HTML tag where **ng-repeat-end** is placed. * * The example below makes use of this feature: * ```html *
    * Header {{ item }} *
    *
    * Body {{ item }} *
    *
    * Footer {{ item }} *
    * ``` * * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to: * ```html *
    * Header A *
    *
    * Body A *
    *
    * Footer A *
    *
    * Header B *
    *
    * Body B *
    *
    * Footer B *
    * ``` * * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**). * * @animations * **.enter** - when a new item is added to the list or when an item is revealed after a filter * * **.leave** - when an item is removed from the list or when an item is filtered out * * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered * * @element ANY * @scope * @priority 1000 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These * formats are currently supported: * * * `variable in expression` – where variable is the user defined loop variable and `expression` * is a scope expression giving the collection to enumerate. * * For example: `album in artist.albums`. * * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, * and `expression` is the scope expression giving the collection to enumerate. * * For example: `(name, age) in {'adam':10, 'amalie':12}`. * * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function * which can be used to associate the objects in the collection with the DOM elements. If no tracking function * is specified the ng-repeat associates elements by identity in the collection. It is an error to have * more than one tracking function to resolve to the same key. (This would mean that two distinct objects are * mapped to the same DOM element, which is not possible.) Filters should be applied to the expression, * before specifying a tracking expression. * * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements * will be associated by item identity in the array. * * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements * with the corresponding item in the array by identity. Moving the same object in array would move the DOM * element in the same way in the DOM. * * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this * case the object identity does not matter. Two objects are considered equivalent as long as their `id` * property is same. * * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter * to items in conjunction with a tracking expression. * * @example * This example initializes the scope to a list of names and * then uses `ngRepeat` to display every person:
    I have {{friends.length}} friends. They are:
    • [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
    .example-animate-container { background:white; border:1px solid black; list-style:none; margin:0; padding:0 10px; } .animate-repeat { line-height:40px; list-style:none; box-sizing:border-box; } .animate-repeat.ng-move, .animate-repeat.ng-enter, .animate-repeat.ng-leave { -webkit-transition:all linear 0.5s; transition:all linear 0.5s; } .animate-repeat.ng-leave.ng-leave-active, .animate-repeat.ng-move, .animate-repeat.ng-enter { opacity:0; max-height:0; } .animate-repeat.ng-leave, .animate-repeat.ng-move.ng-move-active, .animate-repeat.ng-enter.ng-enter-active { opacity:1; max-height:40px; } var friends = element.all(by.repeater('friend in friends')); it('should render initial data set', function() { expect(friends.count()).toBe(10); expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.'); expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.'); expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.'); expect(element(by.binding('friends.length')).getText()) .toMatch("I have 10 friends. They are:"); }); it('should update repeater when filter predicate changes', function() { expect(friends.count()).toBe(10); element(by.model('q')).sendKeys('ma'); expect(friends.count()).toBe(2); expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.'); expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.'); });
    */ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var NG_REMOVED = '$$NG_REMOVED'; var ngRepeatMinErr = minErr('ngRepeat'); return { transclude: 'element', priority: 1000, terminal: true, $$tlb: true, link: function($scope, $element, $attr, ctrl, $transclude){ var expression = $attr.ngRepeat; var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, hashFnLocals = {$id: hashKey}; if (!match) { throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.", expression); } lhs = match[1]; rhs = match[2]; trackByExp = match[3]; if (trackByExp) { trackByExpGetter = $parse(trackByExp); trackByIdExpFn = function(key, value, index) { // assign key, value, and $index to the locals so that they can be used in hash functions if (keyIdentifier) hashFnLocals[keyIdentifier] = key; hashFnLocals[valueIdentifier] = value; hashFnLocals.$index = index; return trackByExpGetter($scope, hashFnLocals); }; } else { trackByIdArrayFn = function(key, value) { return hashKey(value); }; trackByIdObjFn = function(key) { return key; }; } match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); if (!match) { throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.", lhs); } valueIdentifier = match[3] || match[1]; keyIdentifier = match[2]; // Store a list of elements from previous run. This is a hash where key is the item from the // iterator, and the value is objects with following properties. // - scope: bound scope // - element: previous element. // - index: position var lastBlockMap = {}; //watch props $scope.$watchCollection(rhs, function ngRepeatAction(collection){ var index, length, previousNode = $element[0], // current position of the node nextNode, // Same as lastBlockMap but it has the current state. It will become the // lastBlockMap on the next iteration. nextBlockMap = {}, arrayLength, childScope, key, value, // key/value of iteration trackById, trackByIdFn, collectionKeys, block, // last object information {scope, element, id} nextBlockOrder = [], elementsToRemove; if (isArrayLike(collection)) { collectionKeys = collection; trackByIdFn = trackByIdExpFn || trackByIdArrayFn; } else { trackByIdFn = trackByIdExpFn || trackByIdObjFn; // if object, extract keys, sort them and use to determine order of iteration over obj props collectionKeys = []; for (key in collection) { if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { collectionKeys.push(key); } } collectionKeys.sort(); } arrayLength = collectionKeys.length; // locate existing items length = nextBlockOrder.length = collectionKeys.length; for(index = 0; index < length; index++) { key = (collection === collectionKeys) ? index : collectionKeys[index]; value = collection[key]; trackById = trackByIdFn(key, value, index); assertNotHasOwnProperty(trackById, '`track by` id'); if(lastBlockMap.hasOwnProperty(trackById)) { block = lastBlockMap[trackById]; delete lastBlockMap[trackById]; nextBlockMap[trackById] = block; nextBlockOrder[index] = block; } else if (nextBlockMap.hasOwnProperty(trackById)) { // restore lastBlockMap forEach(nextBlockOrder, function(block) { if (block && block.scope) lastBlockMap[block.id] = block; }); // This is a duplicate and we need to throw an error throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}", expression, trackById, toJson(value)); } else { // new never before seen block nextBlockOrder[index] = { id: trackById }; nextBlockMap[trackById] = false; } } // remove existing items for (key in lastBlockMap) { // lastBlockMap is our own object so we don't need to use special hasOwnPropertyFn if (lastBlockMap.hasOwnProperty(key)) { block = lastBlockMap[key]; elementsToRemove = getBlockElements(block.clone); $animate.leave(elementsToRemove); forEach(elementsToRemove, function(element) { element[NG_REMOVED] = true; }); block.scope.$destroy(); } } // we are not using forEach for perf reasons (trying to avoid #call) for (index = 0, length = collectionKeys.length; index < length; index++) { key = (collection === collectionKeys) ? index : collectionKeys[index]; value = collection[key]; block = nextBlockOrder[index]; if (nextBlockOrder[index - 1]) previousNode = getBlockEnd(nextBlockOrder[index - 1]); if (block.scope) { // if we have already seen this object, then we need to reuse the // associated scope/element childScope = block.scope; nextNode = previousNode; do { nextNode = nextNode.nextSibling; } while(nextNode && nextNode[NG_REMOVED]); if (getBlockStart(block) != nextNode) { // existing item which got moved $animate.move(getBlockElements(block.clone), null, jqLite(previousNode)); } previousNode = getBlockEnd(block); } else { // new item which we don't know about childScope = $scope.$new(); } childScope[valueIdentifier] = value; if (keyIdentifier) childScope[keyIdentifier] = key; childScope.$index = index; childScope.$first = (index === 0); childScope.$last = (index === (arrayLength - 1)); childScope.$middle = !(childScope.$first || childScope.$last); // jshint bitwise: false childScope.$odd = !(childScope.$even = (index&1) === 0); // jshint bitwise: true if (!block.scope) { $transclude(childScope, function(clone) { clone[clone.length++] = document.createComment(' end ngRepeat: ' + expression + ' '); $animate.enter(clone, null, jqLite(previousNode)); previousNode = clone; block.scope = childScope; // Note: We only need the first/last node of the cloned nodes. // However, we need to keep the reference to the jqlite wrapper as it might be changed later // by a directive with templateUrl when its template arrives. block.clone = clone; nextBlockMap[block.id] = block; }); } } lastBlockMap = nextBlockMap; }); } }; function getBlockStart(block) { return block.clone[0]; } function getBlockEnd(block) { return block.clone[block.clone.length - 1]; } }]; /** * @ngdoc directive * @name ngShow * * @description * The `ngShow` directive shows or hides the given HTML element based on the expression * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined * in AngularJS and sets the display style to none (using an !important flag). * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). * * ```html * *
    * * *
    * ``` * * When the `ngShow` expression evaluates to false then the `.ng-hide` CSS class is added to the class attribute * on the element causing it to become hidden. When true, the `.ng-hide` CSS class is removed * from the element causing the element not to appear hidden. * *
    * **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):
    * "f" / "0" / "false" / "no" / "n" / "[]" *
    * * ## Why is !important used? * * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector * can be easily overridden by heavier selectors. For example, something as simple * as changing the display style on a HTML list item would make hidden elements appear visible. * This also becomes a bigger issue when dealing with CSS frameworks. * * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. * * ### Overriding `.ng-hide` * * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` * class in CSS: * * ```css * .ng-hide { * //this is just another form of hiding an element * display:block!important; * position:absolute; * top:-9999px; * left:-9999px; * } * ``` * * By default you don't need to override in CSS anything and the animations will work around the display style. * * ## A note about animations with `ngShow` * * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression * is true and false. This system works like the animation system present with ngClass except that * you must also include the !important flag to override the display property * so that you can perform an animation when the element is hidden during the time of the animation. * * ```css * // * //a working example can be found at the bottom of this page * // * .my-element.ng-hide-add, .my-element.ng-hide-remove { * transition:0.5s linear all; * } * * .my-element.ng-hide-add { ... } * .my-element.ng-hide-add.ng-hide-add-active { ... } * .my-element.ng-hide-remove { ... } * .my-element.ng-hide-remove.ng-hide-remove-active { ... } * ``` * * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display * property to block during animation states--ngAnimate will handle the style toggling automatically for you. * * @animations * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden * * @element ANY * @param {expression} ngShow If the {@link guide/expression expression} is truthy * then the element is shown or hidden respectively. * * @example Click me:
    Show:
    I show up when your checkbox is checked.
    Hide:
    I hide when your checkbox is checked.
    @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); .animate-show { -webkit-transition:all linear 0.5s; transition:all linear 0.5s; line-height:20px; opacity:1; padding:10px; border:1px solid black; background:white; } .animate-show.ng-hide { line-height:0; opacity:0; padding:0 10px; } .check-element { padding:10px; border:1px solid black; background:white; } var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); it('should check ng-show / ng-hide', function() { expect(thumbsUp.isDisplayed()).toBeFalsy(); expect(thumbsDown.isDisplayed()).toBeTruthy(); element(by.model('checked')).click(); expect(thumbsUp.isDisplayed()).toBeTruthy(); expect(thumbsDown.isDisplayed()).toBeFalsy(); });
    */ var ngShowDirective = ['$animate', function($animate) { return function(scope, element, attr) { scope.$watch(attr.ngShow, function ngShowWatchAction(value){ $animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide'); }); }; }]; /** * @ngdoc directive * @name ngHide * * @description * The `ngHide` directive shows or hides the given HTML element based on the expression * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined * in AngularJS and sets the display style to none (using an !important flag). * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}). * * ```html * *
    * * *
    * ``` * * When the `.ngHide` expression evaluates to true then the `.ng-hide` CSS class is added to the class attribute * on the element causing it to become hidden. When false, the `.ng-hide` CSS class is removed * from the element causing the element not to appear hidden. * *
    * **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):
    * "f" / "0" / "false" / "no" / "n" / "[]" *
    * * ## Why is !important used? * * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector * can be easily overridden by heavier selectors. For example, something as simple * as changing the display style on a HTML list item would make hidden elements appear visible. * This also becomes a bigger issue when dealing with CSS frameworks. * * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the * styling to change how to hide an element then it is just a matter of using !important in their own CSS code. * * ### Overriding `.ng-hide` * * By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide` * class in CSS: * * ```css * .ng-hide { * //this is just another form of hiding an element * display:block!important; * position:absolute; * top:-9999px; * left:-9999px; * } * ``` * * By default you don't need to override in CSS anything and the animations will work around the display style. * * ## A note about animations with `ngHide` * * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide` * CSS class is added and removed for you instead of your own CSS class. * * ```css * // * //a working example can be found at the bottom of this page * // * .my-element.ng-hide-add, .my-element.ng-hide-remove { * transition:0.5s linear all; * } * * .my-element.ng-hide-add { ... } * .my-element.ng-hide-add.ng-hide-add-active { ... } * .my-element.ng-hide-remove { ... } * .my-element.ng-hide-remove.ng-hide-remove-active { ... } * ``` * * Keep in mind that, as of AngularJS version 1.2.17 (and 1.3.0-beta.11), there is no need to change the display * property to block during animation states--ngAnimate will handle the style toggling automatically for you. * * @animations * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible * * @element ANY * @param {expression} ngHide If the {@link guide/expression expression} is truthy then * the element is shown or hidden respectively. * * @example Click me:
    Show:
    I show up when your checkbox is checked.
    Hide:
    I hide when your checkbox is checked.
    @import url(//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap-glyphicons.css); .animate-hide { -webkit-transition:all linear 0.5s; transition:all linear 0.5s; line-height:20px; opacity:1; padding:10px; border:1px solid black; background:white; } .animate-hide.ng-hide { line-height:0; opacity:0; padding:0 10px; } .check-element { padding:10px; border:1px solid black; background:white; } var thumbsUp = element(by.css('span.glyphicon-thumbs-up')); var thumbsDown = element(by.css('span.glyphicon-thumbs-down')); it('should check ng-show / ng-hide', function() { expect(thumbsUp.isDisplayed()).toBeFalsy(); expect(thumbsDown.isDisplayed()).toBeTruthy(); element(by.model('checked')).click(); expect(thumbsUp.isDisplayed()).toBeTruthy(); expect(thumbsDown.isDisplayed()).toBeFalsy(); });
    */ var ngHideDirective = ['$animate', function($animate) { return function(scope, element, attr) { scope.$watch(attr.ngHide, function ngHideWatchAction(value){ $animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide'); }); }; }]; /** * @ngdoc directive * @name ngStyle * @restrict AC * * @description * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. * * @element ANY * @param {expression} ngStyle * * {@link guide/expression Expression} which evals to an * object whose keys are CSS style names and values are corresponding values for those CSS * keys. * * Since some CSS style names are not valid keys for an object, they must be quoted. * See the 'background-color' style in the example below. * * @example
    Sample Text
    myStyle={{myStyle}}
    span { color: black; } var colorSpan = element(by.css('span')); it('should check ng-style', function() { expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); element(by.css('input[value=\'set color\']')).click(); expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)'); element(by.css('input[value=clear]')).click(); expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)'); });
    */ var ngStyleDirective = ngDirective(function(scope, element, attr) { scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { if (oldStyles && (newStyles !== oldStyles)) { forEach(oldStyles, function(val, style) { element.css(style, '');}); } if (newStyles) element.css(newStyles); }, true); }); /** * @ngdoc directive * @name ngSwitch * @restrict EA * * @description * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression. * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location * as specified in the template. * * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element * matches the value obtained from the evaluated expression. In other words, you define a container element * (where you place the directive), place an expression on the **`on="..."` attribute** * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default * attribute is displayed. * *
    * Be aware that the attribute values to match against cannot be expressions. They are interpreted * as literal string values to match against. * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the * value of the expression `$scope.someVal`. *
    * @animations * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM * * @usage * * ``` * * ... * ... * ... * * ``` * * * @scope * @priority 800 * @param {*} ngSwitch|on expression to match against ng-switch-when. * On child elements add: * * * `ngSwitchWhen`: the case statement to match against. If match then this * case will be displayed. If the same match appears multiple times, all the * elements will be displayed. * * `ngSwitchDefault`: the default case when no other case match. If there * are multiple default cases, all of them will be displayed when no other * case match. * * * @example
    selection={{selection}}
    Settings Div
    Home Span
    default
    angular.module('switchExample', ['ngAnimate']) .controller('ExampleController', ['$scope', function($scope) { $scope.items = ['settings', 'home', 'other']; $scope.selection = $scope.items[0]; }]); .animate-switch-container { position:relative; background:white; border:1px solid black; height:40px; overflow:hidden; } .animate-switch { padding:10px; } .animate-switch.ng-animate { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; position:absolute; top:0; left:0; right:0; bottom:0; } .animate-switch.ng-leave.ng-leave-active, .animate-switch.ng-enter { top:-50px; } .animate-switch.ng-leave, .animate-switch.ng-enter.ng-enter-active { top:0; } var switchElem = element(by.css('[ng-switch]')); var select = element(by.model('selection')); it('should start in settings', function() { expect(switchElem.getText()).toMatch(/Settings Div/); }); it('should change to home', function() { select.all(by.css('option')).get(1).click(); expect(switchElem.getText()).toMatch(/Home Span/); }); it('should select default', function() { select.all(by.css('option')).get(2).click(); expect(switchElem.getText()).toMatch(/default/); });
    */ var ngSwitchDirective = ['$animate', function($animate) { return { restrict: 'EA', require: 'ngSwitch', // asks for $scope to fool the BC controller module controller: ['$scope', function ngSwitchController() { this.cases = {}; }], link: function(scope, element, attr, ngSwitchController) { var watchExpr = attr.ngSwitch || attr.on, selectedTranscludes = [], selectedElements = [], previousElements = [], selectedScopes = []; scope.$watch(watchExpr, function ngSwitchWatchAction(value) { var i, ii; for (i = 0, ii = previousElements.length; i < ii; ++i) { previousElements[i].remove(); } previousElements.length = 0; for (i = 0, ii = selectedScopes.length; i < ii; ++i) { var selected = selectedElements[i]; selectedScopes[i].$destroy(); previousElements[i] = selected; $animate.leave(selected, function() { previousElements.splice(i, 1); }); } selectedElements.length = 0; selectedScopes.length = 0; if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) { scope.$eval(attr.change); forEach(selectedTranscludes, function(selectedTransclude) { var selectedScope = scope.$new(); selectedScopes.push(selectedScope); selectedTransclude.transclude(selectedScope, function(caseElement) { var anchor = selectedTransclude.element; selectedElements.push(caseElement); $animate.enter(caseElement, anchor.parent(), anchor); }); }); } }); } }; }]; var ngSwitchWhenDirective = ngDirective({ transclude: 'element', priority: 800, require: '^ngSwitch', link: function(scope, element, attrs, ctrl, $transclude) { ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []); ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element }); } }); var ngSwitchDefaultDirective = ngDirective({ transclude: 'element', priority: 800, require: '^ngSwitch', link: function(scope, element, attr, ctrl, $transclude) { ctrl.cases['?'] = (ctrl.cases['?'] || []); ctrl.cases['?'].push({ transclude: $transclude, element: element }); } }); /** * @ngdoc directive * @name ngTransclude * @restrict AC * * @description * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion. * * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted. * * @element ANY * * @example


    {{text}}
    it('should have transcluded', function() { var titleElement = element(by.model('title')); titleElement.clear(); titleElement.sendKeys('TITLE'); var textElement = element(by.model('text')); textElement.clear(); textElement.sendKeys('TEXT'); expect(element(by.binding('title')).getText()).toEqual('TITLE'); expect(element(by.binding('text')).getText()).toEqual('TEXT'); });
    * */ var ngTranscludeDirective = ngDirective({ link: function($scope, $element, $attrs, controller, $transclude) { if (!$transclude) { throw minErr('ngTransclude')('orphan', 'Illegal use of ngTransclude directive in the template! ' + 'No parent directive that requires a transclusion found. ' + 'Element: {0}', startingTag($element)); } $transclude(function(clone) { $element.empty(); $element.append(clone); }); } }); /** * @ngdoc directive * @name script * @restrict E * * @description * Load the content of a ` Load inlined template
    it('should load template defined inside script tag', function() { element(by.css('#tpl-link')).click(); expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/); }); */ var scriptDirective = ['$templateCache', function($templateCache) { return { restrict: 'E', terminal: true, compile: function(element, attr) { if (attr.type == 'text/ng-template') { var templateUrl = attr.id, text = element[0].text; $templateCache.put(templateUrl, text); } } }; }]; var ngOptionsMinErr = minErr('ngOptions'); /** * @ngdoc directive * @name select * @restrict E * * @description * HTML `SELECT` element with angular data-binding. * * # `ngOptions` * * The `ngOptions` attribute can be used to dynamically generate a list of `` * DOM element. * * `trackexpr`: Used when working with an array of objects. The result of this expression will be * used to identify the objects in the array. The `trackexpr` will most likely refer to the * `value` variable (e.g. `value.propertyName`). * * @example

    Color (null not allowed):
    Color (null allowed):
    Color grouped by shade:
    Select bogus.

    Currently selected: {{ {selected_color:myColor} }}
    it('should check ng-options', function() { expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red'); element.all(by.model('myColor')).first().click(); element.all(by.css('select[ng-model="myColor"] option')).first().click(); expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black'); element(by.css('.nullable select[ng-model="myColor"]')).click(); element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click(); expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null'); });
    */ var ngOptionsDirective = valueFn({ terminal: true }); // jshint maxlen: false var selectDirective = ['$compile', '$parse', function($compile, $parse) { //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/, nullModelCtrl = {$setViewValue: noop}; // jshint maxlen: 100 return { restrict: 'E', require: ['select', '?ngModel'], controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { var self = this, optionsMap = {}, ngModelCtrl = nullModelCtrl, nullOption, unknownOption; self.databound = $attrs.ngModel; self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { ngModelCtrl = ngModelCtrl_; nullOption = nullOption_; unknownOption = unknownOption_; }; self.addOption = function(value) { assertNotHasOwnProperty(value, '"option value"'); optionsMap[value] = true; if (ngModelCtrl.$viewValue == value) { $element.val(value); if (unknownOption.parent()) unknownOption.remove(); } }; self.removeOption = function(value) { if (this.hasOption(value)) { delete optionsMap[value]; if (ngModelCtrl.$viewValue == value) { this.renderUnknownOption(value); } } }; self.renderUnknownOption = function(val) { var unknownVal = '? ' + hashKey(val) + ' ?'; unknownOption.val(unknownVal); $element.prepend(unknownOption); $element.val(unknownVal); unknownOption.prop('selected', true); // needed for IE }; self.hasOption = function(value) { return optionsMap.hasOwnProperty(value); }; $scope.$on('$destroy', function() { // disable unknown option so that we don't do work when the whole select is being destroyed self.renderUnknownOption = noop; }); }], link: function(scope, element, attr, ctrls) { // if ngModel is not defined, we don't need to do anything if (!ctrls[1]) return; var selectCtrl = ctrls[0], ngModelCtrl = ctrls[1], multiple = attr.multiple, optionsExp = attr.ngOptions, nullOption = false, // if false, user will not be able to select it (used by ngOptions) emptyOption, // we can't just jqLite('