diff --git a/spec/fixtures/ignore.git/HEAD b/spec/fixtures/ignore.git/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/spec/fixtures/ignore.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/ignore.git/config b/spec/fixtures/ignore.git/config new file mode 100644 index 0000000..af10792 --- /dev/null +++ b/spec/fixtures/ignore.git/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/spec/fixtures/ignore.git/index b/spec/fixtures/ignore.git/index new file mode 100644 index 0000000..bf35b18 Binary files /dev/null and b/spec/fixtures/ignore.git/index differ diff --git a/spec/fixtures/ignore.git/info/exclude b/spec/fixtures/ignore.git/info/exclude new file mode 100644 index 0000000..eaa5fa8 --- /dev/null +++ b/spec/fixtures/ignore.git/info/exclude @@ -0,0 +1 @@ +a.txt diff --git a/spec/fixtures/ignore.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 b/spec/fixtures/ignore.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 new file mode 100644 index 0000000..ba1f06f Binary files /dev/null and b/spec/fixtures/ignore.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 differ diff --git a/spec/fixtures/ignore.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/spec/fixtures/ignore.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000..7112238 Binary files /dev/null and b/spec/fixtures/ignore.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/spec/fixtures/ignore.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 b/spec/fixtures/ignore.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 new file mode 100644 index 0000000..eaf6eef Binary files /dev/null and b/spec/fixtures/ignore.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 differ diff --git a/spec/fixtures/ignore.git/refs/heads/master b/spec/fixtures/ignore.git/refs/heads/master new file mode 100644 index 0000000..6134b57 --- /dev/null +++ b/spec/fixtures/ignore.git/refs/heads/master @@ -0,0 +1 @@ +ef046e9eecaa5255ea5e9817132d4001724d6ae1 diff --git a/spec/fixtures/master.git/HEAD b/spec/fixtures/master.git/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/spec/fixtures/master.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/master.git/config b/spec/fixtures/master.git/config new file mode 100644 index 0000000..af10792 --- /dev/null +++ b/spec/fixtures/master.git/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/spec/fixtures/master.git/index b/spec/fixtures/master.git/index new file mode 100644 index 0000000..bf35b18 Binary files /dev/null and b/spec/fixtures/master.git/index differ diff --git a/spec/fixtures/master.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 b/spec/fixtures/master.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 new file mode 100644 index 0000000..ba1f06f Binary files /dev/null and b/spec/fixtures/master.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 differ diff --git a/spec/fixtures/master.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/spec/fixtures/master.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000..7112238 Binary files /dev/null and b/spec/fixtures/master.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/spec/fixtures/master.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 b/spec/fixtures/master.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 new file mode 100644 index 0000000..eaf6eef Binary files /dev/null and b/spec/fixtures/master.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 differ diff --git a/spec/fixtures/master.git/refs/heads/master b/spec/fixtures/master.git/refs/heads/master new file mode 100644 index 0000000..6134b57 --- /dev/null +++ b/spec/fixtures/master.git/refs/heads/master @@ -0,0 +1 @@ +ef046e9eecaa5255ea5e9817132d4001724d6ae1 diff --git a/spec/fixtures/repo-with-submodules/.gitmodules b/spec/fixtures/repo-with-submodules/.gitmodules new file mode 100644 index 0000000..40f4e79 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/.gitmodules @@ -0,0 +1,6 @@ +[submodule "jstips"] + path = jstips + url = https://github.com/loverajoel/jstips +[submodule "You-Dont-Need-jQuery"] + path = You-Dont-Need-jQuery + url = https://github.com/oneuijs/You-Dont-Need-jQuery diff --git a/spec/fixtures/repo-with-submodules/README b/spec/fixtures/repo-with-submodules/README new file mode 100644 index 0000000..e69de29 diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.babelrc b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.babelrc new file mode 100644 index 0000000..e2472af --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.babelrc @@ -0,0 +1,3 @@ +{ + presets: ["es2015", "stage-0"] +} diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.eslintrc b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.eslintrc new file mode 100644 index 0000000..46faa7c --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.eslintrc @@ -0,0 +1,18 @@ +{ + "extends": "eslint-config-airbnb", + "env": { + "browser": true, + "mocha": true, + "node": true + }, + "rules": { + "valid-jsdoc": 2, + "no-param-reassign": 0, + "comma-dangle": 0, + "one-var": 0, + "no-else-return": 1, + "no-unused-expressions": 0, + "indent": 1, + "eol-last": 0 + } +} diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.gitignore b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.gitignore new file mode 100644 index 0000000..539682c --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +*.log +node_modules +coverage +logs diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.travis.yml b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.travis.yml new file mode 100644 index 0000000..69cf167 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: + - "5" + - "4" +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start +script: + - npm run lint + - npm test diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/LICENSE b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/LICENSE new file mode 100644 index 0000000..df31759 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 oneuijs + +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. + diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-es.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-es.md new file mode 100644 index 0000000..6521242 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-es.md @@ -0,0 +1,637 @@ + +> #### You Don't Need jQuery + +Tú no necesitas jQuery +--- +El desarrollo Frontend evoluciona día a día, y los navegadores modernos ya han implementado nativamente APIs para trabajar con DOM/BOM, las cuales son muy buenas, por lo que definitivamente no es necesario aprender jQuery desde cero para manipular el DOM. En la actualidad, gracias al surgimiento de librerías frontend como React, Angular y Vue, manipular el DOM es contrario a los patrones establecidos, y jQuery se ha vuelto menos importante. Este proyecto resume la mayoría de métodos alternativos a jQuery, pero de forma nativa con soporte IE 10+. + +## Tabla de Contenidos + +1. [Query Selector](#query-selector) +1. [CSS & Estilo](#css--estilo) +1. [Manipulación DOM](#manipulación-dom) +1. [Ajax](#ajax) +1. [Eventos](#eventos) +1. [Utilidades](#utilidades) +1. [Traducción](#traducción) +1. [Soporte de Navegadores](#soporte-de-navegadores) + + +## Query Selector + +En lugar de los selectores comunes como clase, id o atributos podemos usar `document.querySelector` o `document.querySelectorAll` como alternativas. Las diferencias radican en: +* `document.querySelector` devuelve el primer elemento que cumpla con la condición +* `document.querySelectorAll` devuelve todos los elementos que cumplen con la condición en forma de NodeList. Puede ser convertido a Array usando `[].slice.call(document.querySelectorAll(selector) || []);` +* Si ningún elemento cumple con la condición, jQuery retornaría `[]` mientras la API DOM retornaría `null`. Nótese el NullPointerException. Se puede usar `||` para establecer el valor por defecto al no encontrar elementos, como en `document.querySelectorAll(selector) || []` + +> Notice: `document.querySelector` and `document.querySelectorAll` are quite **SLOW**, try to use `getElementById`, `document.getElementsByClassName` o `document.getElementsByTagName` if you want to Obtener a performance bonus. + +- [1.0](#1.0) Buscar por selector + + ```js + // jQuery + $('selector'); + + // Nativo + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Buscar por Clase + + ```js + // jQuery + $('.class'); + + // Nativo + document.querySelectorAll('.class'); + + // Forma alternativa + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Buscar por id + + ```js + // jQuery + $('#id'); + + // Nativo + document.querySelector('#id'); + + // Forma alternativa + document.getElementById('id'); + ``` + +- [1.3](#1.3) Buscar por atributo + + ```js + // jQuery + $('a[target=_blank]'); + + // Nativo + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Buscar + + + Buscar nodos + + ```js + // jQuery + $el.find('li'); + + // Nativo + el.querySelectorAll('li'); + ``` + + + Buscar "body" + + ```js + // jQuery + $('body'); + + // Nativo + document.body; + ``` + + + Buscar Atributo + + ```js + // jQuery + $el.attr('foo'); + + // Nativo + e.getAttribute('foo'); + ``` + + + Buscar atributo "data" + + ```js + // jQuery + $el.data('foo'); + + // Nativo + // Usando getAttribute + el.getAttribute('data-foo'); + // También puedes utilizar `dataset` desde IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Elementos Hermanos/Previos/Siguientes + + + Elementos hermanos + + ```js + // jQuery + $el.siblings(); + + // Nativo + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Elementos previos + + ```js + // jQuery + $el.prev(); + + // Nativo + el.previousElementSibling; + ``` + + + Elementos siguientes + + ```js + // jQuery + $el.next(); + + // Nativo + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Retorna el elemento más cercano que coincida con la condición, partiendo desde el nodo actual hasta document. + + ```js + // jQuery + $el.closest(queryString); + + // Nativo + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + Obtiene los ancestros de cada elemento en el set actual de elementos que cumplan con la condición, sin incluir el actual + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Nativo + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // Partir desde el elemento padre + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Formularios + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Nativo + document.querySelector('#my-input').value; + ``` + + + Obtener el índice de e.currentTarget en `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Nativo + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Contenidos de Iframe + + `$('iframe').contents()` devuelve `contentDocument` para este iframe específico + + + Contenidos de Iframe + + ```js + // jQuery + $iframe.contents(); + + // Nativo + iframe.contentDocument; + ``` + + + Buscar dentro de un Iframe + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Nativo + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## CSS & Estilo + +- [2.1](#2.1) CSS + + + Obtener Estilo + + ```js + // jQuery + $el.css("color"); + + // Nativo + // NOTA: Bug conocido, retornará 'auto' si el valor de estilo es 'auto' + const win = el.ownerDocument.defaultView; + // null significa que no tiene pseudo estilos + win.getComputedStyle(el, null).color; + ``` + + + Establecer style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Nativo + el.style.color = '#ff0011'; + ``` + + + Obtener/Establecer Estilos + + Nótese que si se desea establecer múltiples estilos a la vez, se puede utilizar el método [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) en el paquete oui-dom-utils. + + + Agregar clase + + ```js + // jQuery + $el.addClass(className); + + // Nativo + el.classList.add(className); + ``` + + + Quitar Clase + + ```js + // jQuery + $el.removeClass(className); + + // Nativo + el.classList.remove(className); + ``` + + + Consultar si tiene clase + + ```js + // jQuery + $el.hasClass(className); + + // Nativo + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Nativo + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Ancho y Alto son teóricamente idénticos. Usaremos el Alto como ejemplo: + + + Alto de Ventana + + ```js + // alto de ventana + $(window).height(); + // Sin scrollbar, se comporta como jQuery + window.document.documentElement.clientHeight; + // Con scrollbar + window.innerHeight; + ``` + + + Alto de Documento + + ```js + // jQuery + $(document).height(); + + // Nativo + document.documentElement.scrollHeight; + ``` + + + Alto de Elemento + + ```js + // jQuery + $el.height(); + + // Nativo + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // Precisión de integer(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.clientHeight; + // Precisión de decimal(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Posición & Offset + + + Posición + + ```js + // jQuery + $el.position(); + + // Nativo + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Nativo + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Posición del Scroll Vertical + + ```js + // jQuery + $(window).scrollTop(); + + // Nativo + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## Manipulación DOM + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Nativo + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Obtener Texto + + ```js + // jQuery + $el.text(); + + // Nativo + el.textContent; + ``` + + + Establecer Texto + + ```js + // jQuery + $el.text(string); + + // Nativo + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Obtener HTML + + ```js + // jQuery + $el.html(); + + // Nativo + el.innerHTML; + ``` + + + Establecer HTML + + ```js + // jQuery + $el.html(htmlString); + + // Nativo + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Añadir elemento hijo después del último hijo del elemento padre + + ```js + // jQuery + $el.append("
hello
"); + + // Nativo + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) Prepend + + Añadir elemento hijo después del último hijo del elemento padre + + ```js + // jQuery + $el.prepend("
hello
"); + + // Nativo + el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) insertBefore + + Insertar un nuevo nodo antes del primero de los elementos seleccionados + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Insertar un nuevo nodo después de los elementos seleccionados + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## Ajax + +Reemplazar con [fetch](https://github.com/camsong/fetch-ie8) y [fetch-jsonp](https://github.com/camsong/fetch-jsonp) ++[Fetch API](https://fetch.spec.whatwg.org/) es el nuevo estándar quue reemplaza a XMLHttpRequest para efectuar peticiones AJAX. Funciona en Chrome y Firefox, como también es posible usar un polyfill en otros navegadores. ++ ++Es una buena alternativa utilizar [github/fetch](http://github.com/github/fetch) en IE9+ o [fetch-ie8](https://github.com/camsong/fetch-ie8/) en IE8+, [fetch-jsonp](https://github.com/camsong/fetch-jsonp) para efectuar peticiones JSONP. +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## Eventos + +Para un reemplazo completo con namespace y delegación, utilizar https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Asignar un evento con "on" + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Nativo + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Desasignar un evento con "off" + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Nativo + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Nativo + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## Utilidades + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Nativo + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Nativo + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + Utilizar polyfill para object.assign https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Nativo + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Nativo + el !== child && el.contains(child); + ``` + +**[⬆ volver al inicio](#tabla-de-contenidos)** + +## Traducción + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Soporte de Navegadores + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Última ✔ | Última ✔ | 10+ ✔ | Última ✔ | 6.1+ ✔ | + +# Licencia + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-id.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-id.md new file mode 100644 index 0000000..13ca75b --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-id.md @@ -0,0 +1,634 @@ +## Anda tidak memerlukan jQuery + +Dewasa ini perkembangan environment frontend sangatlah pesat, dimana banyak browser sudah mengimplementasikan DOM/BOM APIs dengan baik. Kita tidak perlu lagi belajar jQuery dari nol untuk keperluan manipulasi DOM atau events. Disaat yang sama; dengan berterimakasih kepada library frontend terkini seperti React, Angular dan Vue; Memanipulasi DOM secara langsung telah menjadi anti-pattern alias sesuatu yang tidak perlu dilakukan. Dengan kata lain, jQuery sekarang menjadi semakin tidak diperlukan. Projek ini memberikan informasi mengenai metode alternatif dari jQuery untuk implementasi Native dengan support untuk browser IE 10+. + + +## Daftar Isi + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css-style) +1. [DOM Manipulation](#dom-manipulation) +1. [Ajax](#ajax) +1. [Events](#events) +1. [Utilities](#utilities) +1. [Translation](#translation) +1. [Browser Support](#browser-yang-di-support) + +## Query Selector + +Untuk selector-selector umum seperti class, id atau attribute, kita dapat menggunakan `document.querySelector` atau `document.querySelectorAll` sebagai pengganti. Perbedaan diantaranya adalah: +* `document.querySelector` mengembalikan elemen pertama yang cocok +* `document.querySelectorAll` mengembalikan semua elemen yang cocok sebagai NodeList. Hasilnya bisa dikonversikan menjadi Array `[].slice.call(document.querySelectorAll(selector) || []);` +* Bila tidak ada hasil pengembalian elemen yang cocok, jQuery akan mengembalikan `[]` sedangkan DOM API akan mengembalikan `null`. Mohon diperhatikan mengenai Null Pointer Exception. Anda juga bisa menggunakan operator `||` untuk set nilai awal jika hasil pencarian tidak ditemukan : `document.querySelectorAll(selector) || []` + +> Perhatian: `document.querySelector` dan `document.querySelectorAll` sedikit **LAMBAT**. Silahkan menggunakan `getElementById`, `document.getElementsByClassName` atau `document.getElementsByTagName` jika anda menginginkan tambahan performa. + +- [1.0](#1.0) Query by selector + + ```js + // jQuery + $('selector'); + + // Native + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query by class + + ```js + // jQuery + $('.class'); + + // Native + document.querySelectorAll('.class'); + + // or + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Query by id + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + + // or + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query menggunakan attribute + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Pencarian. + + + Mencari nodes + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + + + Mencari body + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + + + Mencari Attribute + + ```js + // jQuery + $el.attr('foo'); + + // Native + e.getAttribute('foo'); + ``` + + + Mencari data attribute + + ```js + // jQuery + $el.data('foo'); + + // Native + // gunakan getAttribute + el.getAttribute('data-foo'); + // anda juga bisa menggunakan `dataset` bila anda perlu support IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Elemen-elemen Sibling/Previous/Next + + + Elemen Sibling + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Elemen Previous + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + + ``` + + + Elemen Next + + ```js + // next + $el.next(); + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Mengembalikan elemen pertama yang cocok dari selector yang digunakan, dengan cara mencari mulai dari elemen-sekarang sampai ke document. + + ```js + // jQuery + $el.closest(queryString); + + // Native + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + Digunakan untuk mendapatkan "ancestor" dari setiap elemen yang ditemukan. Namun tidak termasuk elemen-sekarang yang didapat dari pencarian oleh selector, DOM node, atau object jQuery. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Get index of e.currentTarget between `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()` mengembalikan `contentDocument` + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ back to top](#daftar-isi)** + +## CSS Style + +- [2.1](#2.1) CSS + + + Get style + + ```js + // jQuery + $el.css("color"); + + // Native + // PERHATIAN: ada bug disini, dimana fungsi ini akan mengembalikan nilai 'auto' bila nilai dari atribut style adalah 'auto' + const win = el.ownerDocument.defaultView; + // null artinya tidak mengembalikan pseudo styles + win.getComputedStyle(el, null).color; + ``` + + + Set style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Get/Set Styles + + Mohon dicatat jika anda ingin men-set banyak style bersamaan, anda dapat menemukan referensi di metode [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) pada package oui-dom-utils + + + Add class + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + Remove class + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + has class + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Secara teori, width dan height identik, contohnya Height: + + + Window height + + ```js + // window height + $(window).height(); + // without scrollbar, behaves like jQuery + window.document.documentElement.clientHeight; + // with scrollbar + window.innerHeight; + ``` + + + Document height + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Element height + + ```js + // jQuery + $el.height(); + + // Native + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // accurate to integer(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.clientHeight; + // accurate to decimal(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ back to top](#daftar-isi)** + +## DOM Manipulation + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Get text + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Get HTML + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + Set HTML + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Menambahkan elemen-anak setelah anak terakhir dari elemen-parent + + ```js + // jQuery + $el.append("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.appendChild(newEl); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.insertBefore(newEl, el.firstChild); + ``` + +- [3.6](#3.6) insertBefore + + Memasukkan node baru sebelum elemen yang dipilih. + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Memasukkan node baru sesudah elemen yang dipilih. + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ back to top](#daftar-isi)** + +## Ajax + +Gantikan dengan [fetch](https://github.com/camsong/fetch-ie8) dan [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ back to top](#daftar-isi)** + +## Events + +Untuk penggantian secara menyeluruh dengan namespace dan delegation, rujuk ke https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind event dengan menggunakan on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind event dengan menggunakan off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ back to top](#daftar-isi)** + +## Utilities + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Native + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Native + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + Extend, use object.assign polyfill https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +**[⬆ back to top](#daftar-isi)** + +## Terjemahan + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Browser yang di Support + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# License + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-it.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-it.md new file mode 100644 index 0000000..2af8920 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-it.md @@ -0,0 +1,651 @@ +## Non hai bisogno di jQuery + +Il mondo del Frontend si evolve rapidamente oggigiorno, i browsers moderni hanno gia' implementato un'ampia gamma di DOM/BOM API soddisfacenti. Non dobbiamo imparare jQuery dalle fondamenta per la manipolazione del DOM o di eventi. Nel frattempo, grazie al prevalicare di librerie per il frontend come React, Angular a Vue, manipolare il DOM direttamente diventa un anti-pattern, di consequenza jQuery non e' mai stato meno importante. Questo progetto sommarizza la maggior parte dei metodi e implementazioni alternative a jQuery, con il supporto di IE 10+. + +## Tabella contenuti + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [Manipolazione DOM](#manipolazione-dom) +1. [Ajax](#ajax) +1. [Eventi](#eventi) +1. [Utilities](#utilities) +1. [Alternative](#alternative) +1. [Traduzioni](#traduzioni) +1. [Supporto Browsers](#supporto-browsers) + +## Query Selector + +Al posto di comuni selettori come class, id o attributi possiamo usare `document.querySelector` o `document.querySelectorAll` per sostituzioni. La differenza risiede in: +* `document.querySelector` restituisce il primo elemento combiaciante +* `document.querySelectorAll` restituisce tutti gli elementi combiacianti della NodeList. Puo' essere convertito in Array usando `[].slice.call(document.querySelectorAll(selector) || []);` +* Se nessun elemento combiacia, jQuery restituitirebbe `[]` li' dove il DOM API ritornera' `null`. Prestate attenzione al Null Pointer Exception. Potete anche usare `||` per settare valori di default se non trovato, come `document.querySelectorAll(selector) || []` + +> Notare: `document.querySelector` e `document.querySelectorAll` sono abbastanza **SLOW**, provate ad usare `getElementById`, `document.getElementsByClassName` o `document.getElementsByTagName` se volete avere un bonus in termini di performance. + +- [1.0](#1.0) Query da selettore + + ```js + // jQuery + $('selector'); + + // Nativo + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query da classe + + ```js + // jQuery + $('.class'); + + // Nativo + document.querySelectorAll('.class'); + + // or + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Query da id + + ```js + // jQuery + $('#id'); + + // Nativo + document.querySelector('#id'); + + // o + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query da attributo + + ```js + // jQuery + $('a[target=_blank]'); + + // Nativo + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Trovare qualcosa. + + + Trovare nodes + + ```js + // jQuery + $el.find('li'); + + // Nativo + el.querySelectorAll('li'); + ``` + + + Trovare body + + ```js + // jQuery + $('body'); + + // Nativo + document.body; + ``` + + + Trovare Attributi + + ```js + // jQuery + $el.attr('foo'); + + // Nativo + e.getAttribute('foo'); + ``` + + + Trovare attributo data + + ```js + // jQuery + $el.data('foo'); + + // Nativo + // using getAttribute + el.getAttribute('data-foo'); + // potete usare `dataset` solo se supportate IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Fratelli/Precedento/Successivo Elemento + + + Elementi fratelli + + ```js + // jQuery + $el.siblings(); + + // Nativo + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Elementi precedenti + + ```js + // jQuery + $el.prev(); + + // Nativo + el.previousElementSibling; + ``` + + + Elementi successivi + + ```js + // jQuery + $el.next(); + + // Nativo + el.nextElementSibling; + ``` + +- [1.6](#1.6) Il piu' vicino + + Restituisce il primo elementi combiaciante il selettore fornito, attraversando dall'elemento corrente fino al document . + + ```js + // jQuery + $el.closest(queryString); + + // Nativo - Solo ultimo, NO IE + el.closest(selector); + + // Nativo - IE10+ + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Fino a parenti + + Ottiene il parente di ogni elemento nel set corrente di elementi combiacianti, fino a ma non incluso, l'elemento combiaciante il selettorer, DOM node, o jQuery object. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Nativo + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // il match parte dal parente + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Get index of e.currentTarget between `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Nativo + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()` restituisce `contentDocument` per questo specifico iframe + + + Iframe contenuti + + ```js + // jQuery + $iframe.contents(); + + // Nativo + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Nativo + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ back to top](#table-of-contents)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Ottenere style + + ```js + // jQuery + $el.css("color"); + + // Nativo + // NOTA: Bug conosciuto, restituira' 'auto' se il valore di style e' 'auto' + const win = el.ownerDocument.defaultView; + // null significa che non restituira' lo psuedo style + win.getComputedStyle(el, null).color; + ``` + + + Settare style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Nativo + el.style.color = '#ff0011'; + ``` + + + Ottenere/Settare Styles + + Nota che se volete settare styles multipli in una sola volta, potete riferire [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) metodo in oui-dom-utils package. + + + + Aggiungere classe + + ```js + // jQuery + $el.addClass(className); + + // Nativo + el.classList.add(className); + ``` + + + Rimouvere class + + ```js + // jQuery + $el.removeClass(className); + + // Nativo + el.classList.remove(className); + ``` + + + has class + + ```js + // jQuery + $el.hasClass(className); + + // Nativo + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Nativo + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Width e Height sono teoricamente identici, prendendo Height come esempio: + + + Window height + + ```js + // window height + $(window).height(); + // senza scrollbar, si comporta comporta jQuery + window.document.documentElement.clientHeight; + // con scrollbar + window.innerHeight; + ``` + + + Document height + + ```js + // jQuery + $(document).height(); + + // Nativo + document.documentElement.scrollHeight; + ``` + + + Element height + + ```js + // jQuery + $el.height(); + + // Nativo + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // preciso a intero(quando `border-box`, e' `height`; quando `content-box`, e' `height + padding + border`) + el.clientHeight; + // preciso a decimale(quando `border-box`, e' `height`; quando `content-box`, e' `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Nativo + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Nativo + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Nativo + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ back to top](#table-of-contents)** + +## Manipolazione DOM + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Nativo + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Get text + + ```js + // jQuery + $el.text(); + + // Nativo + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Nativo + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Ottenere HTML + + ```js + // jQuery + $el.html(); + + // Nativo + el.innerHTML; + ``` + + + Settare HTML + + ```js + // jQuery + $el.html(htmlString); + + // Nativo + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + appendere elemento figlio dopo l'ultimo elemento figlio del genitore + + ```js + // jQuery + $el.append("
hello
"); + + // Nativo + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Nativo + el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) insertBefore + + Inserire un nuovo node dopo l'elmento selezionato + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Insert a new node after the selected elements + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +- [3.8](#3.8) is + + Restituisce `true` se combacia con l'elemento selezionato + + ```js + // jQuery - Notare `is` funziona anche con `function` o `elements` non di importanza qui + $el.is(selector); + + // Nativo + el.matches(selector); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Ajax + +Sostituire con [fetch](https://github.com/camsong/fetch-ie8) and [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ back to top](#table-of-contents)** + +## Eventi + +Per una completa sostituzione con namespace e delegation, riferire a https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind un evento con on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Nativo + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind an event with off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Nativo + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Nativo + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Utilities + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Nativo + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Nativo + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + Extend, usa object.assign polyfill https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Nativo + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Nativo + el !== child && el.contains(child); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Alternative + +* [Forse non hai bisogno di jQuery](http://youmightnotneedjquery.com/) - Esempi di come creare eventi comuni, elementi, ajax etc usando puramente javascript. +* [npm-dom](http://github.com/npm-dom) e [webmodules](http://github.com/webmodules) - Organizzazione dove puoi trovare moduli per il DOM individuale su NPM + +## Traduzioni + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Italiano](./README-it.md) +* [Türkçe](./README-tr.md) + +## Supporto Browsers + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Ultimo ✔ | Ultimo ✔ | 10+ ✔ | Ultimo ✔ | 6.1+ ✔ | + +# Licenza + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-my.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-my.md new file mode 100644 index 0000000..1f0a759 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-my.md @@ -0,0 +1,615 @@ +## Anda tidak memerlukan jQuery + +Mutakhir ini perkembangan dalam persekitaran frontend berlaku begitu pesat sekali. Justeru itu kebanyakan pelayar moden telahpun menyediakan API yang memadai untuk pengaksesan DOM/BOM. Kita tak payah lagi belajar jQuery dari asas untuk memanipulasi DOM dan acara-acara. Projek ini menawarkan perlaksanaan alternatif kepada kebanyakan kaedah-kaedah jQuery yang menyokong IE 10+. + +## Isi Kandungan + +1. [Pemilihan elemen](#pemilihan-elemen) +1. [CSS & Penggayaan](#css-penggayaan) +1. [Manipulasi DOM](#manipulasi-dom) +1. [Ajax](#ajax) +1. [Events](#events) +1. [Utiliti](#utiliti) +1. [Terjemahan](#terjemahan) +1. [Browser Support](#browser-support) + +## Pemilihan Elemen + +Pemilihan elemen yang umum seperti class, id atau atribut, biasanya kita boleh pakai `document.querySelector` atau `document.querySelectorAll` sebagai ganti. Bezanya terletak pada +* `document.querySelector` akan mengembalikan elemen pertama sekali yang sepadan dijumpai +* `document.querySelectorAll` akan mengembalikan kesemua elemen yang sepadan dijumpai kedalam sebuah NodeList. Ia boleh ditukar kedalam bentuk array menggunakan `[].slice.call` +* Sekiranya tiada elemen yang sepadan dijumpai, jQuery akan mengembalikan `[]` dimana API DOM pula akan mengembalikan `null`. Sila ambil perhatian pada Null Pointer Exception + +> AWAS: `document.querySelector` dan `document.querySelectorAll` agak **LEMBAB** berbanding `getElementById`, `document.getElementsByClassName` atau `document.getElementsByTagName` jika anda menginginkan bonus dari segi prestasi. + +- [1.1](#1.1) Pemilihan menggunakan class + + ```js + // jQuery + $('.css'); + + // Native + document.querySelectorAll('.css'); + ``` + +- [1.2](#1.2) Pemilihan menggunakan id + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + ``` + +- [1.3](#1.3) Pemilihan menggunakan atribut + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Cari sth. + + + Find nodes + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + + + Cari body + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + + + Cari Attribute + + ```js + // jQuery + $el.attr('foo'); + + // Native + e.getAttribute('foo'); + ``` + + + Cari atribut data + + ```js + // jQuery + $el.data('foo'); + + // Native + // menggunakan getAttribute + el.getAttribute('data-foo'); + // anda boleh juga gunakan `dataset` jika ingin pakai IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Sibling/Previous/Next Elements + + + Sibling elements + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Previous elements + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + + ``` + + + Next elements + + ```js + // next + $el.next(); + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Return the first matched element by provided selector, traversing from current element to document. + + ```js + // jQuery + $el.closest(queryString); + + // Native + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Get index of e.currentTarget between `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()` returns `contentDocument` for this specific iframe + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ back to top](#table-of-contents)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Get style + + ```js + // jQuery + $el.css("color"); + + // Native + // NOTE: Known bug, will return 'auto' if style value is 'auto' + const win = el.ownerDocument.defaultView; + // null means not return presudo styles + win.getComputedStyle(el, null).color; + ``` + + + Set style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Get/Set Styles + + Note that if you want to set multiple styles once, you could refer to [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) method in oui-dom-utils package. + + + + Add class + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + Remove class + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + has class + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Width and Height are theoretically identical, take Height as example: + + + Window height + + ```js + // window height + $(window).height(); + // without scrollbar, behaves like jQuery + window.document.documentElement.clientHeight; + // with scrollbar + window.innerHeight; + ``` + + + Document height + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Element height + + ```js + // jQuery + $el.height(); + + // Native + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // accurate to integer(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.clientHeight; + // accurate to decimal(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ back to top](#table-of-contents)** + +## DOM Manipulation + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Get text + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Get HTML + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + Set HTML + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + append child element after the last child of parent element + + ```js + // jQuery + $el.append("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.appendChild(newEl); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.insertBefore(newEl, el.firstChild); + ``` + +- [3.6](#3.6) insertBefore + + Insert a new node before the selected elements + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Insert a new node after the selected elements + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Ajax + +Replace with [fetch](https://github.com/camsong/fetch-ie8) and [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ back to top](#table-of-contents)** + +## Events + +For a complete replacement with namespace and delegation, refer to https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind an event with on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind an event with off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Utility + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Native + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Native + String.trim(string); + ``` + +- [6.3](#6.3) Object Assign + + Extend, use object.assign polyfill https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Terjemahan + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [English](./README.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Sokongan Pelayar + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# Lesen + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-ru.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-ru.md new file mode 100644 index 0000000..561bdb9 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-ru.md @@ -0,0 +1,650 @@ +## Вам не нужен jQuery + +В наше время среда фронт энд разработки быстро развивается, современные браузеры уже реализовали значимую часть DOM/BOM APIs и это хорошо. Вам не нужно изучать jQuery с нуля для манипуляцией DOM'ом или обектами событий. В то же время, благодаря лидирующим фронт энд библиотекам, таким как React, Angular и Vue, манипуляция DOM'ом напрямую становится противо шаблонной, jQuery никогда не был менее важен. Этот проект суммирует большинство альтернатив методов jQuery в нативном исполнении с поддержкой IE 10+. + +## Содержание + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [Манипуляция DOM](#Манипуляции-dom) +1. [Ajax](#ajax) +1. [События](#События) +1. [Утилиты](#Утилиты) +1. [Альтернативы](#Альтернативы) +1. [Переводы](#Переводы) +1. [Поддержка браузеров](#Поддержка-браузеров) + +## Query Selector +Для часто используемых селекторов, таких как class, id или attribute мы можем использовать `document.querySelector` или `document.querySelectorAll` для замены. Разница такова: +* `document.querySelector` возвращает первый совпавший элемент +* `document.querySelectorAll` возвращает все совспавшие элементы как коллекцию узлов(NodeList). Его можно конвертировать в массив используя `[].slice.call(document.querySelectorAll(selector) || []);` +* Если никакие элементы не совпадут, jQuery вернет `[]` где DOM API вернет `null`. Обратите внимание на указатель исключения Null (Null Pointer Exception). Вы так же можете использовать `||` для установки значения по умолчанию если не было найдемо совпадений `document.querySelectorAll(selector) || []` + +> Заметка: `document.querySelector` и `document.querySelectorAll` достаточно **МЕДЛЕННЫ**, старайтесь использовать `getElementById`, `document.getElementsByClassName` или `document.getElementsByTagName` если хотите улучшить производительность. + +- [1.0](#1.0) Query by selector + + ```js + // jQuery + $('selector'); + + // Нативно + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Запрос по классу + + ```js + // jQuery + $('.class'); + + // Нативно + document.querySelectorAll('.class'); + + // или + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Запрос по ID + + ```js + // jQuery + $('#id'); + + // Нативно + document.querySelector('#id'); + + // или + document.getElementById('id'); + ``` + +- [1.3](#1.3) Запрос по атрибуту + + ```js + // jQuery + $('a[target=_blank]'); + + // Нативно + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Найти среди потомков + + + Найти nodes + + ```js + // jQuery + $el.find('li'); + + // Нативно + el.querySelectorAll('li'); + ``` + + + Найти body + + ```js + // jQuery + $('body'); + + // Нативно + document.body; + ``` + + + Найти атрибуты + + ```js + // jQuery + $el.attr('foo'); + + // Нативно + e.getAttribute('foo'); + ``` + + + Найти data attribute + + ```js + // jQuery + $el.data('foo'); + + // Нативно + // используя getAttribute + el.getAttribute('data-foo'); + // также можно использовать `dataset`, если не требуется поддержка ниже IE 11. + el.dataset['foo']; + ``` + +- [1.5](#1.5) Родственные/Предыдущие/Следующие Элементы + + + Родственные элементы + + ```js + // jQuery + $el.siblings(); + + // Нативно + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Предыдущие элементы + + ```js + // jQuery + $el.prev(); + + // Нативно + el.previousElementSibling; + ``` + + + Следующие элементы + + ```js + // jQuery + $el.next(); + + // Нативно + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Возвращает первый совпавший элемент по предоставленному селектору, обоходя от текущего элементы до документа. + + ```js + // jQuery + $el.closest(queryString); + + // Нативно - Only latest, NO IE + el.closest(selector); + + // Нативно - IE10+ + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Родители до + + Получить родителей кажого элемента в текущем сете совпавших элементов, но не включая элемент совпавший с селектором, узел DOM'а, или объект jQuery. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Нативно + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // Совпадать начиная от родителя + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) От + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Нативно + document.querySelector('#my-input').value; + ``` + + + получить индекс e.currentTarget между `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Нативно + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Контент Iframe + + `$('iframe').contents()` возвращает `contentDocument` для именно этого iframe + + + Контент Iframe + + ```js + // jQuery + $iframe.contents(); + + // Нативно + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Нативно + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ Наверх](#Содержание)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Получить стиль + + ```js + // jQuery + $el.css("color"); + + // Нативно + // ЗАМЕТКА: Известная ошика, возвращает 'auto' если значение стиля 'auto' + const win = el.ownerDocument.defaultView; + // null означает не возвращать псевдостили + win.getComputedStyle(el, null).color; + ``` + + + Присвоение style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Нативно + el.style.color = '#ff0011'; + ``` + + + Получение/Присвоение стилей + + Заметьте что если вы хотите присвоить несколько стилей за раз, вы можете сослаться на [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) метод в oui-dom-utils package. + + + + Добавить класс + + ```js + // jQuery + $el.addClass(className); + + // Нативно + el.classList.add(className); + ``` + + + Удалить class + + ```js + // jQuery + $el.removeClass(className); + + // Нативно + el.classList.remove(className); + ``` + + + Имеет класс + + ```js + // jQuery + $el.hasClass(className); + + // Нативно + el.classList.contains(className); + ``` + + + Переключать класс + + ```js + // jQuery + $el.toggleClass(className); + + // Нативно + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Ширина и Высота + + Ширина и высота теоритечески идентичны, например возьмем высоту: + + + высота окна + + ```js + // Высота окна + $(window).height(); + // без скроллбара, ведет себя как jQuery + window.document.documentElement.clientHeight; + // вместе с скроллбаром + window.innerHeight; + ``` + + + высота документа + + ```js + // jQuery + $(document).height(); + + // Нативно + document.documentElement.scrollHeight; + ``` + + + Высота элемента + + ```js + // jQuery + $el.height(); + + // Нативно + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // С точностью до целого числа(когда `border-box`, это `height`; когда `content-box`, это `height + padding + border`) + el.clientHeight; + // с точностью до десятых(когда `border-box`, это `height`; когда `content-box`, это `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Позиция и смещение + + + Позиция + + ```js + // jQuery + $el.position(); + + // Нативно + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Смещение + + ```js + // jQuery + $el.offset(); + + // Нативно + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Прокрутка вверх + + ```js + // jQuery + $(window).scrollTop(); + + // Нативно + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ Наверх](#Содержание)** + +## Манипуляции DOM + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Нативно + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Текст + + + Получить текст + + ```js + // jQuery + $el.text(); + + // Нативно + el.textContent; + ``` + + + Присвоить текст + + ```js + // jQuery + $el.text(string); + + // Нативно + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Получить HTML + + ```js + // jQuery + $el.html(); + + // Нативно + el.innerHTML; + ``` + + + Присвоить HTML + + ```js + // jQuery + $el.html(htmlString); + + // Нативно + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Добавление элемента ребенка после последнего ребенка элемента родителя + + ```js + // jQuery + $el.append("
hello
"); + + // Нативно + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Нативно + el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) insertBefore + + Вставка нового элемента перед выбранным элементом + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Нативно + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Вставка новго элемента после выбранного элемента + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Нативно + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +- [3.8](#3.8) is + + Возвращает `true` если совпадает с селектором запроса + + ```js + // jQuery - заметьте что `is` так же работает с `function` или `elements` которые не имют к этому отношения + $el.is(selector); + + // Нативно + el.matches(selector); + ``` + +**[⬆ Наверх](#Содержание)** + +## Ajax + +Заменить с [fetch](https://github.com/camsong/fetch-ie8) и [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ Наверх](#Содержание)** + +## События + +Для полной замены с пространством имен и делегация, сослаться на [oui-dom-events](https://github.com/oneuijs/oui-dom-events) + +- [5.1](#5.1) Связать событие используя on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Нативно + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Отвязать событие используя off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Нативно + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Нативно + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ Наверх](#Содержание)** + +## Утилиты + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Нативно + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Нативно + string.trim(); + ``` + +- [6.3](#6.3) Назначение объекта + + Дополнительно, используйте полифил object.assign https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Нативно + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Нативно + el !== child && el.contains(child); + ``` + +**[⬆ Наверх](#Содержание)** + +## Альтернативы + +* [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Примеры как исполняются частые события, элементы, ajax и тд с ванильным javascript. +* [npm-dom](http://github.com/npm-dom) и [webmodules](http://github.com/webmodules) - Отдельные DOM модули можно найти на NPM + +## Переводы + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Поддержка браузеров + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# License + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-tr.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-tr.md new file mode 100644 index 0000000..85d614f --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-tr.md @@ -0,0 +1,665 @@ +## jQuery'e İhtiyacınız Yok [![Build Status](https://travis-ci.org/oneuijs/You-Dont-Need-jQuery.svg)](https://travis-ci.org/oneuijs/You-Dont-Need-jQuery) + +Önyüz ortamları bugünlerde çok hızlı gelişiyor, öyle ki modern tarayıcılar DOM/DOM APİ'lere ait önemli gereklilikleri çoktan yerine getirdiler. DOM işleme ve olaylar için, en baştan jQuery ögrenmemize gerek kalmadı. Bu arada, üstünlükleri ile jQuery'i önemsizleştiren ve doğrudan DOM değişikliklerinin bir Anti-pattern olduğunu gösteren, React, Angular ve Vue gibi gelişmiş önyüz kütüphanelerine ayrıca teşekkür ederiz. Bu proje, IE10+ desteği ile coğunluğu jQuery yöntemlerine alternatif olan yerleşik uygulamaları içerir. + +## İçerik Tablosu + +1. [Sorgu seçiciler](#sorgu-seçiciler) +1. [CSS & Stil](#css--stil) +1. [DOM düzenleme](#dom-düzenleme) +1. [Ajax](#ajax) +1. [Olaylar](#olaylar) +1. [Araçlar](#araçlar) +1. [Alternatifler](#alternatifler) +1. [Çeviriler](#Çeviriler) +1. [Tarayıcı desteği](#tarayıcı-desteği) + +## Sorgu seçiciler + +Yaygın olan class, id ve özellik seçiciler yerine, `document.querySelector` yada `document.querySelectorAll` kullanabiliriz. Ayrıldıkları nokta: +* `document.querySelector` ilk seçilen öğeyi döndürür +* `document.querySelectorAll` Seçilen tüm öğeleri NodeList olarak geri döndürür. `[].slice.call(document.querySelectorAll(selector) || []);` kullanarak bir diziye dönüştürebilirsiniz. +* Herhangi bir öğenin seçilememesi durumda ise, jQuery `[]` döndürürken, DOM API `null` döndürecektir. Null Pointer istisnası almamak için `||` ile varsayılan değere atama yapabilirsiniz, örnek: `document.querySelectorAll(selector) || []` + +> Uyarı: `document.querySelector` ve `document.querySelectorAll` biraz **YAVAŞ** olabilir, Daha hızlısını isterseniz, `getElementById`, `document.getElementsByClassName` yada `document.getElementsByTagName` kullanabilirsiniz. + +- [1.0](#1.0) Seçici ile sorgu + + ```js + // jQuery + $('selector'); + + // Yerleşik + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Sınıf ile sorgu + + ```js + // jQuery + $('.class'); + + // Yerleşik + document.querySelectorAll('.class'); + + // yada + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Id ile sorgu + + ```js + // jQuery + $('#id'); + + // Yerleşik + document.querySelector('#id'); + + // yada + document.getElementById('id'); + ``` + +- [1.3](#1.3) Özellik ile sorgu + + ```js + // jQuery + $('a[target=_blank]'); + + // Yerleşik + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Öğe erişimi + + + Node'a erişim + + ```js + // jQuery + $el.find('li'); + + // Yerleşik + el.querySelectorAll('li'); + ``` + + + Body'e erişim + + ```js + // jQuery + $('body'); + + // Yerleşik + document.body; + ``` + + + Özelliğe erişim + + ```js + // jQuery + $el.attr('foo'); + + // Yerleşik + el.getAttribute('foo'); + ``` + + + Data özelliğine erişim + + ```js + // jQuery + $el.data('foo'); + + // Yerleşik + // getAttribute kullanarak + el.getAttribute('data-foo'); + // Eğer IE 11+ kullanıyor iseniz, `dataset` ile de erişebilirsiniz + el.dataset['foo']; + ``` + +- [1.5](#1.5) Kardeş/Önceki/Sonraki öğeler + + + Kardeş öğeler + + ```js + // jQuery + $el.siblings(); + + // Yerleşik + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Önceki öğeler + + ```js + // jQuery + $el.prev(); + + // Yerleşik + el.previousElementSibling; + ``` + + + Sonraki öğeler + + ```js + // jQuery + $el.next(); + + // Yerleşik + el.nextElementSibling; + ``` + +- [1.6](#1.6) En yakın + + Verilen seçici ile eşleşen ilk öğeyi döndürür, geçerli öğeden başlayarak document'a kadar geçiş yapar. + + ```js + // jQuery + $el.closest(selector); + + // Yerleşik - Sadece en güncellerde, IE desteklemiyor + el.closest(selector); + + // Yerleşik - IE10+ + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Önceki atalar + + Verilen seçici ile eşleşen öğe veya DOM node veya jQuery nesnesi hariç, mevcut öğe ile aradaki tüm önceki ataları bir set dahilinde verir. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Yerleşik + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // eşleştirme, atadan başlar + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Yerleşik + document.querySelector('#my-input').value; + ``` + + + e.currentTarget ile `.radio` arasındaki dizini verir + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Yerleşik + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe İçeriği + + Mevcut Iframe için `$('iframe').contents()` yerine `contentDocument` döndürür. + + + Iframe İçeriği + + ```js + // jQuery + $iframe.contents(); + + // Yerleşik + iframe.contentDocument; + ``` + + + Iframe seçici + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Yerleşik + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ üste dön](#İçerik-tablosu)** + +## CSS & Stil + +- [2.1](#2.1) CSS + + + Stili verir + + ```js + // jQuery + $el.css("color"); + + // Yerleşik + // NOT: Bilinen bir hata, eğer stil değeri 'auto' ise 'auto' döndürür + const win = el.ownerDocument.defaultView; + // null sahte tipleri döndürmemesi için + win.getComputedStyle(el, null).color; + ``` + + + Stil değiştir + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Yerleşik + el.style.color = '#ff0011'; + ``` + + + Stil değeri al/değiştir + + Eğer aynı anda birden fazla stili değiştirmek istiyor iseniz, oui-dom-utils paketi içindeki [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) metoduna göz atınız. + + + + Sınıf ekle + + ```js + // jQuery + $el.addClass(className); + + // Yerleşik + el.classList.add(className); + ``` + + + Sınıf çıkart + + ```js + // jQuery + $el.removeClass(className); + + // Yerleşik + el.classList.remove(className); + ``` + + + sınfı var mı? + + ```js + // jQuery + $el.hasClass(className); + + // Yerleşik + el.classList.contains(className); + ``` + + + Sınfı takas et + + ```js + // jQuery + $el.toggleClass(className); + + // Yerleşik + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Genişlik ve Yükseklik + + Genişlik ve Yükseklik teorik olarak aynı şekilde, örnek olarak Yükseklik veriliyor + + + Window Yüksekliği + + ```js + // window yüksekliği + $(window).height(); + // kaydırma çubuğu olmaksızın, jQuery ile aynı + window.document.documentElement.clientHeight; + // kaydırma çubuğu ile birlikte + window.innerHeight; + ``` + + + Document yüksekliği + + ```js + // jQuery + $(document).height(); + + // Yerleşik + document.documentElement.scrollHeight; + ``` + + + Öğe yüksekliği + + ```js + // jQuery + $el.height(); + + // Yerleşik + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // Tamsayı olarak daha doğru olanı(`border-box` iken, `height` esas; `content-box` ise, `height + padding + border` esas alınır) + el.clientHeight; + // Ondalık olarak daha doğru olanı(`border-box` iken, `height` esas; `content-box` ise, `height + padding + border` esas alınır) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Pozisyon ve Ara-Açıklığı + + + Pozisyon + + ```js + // jQuery + $el.position(); + + // Yerleşik + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Ara-Açıklığı + + ```js + // jQuery + $el.offset(); + + // Yerleşik + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Üste kaydır + + ```js + // jQuery + $(window).scrollTop(); + + // Yerleşik + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ üste dön](#İçerik-tablosu)** + +## DOM düzenleme + +- [3.1](#3.1) Çıkartma + ```js + // jQuery + $el.remove(); + + // Yerleşik + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Metin + + + Get text + + ```js + // jQuery + $el.text(); + + // Yerleşik + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Yerleşik + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + HTML'i alma + + ```js + // jQuery + $el.html(); + + // Yerleşik + el.innerHTML; + ``` + + + HTML atama + + ```js + // jQuery + $el.html(htmlString); + + // Yerleşik + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Sona ekleme + + Ata öğenin son çocuğundan sonra öğe ekleme + + ```js + // jQuery + $el.append("
hello
"); + + // Yerleşik + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) Öne ekleme + + ```js + // jQuery + $el.prepend("
hello
"); + + // Yerleşik + el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) Öncesine Ekleme + + Seçili öğeden önceki yere yeni öğe ekleme + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Yerleşik + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) Sonrasına ekleme + + Seçili öğeden sonraki yere yeni öğe ekleme + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Yerleşik + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +- [3.8](#3.8) eşit mi? + + Sorgu seçici ile eşleşiyor ise `true` döner + + ```js + // jQuery için not: `is` aynı zamanda `function` veya `elements` için de geçerlidir fakat burada bir önemi bulunmuyor + $el.is(selector); + + // Yerleşik + el.matches(selector); + ``` +- [3.9](#3.9) Klonlama + + Mevcut öğenin bir derin kopyasını oluşturur + + ```js + // jQuery + $el.clone(); + + // Yerleşik + el.cloneNode(); + + // Derin kopya için, `true` parametresi kullanınız + ``` +**[⬆ üste dön](#İçerik-tablosu)** + +## Ajax + +[Fetch API](https://fetch.spec.whatwg.org/) ajax için XMLHttpRequest yerine kullanan yeni standarttır. Chrome ve Firefox destekler, eski tarayıcılar için polyfill kullanabilirsiniz. + +IE9+ ve üstü için [github/fetch](http://github.com/github/fetch) yada IE8+ ve üstü için [fetch-ie8](https://github.com/camsong/fetch-ie8/), JSONP istekler için [fetch-jsonp](https://github.com/camsong/fetch-jsonp) deneyiniz. + +**[⬆ üste dön](#İçerik-tablosu)** + +## Olaylar + +Namespace ve Delegasyon ile tam olarak değiştirmek için, https://github.com/oneuijs/oui-dom-events sayfasına bakınız + +- [5.1](#5.1) on ile bir öğeye bağlama + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Yerleşik + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) off ile bir bağlamayı sonlandırma + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Yerleşik + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Tetikleyici + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Yerleşik + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ üste dön](#İçerik-tablosu)** + +## Araçlar + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Yerleşik + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Yerleşik + string.trim(); + ``` + +- [6.3](#6.3) Nesne atama + + Türetmek için, object.assign polyfill'ini deneyiniz https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Yerleşik + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) İçerme + + ```js + // jQuery + $.contains(el, child); + + // Yerleşik + el !== child && el.contains(child); + ``` + +**[⬆ üste dön](#İçerik-tablosu)** + +## Alternatifler + +* [jQuery'e İhtiyacınız Yok](http://youmightnotneedjquery.com/) - Yaygın olan olay, öğe ve ajax işlemlerinin yalın Javascript'teki karşılıklarına ait örnekler +* [npm-dom](http://github.com/npm-dom) ve [webmodules](http://github.com/webmodules) - NPM için ayrı DOM modül organizasyonları + +## Çeviriler + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Tarayıcı Desteği + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# Lisans + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-vi.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-vi.md new file mode 100644 index 0000000..7eb60f4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README-vi.md @@ -0,0 +1,634 @@ +## Bạn không cần jQuery nữa đâu + +Ngày nay, môi trường lập trình front-end phát triển rất nhanh chóng, các trình duyệt hiện đại đã cung cấp các API đủ tốt để làm việc với DOM/BOM. Bạn không còn cần phải học về jQuery nữa. Đồng thời, nhờ sự ra đời của các thư viện như React, Angular và Vue đã khiến cho việc can thiệp trực tiếp vào DOM trở thành một việc không tốt. jQuery đã không còn quan trọng như trước nữa. Bài viết này tổng hợp những cách để thay thế các hàm của jQuery bằng các hàm được hỗ trợ bởi trình duyệt, và hó cũng hoạt động trên IE 10+ + +## Danh mục + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [Thao tác với DOM](#thao-tác-với-dom) +1. [Ajax](#ajax) +1. [Events](#events) +1. [Hàm tiện ích](#hàm-tiện-ích) +1. [Ngôn ngữ khác](#ngôn-ngữ-khác) +1. [Các trình duyệt hỗ trợ](#các-trình-duyệt-hỗ-trợ) + +## Query Selector + +Đối với những selector phổ biến như class, id hoặc thuộc tính thì chúng ta có thể sử dụng `document.querySelector` hoặc `document.querySelectorAll` để thay thế cho jQuery selector. Sự khác biệt của hai hàm này là ở chỗ: + +* `document.querySelector` trả về element đầu tiên được tìm thấy +* `document.querySelectorAll` trả về tất cả các element được tìm thấy dưới dạng một instance của NodeList. Nó có thể được convert qua array bằng cách `[].slice.call(document.querySelectorAll(selector) || []);` +* Nếu không có element nào được tìm thấy, thì jQuery sẽ trả về một array rỗng `[]` trong khi đó DOM API sẽ trả về `null`. Hãy chú ý đến Null Pointer Exception. Bạn có thể sử dụng toán tử `||` để đặt giá trị default nếu như không có element nào được tìm thấy, ví dụ như `document.querySelectorAll(selector) || []` + +> Chú ý : `document.querySelector` và `document.querySelectorAll` hoạt động khá **CHẬM**, hãy thử dùng `getElementById`, `document.getElementsByClassName` hoặc `document.getElementsByTagName` nếu bạn muốn đạt hiệu suất tốt hơn. + +- [1.0](#1.0) Query bằng selector + + ```js + // jQuery + $('selector'); + + // Native + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query bằng class + + ```js + // jQuery + $('.class'); + + // Native + document.querySelectorAll('.class'); + + // hoặc + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Query bằng id + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + + // hoặc + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query bằng thuộc tính + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Tìm bất cứ gì. + + + Tìm node + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + + + Tìm body + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + + + lấy thuộc tính + + ```js + // jQuery + $el.attr('foo'); + + // Native + e.getAttribute('foo'); + ``` + + + Lấy giá trị của thuộc tính `data` + + ```js + // jQuery + $el.data('foo'); + + // Native + // using getAttribute + el.getAttribute('data-foo'); + // you can also use `dataset` if only need to support IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Tìm element cùng level/trước/sau + + + Element cùng level + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Element ở phía trước + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + + ``` + + + Element ở phía sau + + ```js + // next + $el.next(); + el.nextElementSibling; + ``` + +- [1.6](#1.6) Element gần nhất + + Trả về element đầu tiên có selector khớp với yêu cầu khi duyệt từ element hiện tại trở lên tới document. + + ```js + // jQuery + $el.closest(queryString); + + // Native + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Tìm parent + + Truy ngược một cách đệ quy tổ tiên của element hiện tại, cho đến khi tìm được một element tổ tiên ( element cần tìm ) mà element đó là con trực tiếp của element khớp với selector được cung cấp, Return lại element cần tìm đó. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Lấy index của e.currentTarget trong danh sách các element khớp với selector `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Nội dung Iframe + + `$('iframe').contents()` trả về thuộc tính `contentDocument` của iframe được tìm thấy + + + Nọi dung iframe + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Query Iframe + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ Trở về đầu](#danh-mục)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Lấy style + + ```js + // jQuery + $el.css("color"); + + // Native + // NOTE: Bug đã được biết, sẽ trả về 'auto' nếu giá trị của style là 'auto' + const win = el.ownerDocument.defaultView; + // null means not return presudo styles + win.getComputedStyle(el, null).color; + ``` + + + Đặt style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Lấy/Đặt Nhiều style + + Nếu bạn muốn đặt nhiều style một lần, bạn có thể sẽ thích phương thức [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) trong thư viện oui-dom-utils. + + + + Thêm class và element + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + Loại bỏ class class ra khỏi element + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + Kiểm tra xem element có class nào đó hay không + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Chiều rộng, chiều cao + + Về mặt lý thuyết thì chiều rộng và chiều cao giống như nhau trong cả jQuery và DOM API: + + + Chiều rộng của window + + ```js + // window height + $(window).height(); + // trừ đi scrollbar + window.document.documentElement.clientHeight; + // Tính luôn scrollbar + window.innerHeight; + ``` + + + Chiều cao của Document + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Chiều cao của element + + ```js + // jQuery + $el.height(); + + // Native + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // chính xác tới số nguyên(khi có thuộc tính `box-sizing` là `border-box`, nó là `height`; khi box-sizing là `content-box`, nó là `height + padding + border`) + el.clientHeight; + // Chính xác tới số thập phân(khi `box-sizing` là `border-box`, nó là `height`; khi `box-sizing` là `content-box`, nó là `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ Trở về đầu](#danh-mục)** + +## Thao tác với DOM + +- [3.1](#3.1) Loại bỏ + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Lấy text + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + Đặt giá trị text + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Lấy HTML + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + Đặt giá trị HTML + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + append một element sau element con cuối cùng của element cha + + ```js + // jQuery + $el.append("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.appendChild(newEl); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.insertBefore(newEl, el.firstChild); + ``` + +- [3.6](#3.6) insertBefore + + Chèn một node vào trước element được query. + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Chèn node vào sau element được query + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ Trở về đầu](#danh-mục)** + +## Ajax + +Thay thế bằng [fetch](https://github.com/camsong/fetch-ie8) và [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ Trở về đầu](#danh-mục)** + +## Events + +Để có một sự thay thế đầy đủ nhất, bạn nên sử dụng https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind event bằng on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind event bằng off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ Trở về đầu](#danh-mục)** + +## Hàm tiện ích + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Native + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Native + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + Mở rộng, sử dụng object.assign https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +**[⬆ Trở về đầu](#danh-mục)** + +## Ngôn ngữ khác + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) + +## Các trình duyệt hỗ trợ + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# Giấy phép + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.ko-KR.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.ko-KR.md new file mode 100644 index 0000000..5445511 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.ko-KR.md @@ -0,0 +1,709 @@ +## You Don't Need jQuery + +오늘날 프론트엔드 개발 환경은 급격히 진화하고 있고, 모던 브라우저들은 이미 충분히 많은 DOM/BOM API들을 구현했습니다. 우리는 jQuery를 DOM 처리나 이벤트를 위해 처음부터 배울 필요가 없습니다. React, Angular, Vue같은 프론트엔드 라이브러리들이 주도권을 차지하는 동안 DOM을 바로 처리하는 것은 안티패턴이 되었고, jQuery의 중요성은 줄어들었습니다. 이 프로젝트는 대부분의 jQuery 메소드의 대안을 IE 10+ 이상을 지원하는 네이티브 구현으로 소개합니다. + +## 목차 + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [DOM 조작](#dom-조작) +1. [Ajax](#ajax) +1. [이벤트](#이벤트) +1. [유틸리티](#유틸리티) +1. [대안방법](#대안방법) +1. [번역](#번역) +1. [브라우저 지원](#브라우저-지원) + +## Query Selector + +평범한 class, id, attribute같은 selecotor는 `document.querySelector`나 `document.querySelectorAll`으로 대체할 수 있습니다. +* `document.querySelector`는 처음 매칭된 엘리먼트를 반환합니다. +* `document.querySelectorAll`는 모든 매칭된 엘리먼트를 NodeList로 반환합니다. `[].slice.call`을 사용해서 Array로 변환할 수 있습니다. +* 만약 매칭된 엘리멘트가 없으면 jQuery는 `[]` 를 반환하지만 DOM API는 `null`을 반환합니다. Null Pointer Exception에 주의하세요. + +> 안내: `document.querySelector`와 `document.querySelectorAll`는 꽤 **느립니다**, `getElementById`나 `document.getElementsByClassName`, `document.getElementsByTagName`를 사용하면 퍼포먼스가 향상을 기대할 수 있습니다. + +- [1.0](#1.0) selector로 찾기 + + ```js + // jQuery + $('selector'); + + // Native + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) class로 찾기 + + ```js + // jQuery + $('.class'); + + // Native + document.querySelectorAll('.class'); + + // or + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) id로 찾기 + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + + // or + document.getElementById('id'); + ``` + +- [1.3](#1.3) 속성(attribute)으로 찾기 + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) 자식에서 찾기 + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + +- [1.5](#1.5) 형제/이전/다음 엘리먼트 찾기 + + + 형제 엘리먼트 + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + 이전 엘리먼트 + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + ``` + + + 다음 엘리먼트 + + ```js + // jQuery + $el.next(); + + // Native + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + 현재 엘리먼트부터 document로 이동하면서 주어진 셀렉터와 일치하는 가장 가까운 엘리먼트를 반환합니다. + + ```js + // jQuery + $el.closest(selector); + + // Native - 최신 브라우저만, IE는 미지원 + el.closest(selector); + + // Native - IE10 이상 + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + 주어진 셀렉터에 매칭되는 엘리먼트를 찾기까지 부모 태그들을 위로 올라가며 탐색하여 저장해두었다가 DOM 노드 또는 jQuery object로 반환합니다. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + e.currentTarget이 `.radio`의 몇번째인지 구하기 + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()`는 iframe에 한정해서 `contentDocument`를 반환합니다. + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Iframe에서 찾기 + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +- [1.10](#1.10) body 얻기 + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + +- [1.11](#1.11) 속성 얻기 및 설정 + + + 속성 얻기 + + ```js + // jQuery + $el.attr('foo'); + + // Native + el.getAttribute('foo'); + ``` + + 속성 설정하기 + + ```js + // jQuery, DOM 변형 없이 메모리에서 작동됩니다. + $el.attr('foo', 'bar'); + + // Native + el.setAttribute('foo', 'bar'); + ``` + + + `data-` 속성 얻기 + + ```js + // jQuery + $el.data('foo'); + + // Native (`getAttribute` 사용) + el.getAttribute('data-foo'); + // Native (IE 11 이상의 지원만 필요하다면 `dataset`을 사용) + el.dataset['foo']; + ``` + +**[⬆ 목차로 돌아가기](#목차)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + style값 얻기 + + ```js + // jQuery + $el.css("color"); + + // Native + // NOTE: 알려진 버그로, style값이 'auto'이면 'auto'를 반환합니다. + const win = el.ownerDocument.defaultView; + // null은 가상 스타일은 반환하지 않음을 의미합니다. + win.getComputedStyle(el, null).color; + ``` + + + style값 설정하기 + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Style값들을 동시에 얻거나 설정하기 + + 만약 한번에 여러 style값을 바꾸고 싶다면 oui-dom-utils 패키지의 [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194)를 사용해보세요. + + + + class 추가하기 + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + class 제거하기 + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + class를 포함하고 있는지 검사하기 + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + class 토글하기 + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) 폭과 높이 + + 폭과 높이는 이론상 동일합니다. 높이로 예를 들겠습니다. + + + Window의 높이 + + ```js + // window 높이 + $(window).height(); + // jQuery처럼 스크롤바를 제외하기 + window.document.documentElement.clientHeight; + // 스크롤바 포함 + window.innerHeight; + ``` + + + 문서 높이 + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Element 높이 + + ```js + // jQuery + $el.height(); + + // Native + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // 정수로 정확하게(`border-box`일 때 이 값은 `height`이고, `content-box`일 때, 이 값은 `height + padding + border`) + el.clientHeight; + // 실수로 정확하게(`border-box`일 때 이 값은 `height`이고, `content-box`일 때, 이 값은 `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ 목차로 돌아가기](#목차)** + +## DOM 조작 + +- [3.1](#3.1) 제거 + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + text 가져오기 + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + text 설정하기 + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + HTML 가져오기 + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + HTML 설정하기 + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) 해당 엘리먼트의 자식들 뒤에 넣기(Append) + + 부모 엘리먼트의 마지막 자식으로 엘리먼트를 추가합니다. + + ```js + // jQuery + $el.append("
hello
"); + + // Native + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) 해당 엘리먼트의 자식들 앞에 넣기(Prepend) + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native +el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) 해당 엘리먼트 앞에 넣기(insertBefore) + + 새 노드를 선택한 엘리먼트 앞에 넣습니다. + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) 해당 엘리먼트 뒤에 넣기(insertAfter) + + 새 노드를 선택한 엘리먼트 뒤에 넣습니다. + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` +- [3.8](#3.8) is + + query selector와 일치하면 `true` 를 반환합니다. + + ```js + // jQuery + $el.is(selector); + + // Native + el.matches(selector); + ``` + +- [3.9](#3.9) clone + + 엘리먼트의 복제본을 만듭니다. + + ```js + // jQuery + $el.clone(); + + // Native + el.cloneNode(); + + // Deep clone은 파라미터를 `true` 로 설정하세요. + ``` + +- [3.10](#3.10) empty + + 모든 자식 노드를 제거합니다. + + ```js + // jQuery + $el.empty(); + + // Native + el.innerHTML = ''; + ``` + +**[⬆ 목차로 돌아가기](#목차)** + +## Ajax + +[Fetch API](https://fetch.spec.whatwg.org/) 는 XMLHttpRequest를 ajax로 대체하는 새로운 표준 입니다. Chrome과 Firefox에서 작동하며, polyfill을 이용해서 구형 브라우저에서 작동되도록 만들 수도 있습니다. + +IE9 이상에서 지원하는 [github/fetch](http://github.com/github/fetch) 혹은 IE8 이상에서 지원하는 [fetch-ie8](https://github.com/camsong/fetch-ie8/), JSONP 요청을 만드는 [fetch-jsonp](https://github.com/camsong/fetch-jsonp)를 이용해보세요. + +**[⬆ 목차로 돌아가기](#목차)** + +## 이벤트 + +namespace와 delegation을 포함해서 완전히 갈아 엎길 원하시면 https://github.com/oneuijs/oui-dom-events 를 고려해보세요. + +- [5.1](#5.1) 이벤트 Bind 걸기 + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) 이벤트 Bind 풀기 + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) 이벤트 발생시키기(Trigger) + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ 목차로 돌아가기](#목차)** + +## 유틸리티 + +- [6.1](#6.1) 배열인지 검사(isArray) + + ```js + // jQuery + $.isArray(range); + + // Native + Array.isArray(range); + ``` + +- [6.2](#6.2) 앞뒤 공백 지우기(Trim) + + ```js + // jQuery + $.trim(string); + + // Native + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + 사용하려면 object.assign polyfill을 사용하세요. https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +- [6.5](#6.5) inArray + + ```js + // jQuery + $.inArray(item, array); + + // Native + array.indexOf(item); + ``` + +- [6.6](#6.6) map + + ```js + // jQuery + $.map(array, function(value, index) { + }); + + // Native + Array.map(function(value, index) { + }); + ``` + +**[⬆ 목차로 돌아가기](#목차)** + +## 대안방법 + +* [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - 일반 자바스크립트로 공통이벤트, 엘리먼트, ajax 등을 다루는 방법 예제. +* [npm-dom](http://github.com/npm-dom) 과 [webmodules](http://github.com/webmodules) - 개별 DOM모듈을 NPM에서 찾을 수 있습니다. + +## 번역 + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) +* [Italian](./README-it.md) + +## 브라우저 지원 + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# License + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.md new file mode 100644 index 0000000..ae897dc --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.md @@ -0,0 +1,1191 @@ +## You Don't Need jQuery [![Build Status](https://travis-ci.org/oneuijs/You-Dont-Need-jQuery.svg)](https://travis-ci.org/oneuijs/You-Dont-Need-jQuery) + +Frontend environments evolve rapidly nowadays, modern browsers have already implemented a great deal of DOM/BOM APIs which are good enough. We don't have to learn jQuery from scratch for DOM manipulation or events. In the meantime, thanks to the prevailment of frontend libraries such as React, Angular and Vue, manipulating DOM directly becomes anti-pattern, jQuery has never been less important. This project summarizes most of the jQuery method alternatives in native implementation, with IE 10+ support. + + +## Table of Contents + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [DOM Manipulation](#dom-manipulation) +1. [Ajax](#ajax) +1. [Events](#events) +1. [Utilities](#utilities) +1. [Promises](#promises) +1. [Animation](#animation) +1. [Alternatives](#alternatives) +1. [Translations](#translations) +1. [Browser Support](#browser-support) + +## Query Selector + +In place of common selectors like class, id or attribute we can use `document.querySelector` or `document.querySelectorAll` for substitution. The differences lie in: +* `document.querySelector` returns the first matched element +* `document.querySelectorAll` returns all matched elements as NodeList. It can be converted to Array using `[].slice.call(document.querySelectorAll(selector) || []);` +* If no elements matched, jQuery would return `[]` whereas the DOM API will return `null`. Pay attention to Null Pointer Exception. You can also use `||` to set default value if not found, like `document.querySelectorAll(selector) || []` + +> Notice: `document.querySelector` and `document.querySelectorAll` are quite **SLOW**, try to use `getElementById`, `document.getElementsByClassName` or `document.getElementsByTagName` if you want to get a performance bonus. + +- [1.0](#1.0) Query by selector + + ```js + // jQuery + $('selector'); + + // Native + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query by class + + ```js + // jQuery + $('.class'); + + // Native + document.querySelectorAll('.class'); + + // or + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Query by id + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + + // or + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query by attribute + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Query in descendents + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + +- [1.5](#1.5) Sibling/Previous/Next Elements + + + Sibling elements + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Previous elements + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + ``` + + + Next elements + + ```js + // jQuery + $el.next(); + + // Native + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Return the first matched element by provided selector, traversing from current element to document. + + ```js + // jQuery + $el.closest(selector); + + // Native - Only latest, NO IE + el.closest(selector); + + // Native - IE10+ + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + Get the ancestors of each element in the current set of matched elements, up to but not including the element matched by the selector, DOM node, or jQuery object. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Get index of e.currentTarget between `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()` returns `contentDocument` for this specific iframe + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +- [1.10](#1.10) Get body + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + +- [1.11](#1.11) Attribute getter and setter + + + Get an attribute + + ```js + // jQuery + $el.attr('foo'); + + // Native + el.getAttribute('foo'); + ``` + + Set an attribute + + ```js + // jQuery, note that this works in memory without change the DOM + $el.attr('foo', 'bar'); + + // Native + el.setAttribute('foo', 'bar'); + ``` + + + Get a `data-` attribute + + ```js + // jQuery + $el.data('foo'); + + // Native (use `getAttribute`) + el.getAttribute('data-foo'); + // Native (use `dataset` if only need to support IE 11+) + el.dataset['foo']; + ``` + +**[⬆ back to top](#table-of-contents)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Get style + + ```js + // jQuery + $el.css("color"); + + // Native + // NOTE: Known bug, will return 'auto' if style value is 'auto' + const win = el.ownerDocument.defaultView; + // null means not return pseudo styles + win.getComputedStyle(el, null).color; + ``` + + + Set style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Get/Set Styles + + Note that if you want to set multiple styles once, you could refer to [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) method in oui-dom-utils package. + + + + Add class + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + Remove class + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + has class + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Width and Height are theoretically identical, take Height as example: + + + Window height + + ```js + // window height + $(window).height(); + // without scrollbar, behaves like jQuery + window.document.documentElement.clientHeight; + // with scrollbar + window.innerHeight; + ``` + + + Document height + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Element height + + ```js + // jQuery + $el.height(); + + // Native + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // accurate to integer(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.clientHeight; + // accurate to decimal(when `border-box`, it's `height`; when `content-box`, it's `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ back to top](#table-of-contents)** + +## DOM Manipulation + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Get text + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Get HTML + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + Set HTML + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Append child element after the last child of parent element + + ```js + // jQuery + $el.append("
hello
"); + + // Native + el.insertAdjacentHTML("beforeend","
hello
"); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native + el.insertAdjacentHTML("afterbegin","
hello
"); + ``` + +- [3.6](#3.6) insertBefore + + Insert a new node before the selected elements + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Insert a new node after the selected elements + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +- [3.8](#3.8) is + + Return `true` if it matches the query selector + + ```js + // jQuery - Notice `is` also work with `function` or `elements` which is not concerned here + $el.is(selector); + + // Native + el.matches(selector); + ``` +- [3.9](#3.9) clone + + Create a deep copy of that element + + ```js + // jQuery + $el.clone(); + + // Native + el.cloneNode(); + + // For Deep clone , set param as `true` + ``` + +- [3.10](#3.10) empty + + Remove all child nodes + + ```js + // jQuery + $el.empty(); + + // Native + el.innerHTML = ''; + ``` + +- [3.11](#3.11) wrap + + Wrap an HTML structure around each element + + ```js + // jQuery + $('.inner').wrap('
'); + + // Native + [].slice.call(document.querySelectorAll('.inner')).forEach(function(el){ + var wrapper = document.createElement('div'); + wrapper.className = 'wrapper'; + el.parentNode.insertBefore(wrapper, el); + el.parentNode.removeChild(el); + wrapper.appendChild(el); + }); + ``` + +- [3.12](#3.12) unwrap + + Remove the parents of the set of matched elements from the DOM + + ```js + // jQuery + $('.inner').unwrap(); + + // Native + [].slice.call(document.querySelectorAll('.inner')).forEach(function(el){ + [].slice.call(el.childNodes).forEach(function(child){ + el.parentNode.insertBefore(child, el); + }); + el.parentNode.removeChild(el); + }); + ``` + + - [3.13](#3.13) replaceWith + + Replace each element in the set of matched elements with the provided new content + + ```js + // jQuery + $('.inner').replaceWith('
'); + + // Native + [].slice.call(document.querySelectorAll('.inner')).forEach(function(el){ + var outer = document.createElement('div'); + outer.className = 'outer'; + el.parentNode.insertBefore(outer, el); + el.parentNode.removeChild(el); + }); + ``` + + +**[⬆ back to top](#table-of-contents)** + +## Ajax + +[Fetch API](https://fetch.spec.whatwg.org/) is the new standard to replace XMLHttpRequest to do ajax. It works on Chrome and Firefox, you can use polyfills to make it work on legacy browsers. + +Try [github/fetch](http://github.com/github/fetch) on IE9+ or [fetch-ie8](https://github.com/camsong/fetch-ie8/) on IE8+, [fetch-jsonp](https://github.com/camsong/fetch-jsonp) to make JSONP requests. + +**[⬆ back to top](#table-of-contents)** + +## Events + +For a complete replacement with namespace and delegation, refer to https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind an event with on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind an event with off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Utilities + +Most of utilities are found by native API. Others advanced functions could be choosed better utilities library focus on consistency and performance. Recommend [lodash](https://lodash.com) to replace. + +- [6.1](#6.1) Basic utilities + + + isArray + + Determine whether the argument is an array. + + ```js + // jQuery + $.isArray(array); + + // Native + Array.isArray(array); + ``` + + + isWindow + + Determine whether the argument is a window. + + ```js + // jQuery + $.isArray(obj); + + // Native + function isWindow(obj) { + return obj != null && obj === obj.window; + } + ``` + + + inArray + + Search for a specified value within an array and return its index (or -1 if not found). + + ```js + // jQuery + $.inArray(item, array); + + // Native + Array.indexOf(item); + ``` + + + isNumbic + + Determines whether its argument is a number. + Use `typeof` to decide type. if necessary to use library, sometimes `typeof` isn't accurate. + + ```js + // jQuery + $.isNumbic(item); + + // Native + function isNumbic(item) { + return typeof value === 'number'; + } + ``` + + + isFunction + + Determine if the argument passed is a JavaScript function object. + + ```js + // jQuery + $.isFunction(item); + + // Native + function isFunction(item) { + return typeof value === 'function'; + } + ``` + + + isEmptyObject + + Check to see if an object is empty (contains no enumerable properties). + + ```js + // jQuery + $.isEmptyObject(obj); + + // Native + function isEmptyObject(obj) { + for (let key in obj) { + return false; + } + return true; + } + ``` + + + isPlanObject + + Check to see if an object is a plain object (created using “{}” or “new Object”). + + ```js + // jQuery + $.isPlanObject(obj); + + // Native + function isPlainObject(obj) { + if (typeof (obj) !== 'object' || obj.nodeType || obj != null && obj === obj.window) { + return false; + } + + if (obj.constructor && + !{}.hasOwnPropert.call(obj.constructor.prototype, 'isPrototypeOf')) { + return false; + } + + return true; + } + ``` + + + extend + + Merge the contents of two or more objects together into the first object. + object.assign is ES6 API, and you could use [polyfill](https://github.com/ljharb/object.assign) also. + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + + + trim + + Remove the whitespace from the beginning and end of a string. + + ```js + // jQuery + $.trim(string); + + // Native + string.trim(); + ``` + + + map + + Translate all items in an array or object to new array of items. + + ```js + // jQuery + $.map(array, function(value, index) { + }); + + // Native + array.map(function(value, index) { + }); + ``` + + + each + + A generic iterator function, which can be used to seamlessly iterate over both objects and arrays. + + ```js + // jQuery + $.each(array, function(value, index) { + }); + + // Native + array.forEach(function(value, index) { + }); + ``` + + + grep + + Finds the elements of an array which satisfy a filter function. + + ```js + // jQuery + $.grep(array, function(value, index) { + }); + + // Native + array.filter(function(value, index) { + }); + ``` + + + type + + Determine the internal JavaScript [[Class]] of an object. + + ```js + // jQuery + $.type(obj); + + // Native + Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase(); + ``` + + + merge + + Merge the contents of two arrays together into the first array. + + ```js + // jQuery + $.merge(array1, array2); + + // Native + // But concat function don't remove duplicate items. + function merge() { + return Array.prototype.concat.apply([], arguments) + } + ``` + + + now + + Return a number representing the current time. + + ```js + // jQuery + $.now(); + + // Native + Date.now(); + ``` + + + proxy + + Takes a function and returns a new one that will always have a particular context. + + ```js + // jQuery + $.proxy(fn, context); + + // Native + fn.bind(context); + ``` + + + makeArray + + Convert an array-like object into a true JavaScript array. + + ```js + // jQuery + $.makeArray(array); + + // Native + [].slice.call(array); + ``` + +- [6.2](#6.2) DOM utilities + + + unique + + Sorts an array of DOM elements, in place, with the duplicates removed. Note that this only works on arrays of DOM elements, not strings or numbers. + + Sizzle's API + + + contains + + Check to see if a DOM element is a descendant of another DOM element. + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +- [6.3](#6.3) Globaleval + + ```js + // jQuery + $.globaleval(code); + + // Native + function Globaleval(code) { + let script = document.createElement('script'); + script.text = code; + + document.head.appendChild(script).parentNode.removeChild(script); + } + + // Use eval, but context of eval is current, context of $.Globaleval is global. + eval(code); + ``` + +- [6.4](#6.4) parse + + + parseHTML + + Parses a string into an array of DOM nodes. + + ```js + // jQuery + $.parseHTML(htmlString); + + // Native + function parseHTML(string) { + const tmp = document.implementation.createHTMLDocument(); + tmp.body.innerHTML = string; + return tmp.body.children; + } + ``` + + + parseJSON + + Takes a well-formed JSON string and returns the resulting JavaScript value. + + ```js + // jQuery + $.parseJSON(str); + + // Native + JSON.parse(str); + ``` + +**[⬆ back to top](#table-of-contents)** + +## Promises + +A promise represents the eventual result of an asynchronous operation. jQuery has its own way to handle promises. Native JavaScript implements a thin and minimal API to handle promises according to the [Promises/A+](http://promises-aplus.github.io/promises-spec/) specification. + +- [7.1](#7.1) done, fail, always + + `done` is called when promise is resolved, `fail` is called when promise is rejected, `always` is called when promise is either resolved or rejected. + + ```js + // jQuery + $promise.done(doneCallback).fail(failCallback).always(alwaysCallback) + + // Native + promise.then(doneCallback, failCallback).then(alwaysCallback, alwaysCallback) + ``` + +- [7.2](#7.2) when + + `when` is used to handle multiple promises. It will resolve when all promises are resolved, and reject if either one is rejected. + + ```js + // jQuery + $.when($promise1, $promise2).done((promise1Result, promise2Result) => {}) + + // Native + Promise.all([$promise1, $promise2]).then([promise1Result, promise2Result] => {}); + ``` + +- [7.3](#7.3) Deferred + + Deferred is a way to create promises. + + ```js + // jQuery + function asyncFunc() { + var d = new $.Deferred(); + setTimeout(function() { + if(true) { + d.resolve('some_value_compute_asynchronously'); + } else { + d.reject('failed'); + } + }, 1000); + return d.promise(); + } + + // Native + function asyncFunc() { + return new Promise((resolve, reject) => { + setTimeout(function() { + if (true) { + resolve('some_value_compute_asynchronously'); + } else { + reject('failed'); + } + }, 1000); + }); + } + ``` + +**[⬆ back to top](#table-of-contents)** + +## Animation + +- [8.1](#8.1) Show & Hide + + ```js + // jQuery + $el.show(); + $el.hide(); + + // Native + // More detail about show method, please refer to https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L363 + el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block'; + el.style.display = 'none'; + ``` + +- [8.2](#8.2) Toggle + + ```js + // jQuery + $el.toggle(); + + // Native + if (el.ownerDocument.defaultView.getComputedStyle(el, null).display === 'none') { + el.style.display = ''|'inline'|'inline-block'|'inline-table'|'block'; + } + else { + el.style.display = 'none'; + } + ``` + +- [8.3](#8.3) FadeIn & FadeOut + + ```js + // jQuery + $el.fadeIn(3000); + $el.fadeOut(3000); + + // Native + el.style.transition = 'opacity 3s'; + // fadeIn + el.style.opacity = '1'; + // fadeOut + el.style.opacity = '0'; + ``` + +- [8.4](#8.4) FadeTo + + ```js + // jQuery + $el.fadeTo('slow',0.15); + // Native + el.style.transition = 'opacity 3s'; // assume 'slow' equals 3 seconds + el.style.opacity = '0.15'; + ``` + +- [8.5](#8.5) FadeToggle + + ```js + // jQuery + $el.fadeToggle(); + + // Native + el.style.transition = 'opacity 3s'; + let { opacity } = el.ownerDocument.defaultView.getComputedStyle(el, null); + if (opacity === '1') { + el.style.opacity = '0'; + } + else { + el.style.opacity = '1'; + } + ``` + +- [8.6](#8.6) SlideUp & SlideDown + + ```js + // jQuery + $el.slideUp(); + $el.slideDown(); + + // Native + let originHeight = '100px'; + el.style.transition = 'height 3s'; + // slideUp + el.style.height = '0px'; + // slideDown + el.style.height = originHeight; + ``` + +- [8.7](#8.7) SlideToggle + + ```js + // jQuery + $el.slideToggle(); + + // Native + let originHeight = '100px'; + el.style.transition = 'height 3s'; + let { height } = el.ownerDocument.defaultView.getComputedStyle(el, null); + if (parseInt(height, 10) === 0) { + el.style.height = originHeight; + } + else { + el.style.height = '0px'; + } + ``` + +- [8.8](#8.8) Animate + + ```js + // jQuery + $el.animate({params}, speed); + + // Native + el.style.transition = 'all' + speed; + Object.keys(params).forEach(function(key) { + el.style[key] = params[key]; + }) + ``` + +## Alternatives + +* [You Might Not Need jQuery](http://youmightnotneedjquery.com/) - Examples of how to do common event, element, ajax etc with plain javascript. +* [npm-dom](http://github.com/npm-dom) and [webmodules](http://github.com/webmodules) - Organizations you can find individual DOM modules on NPM + +## Translations + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) +* [Italian](./README-it.md) + +## Browser Support + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# License + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.pt-BR.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.pt-BR.md new file mode 100644 index 0000000..e576f0e --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.pt-BR.md @@ -0,0 +1,628 @@ +> #### You Don't Need jQuery + +Você não precisa de jQuery +--- + +Ambientes Frontend evoluem rapidamente nos dias de hoje, navegadores modernos já implementaram uma grande parte das APIs DOM/BOM que são boas o suficiente. Nós não temos que aprender jQuery a partir do zero para manipulação do DOM ou eventos. Nesse meio tempo, graças a bibliotecas frontend como React, Angular e Vue, a manipulação direta do DOM torna-se um anti-padrão, jQuery é menos importante do que nunca. Este projeto resume a maioria das alternativas dos métodos jQuery em implementação nativa, com suporte ao IE 10+. + +## Tabela de conteúdos + +1. [Query Selector](#query-selector) +1. [CSS & Estilo](#css--estilo) +1. [Manipulação do DOM](#manipulação-do-dom) +1. [Ajax](#ajax) +1. [Eventos](#eventos) +1. [Utilitários](#utilitários) +1. [Suporte dos Navegadores](#suporte-dos-navegadores) + +## Query Selector + +No lugar de seletores comuns como classe, id ou atributo podemos usar `document.querySelector` ou `document.querySelectorAll` para substituição. As diferenças são: +* `document.querySelector` retorna o primeiro elemento correspondente +* `document.querySelectorAll` retorna todos os elementos correspondentes como NodeList. Pode ser convertido para Array usando `[].slice.call(document.querySelectorAll(selector) || []);` +* Se não tiver elementos correspondentes, jQuery retornaria `[]` considerando que a DOM API irá retornar `null`. Preste atenção ao Null Pointer Exception. Você também pode usar `||` para definir um valor padrão caso nenhum elemento seja encontrado, como `document.querySelectorAll(selector) || []` + +> Aviso: `document.querySelector` e `document.querySelectorAll` são bastante **LENTOS**, tente usar `getElementById`, `document.getElementsByClassName` ou `document.getElementsByTagName` se você quer ter uma maior performance. + +- [1.0](#1.0) Query por seletor + + ```js + // jQuery + $('selector'); + + // Nativo + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query por classe + + ```js + // jQuery + $('.class'); + + // Nativo + document.querySelectorAll('.class'); + + // ou + document.getElementsByClassName('class'); + ``` + +- [1.2](#1.2) Query por id + + ```js + // jQuery + $('#id'); + + // Nativo + document.querySelector('#id'); + + // ou + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query por atributo + + ```js + // jQuery + $('a[target=_blank]'); + + // Nativo + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Find sth. + + + Busca por nós + + ```js + // jQuery + $el.find('li'); + + // Nativo + el.querySelectorAll('li'); + ``` + + + Buscar `body` + + ```js + // jQuery + $('body'); + + // Nativo + document.body; + ``` + + + Buscar atributos + + ```js + // jQuery + $el.attr('foo'); + + // Nativo + e.getAttribute('foo'); + ``` + + + Buscar atributos `data-` + + ```js + // jQuery + $el.data('foo'); + + // Nativo + // usando getAttribute + el.getAttribute('data-foo'); + // você também pode usar `dataset` se você precisar suportar apenas IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Sibling/Previous/Next Elements + + + Sibling elements + + ```js + // jQuery + $el.siblings(); + + // Nativo + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Previous elements + + ```js + // jQuery + $el.prev(); + + // Nativo + el.previousElementSibling; + + ``` + + + Next elements + + ```js + // jQuery + $el.next(); + + // Nativo + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Retorna o primeiro elemento que corresponda ao seletor, partindo do elemento atual para o document. + + ```js + // jQuery + $el.closest(queryString); + + // Nativo + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + Obtém os ancestrais de cada elemento no atual conjunto de elementos combinados, mas não inclui o elemento correspondente pelo seletor, nó do DOM, ou objeto jQuery. + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Nativo + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Nativo + document.querySelector('#my-input').value; + ``` + + + Obter o índice do e.currentTarget entre `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Nativo + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + `$('iframe').contents()` retorna `contentDocument` para este iframe específico + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Nativo + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Nativo + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + + +## CSS & Estilo + +- [2.1](#2.1) CSS + + + Obter estilo + + ```js + // jQuery + $el.css("color"); + + // Nativo + // AVISO: Bug conhecido, irá retornar 'auto' se o valor do estilo for 'auto' + const win = el.ownerDocument.defaultView; + // null significa não retornar estilos + win.getComputedStyle(el, null).color; + ``` + + + Definir Estilo + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Nativo + el.style.color = '#ff0011'; + ``` + + + Get/Set Styles + + Observe que se você deseja setar vários estilos de uma vez, você pode optar por [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) método no pacote oui-dom-utils. + + + + Adicionar classe + + ```js + // jQuery + $el.addClass(className); + + // Nativo + el.classList.add(className); + ``` + + + Remover classe + + ```js + // jQuery + $el.removeClass(className); + + // Nativo + el.classList.remove(className); + ``` + + + Verificar classe + + ```js + // jQuery + $el.hasClass(className); + + // Nativo + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Nativo + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Largura e Altura + + `width` e `height` são teoricamente idênticos, vamos pegar `height` como exemplo: + + + Altura da janela + + ```jsc + // window height + $(window).height(); + // sem scrollbar, se comporta como jQuery + window.document.documentElement.clientHeight; + // com scrollbar + window.innerHeight; + ``` + + + Altura do Documento + + ```js + // jQuery + $(document).height(); + + // Nativo + document.documentElement.scrollHeight; + ``` + + + Altura do Elemento + + ```js + // jQuery + $el.height(); + + // Nativo + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // preciso para inteiro(quando `border-box`, é `height`; quando `content-box`, é `height + padding + border`) + el.clientHeight; + // preciso para decimal(quando `border-box`, é `height`; quando `content-box`, é `height + padding + border`) + el.getBoundingClientRect().height; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Nativo + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Nativo + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Rolar para o topo + + ```js + // jQuery + $(window).scrollTop(); + + // Nativo + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + +## Manipulação do Dom + +- [3.1](#3.1) Remover + ```js + // jQuery + $el.remove(); + + // Nativo + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Texto + + + Obter texto + + ```js + // jQuery + $el.text(); + + // Nativo + el.textContent; + ``` + + + Definir texto + + ```js + // jQuery + $el.text(string); + + // Nativo + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Obter HTML + + ```js + // jQuery + $el.html(); + + // Nativo + el.innerHTML; + ``` + + + Definir HTML + + ```js + // jQuery + $el.html(htmlString); + + // Nativo + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Incluir elemento filho após o último filho do elemento pai. + + ```js + // jQuery + $el.append("
hello
"); + + // Nativo + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.appendChild(newEl); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Nativo + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.insertBefore(newEl, el.firstChild); + ``` + +- [3.6](#3.6) insertBefore + + Insere um novo nó antes dos elementos selecionados. + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + Insere um novo nó após os elementos selecionados. + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Nativo + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + +## Ajax + +Substitua por [fetch](https://github.com/camsong/fetch-ie8) e [fetch-jsonp](https://github.com/camsong/fetch-jsonp) + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + +## Eventos + +Para uma substituição completa com namespace e delegation, consulte https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) `Bind` num evento com `on` + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Nativo + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) `Unbind` num evento com `off` + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Nativo + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Nativo + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + +## Utilitários + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Nativo + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Nativo + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + Use o polyfill `object.assign` para eetender um Object: https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Nativo + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Nativo + el !== child && el.contains(child); + ``` + +**[⬆ ir para o topo](#tabela-de-conteúdos)** + +## Suporte dos Navegadores + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# Licença + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.zh-CN.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.zh-CN.md new file mode 100644 index 0000000..24d6f87 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/README.zh-CN.md @@ -0,0 +1,656 @@ +## You Don't Need jQuery + +前端发展很快,现代浏览器原生 API 已经足够好用。我们并不需要为了操作 DOM、Event 等再学习一下 jQuery 的 API。同时由于 React、Angular、Vue 等框架的流行,直接操作 DOM 不再是好的模式,jQuery 使用场景大大减少。本项目总结了大部分 jQuery API 替代的方法,暂时只支持 IE10+ 以上浏览器。 + +## 目录 + +1. [Query Selector](#query-selector) +1. [CSS & Style](#css--style) +1. [DOM Manipulation](#dom-manipulation) +1. [Ajax](#ajax) +1. [Events](#events) +1. [Utilities](#utilities) +1. [Alternatives](#alternatives) +1. [Translations](#translations) +1. [Browser Support](#browser-support) + +## Query Selector + +常用的 class、id、属性 选择器都可以使用 `document.querySelector` 或 `document.querySelectorAll` 替代。区别是 +* `document.querySelector` 返回第一个匹配的 Element +* `document.querySelectorAll` 返回所有匹配的 Element 组成的 NodeList。它可以通过 `[].slice.call()` 把它转成 Array +* 如果匹配不到任何 Element,jQuery 返回空数组 `[]`,但 `document.querySelector` 返回 `null`,注意空指针异常。当找不到时,也可以使用 `||` 设置默认的值,如 `document.querySelectorAll(selector) || []` + +> 注意:`document.querySelector` 和 `document.querySelectorAll` 性能很**差**。如果想提高性能,尽量使用 `document.getElementById`、`document.getElementsByClassName` 或 `document.getElementsByTagName`。 + +- [1.0](#1.0) Query by selector + + ```js + // jQuery + $('selector'); + + // Native + document.querySelectorAll('selector'); + ``` + +- [1.1](#1.1) Query by class + + ```js + // jQuery + $('.css'); + + // Native + document.querySelectorAll('.css'); + + // or + document.getElementsByClassName('css'); + ``` + +- [1.2](#1.2) Query by id + + ```js + // jQuery + $('#id'); + + // Native + document.querySelector('#id'); + + // or + document.getElementById('id'); + ``` + +- [1.3](#1.3) Query by attribute + + ```js + // jQuery + $('a[target=_blank]'); + + // Native + document.querySelectorAll('a[target=_blank]'); + ``` + +- [1.4](#1.4) Find sth. + + + Find nodes + + ```js + // jQuery + $el.find('li'); + + // Native + el.querySelectorAll('li'); + ``` + + + Find body + + ```js + // jQuery + $('body'); + + // Native + document.body; + ``` + + + Find Attribute + + ```js + // jQuery + $el.attr('foo'); + + // Native + e.getAttribute('foo'); + ``` + + + Find data attribute + + ```js + // jQuery + $el.data('foo'); + + // Native + // using getAttribute + el.getAttribute('data-foo'); + // you can also use `dataset` if only need to support IE 11+ + el.dataset['foo']; + ``` + +- [1.5](#1.5) Sibling/Previous/Next Elements + + + Sibling elements + + ```js + // jQuery + $el.siblings(); + + // Native + [].filter.call(el.parentNode.children, function(child) { + return child !== el; + }); + ``` + + + Previous elements + + ```js + // jQuery + $el.prev(); + + // Native + el.previousElementSibling; + + ``` + + + Next elements + + ```js + // next + $el.next(); + el.nextElementSibling; + ``` + +- [1.6](#1.6) Closest + + Closest 获得匹配选择器的第一个祖先元素,从当前元素开始沿 DOM 树向上。 + + ```js + // jQuery + $el.closest(queryString); + + // Native + function closest(el, selector) { + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + while (el) { + if (matchesSelector.call(el, selector)) { + return el; + } else { + el = el.parentElement; + } + } + return null; + } + ``` + +- [1.7](#1.7) Parents Until + + 获取当前每一个匹配元素集的祖先,不包括匹配元素的本身。 + + ```js + // jQuery + $el.parentsUntil(selector, filter); + + // Native + function parentsUntil(el, selector, filter) { + const result = []; + const matchesSelector = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector || el.msMatchesSelector; + + // match start from parent + el = el.parentElement; + while (el && !matchesSelector.call(el, selector)) { + if (!filter) { + result.push(el); + } else { + if (matchesSelector.call(el, filter)) { + result.push(el); + } + } + el = el.parentElement; + } + return result; + } + ``` + +- [1.8](#1.8) Form + + + Input/Textarea + + ```js + // jQuery + $('#my-input').val(); + + // Native + document.querySelector('#my-input').value; + ``` + + + Get index of e.currentTarget between `.radio` + + ```js + // jQuery + $(e.currentTarget).index('.radio'); + + // Native + [].indexOf.call(document.querySelectAll('.radio'), e.currentTarget); + ``` + +- [1.9](#1.9) Iframe Contents + + jQuery 对象的 iframe `contents()` 返回的是 iframe 内的 `document` + + + Iframe contents + + ```js + // jQuery + $iframe.contents(); + + // Native + iframe.contentDocument; + ``` + + + Iframe Query + + ```js + // jQuery + $iframe.contents().find('.css'); + + // Native + iframe.contentDocument.querySelectorAll('.css'); + ``` + +**[⬆ 回到顶部](#目录)** + +## CSS & Style + +- [2.1](#2.1) CSS + + + Get style + + ```js + // jQuery + $el.css("color"); + + // Native + // 注意:此处为了解决当 style 值为 auto 时,返回 auto 的问题 + const win = el.ownerDocument.defaultView; + // null 的意思是不返回伪类元素 + win.getComputedStyle(el, null).color; + ``` + + + Set style + + ```js + // jQuery + $el.css({ color: "#ff0011" }); + + // Native + el.style.color = '#ff0011'; + ``` + + + Get/Set Styles + + 注意,如果想一次设置多个 style,可以参考 oui-dom-utils 中 [setStyles](https://github.com/oneuijs/oui-dom-utils/blob/master/src/index.js#L194) 方法 + + + Add class + + ```js + // jQuery + $el.addClass(className); + + // Native + el.classList.add(className); + ``` + + + Remove class + + ```js + // jQuery + $el.removeClass(className); + + // Native + el.classList.remove(className); + ``` + + + has class + + ```js + // jQuery + $el.hasClass(className); + + // Native + el.classList.contains(className); + ``` + + + Toggle class + + ```js + // jQuery + $el.toggleClass(className); + + // Native + el.classList.toggle(className); + ``` + +- [2.2](#2.2) Width & Height + + Width 与 Height 获取方法相同,下面以 Height 为例: + + + Window height + + ```js + // jQuery + $(window).height(); + + // Native + // 不含 scrollbar,与 jQuery 行为一致 + window.document.documentElement.clientHeight; + // 含 scrollbar + window.innerHeight; + ``` + + + Document height + + ```js + // jQuery + $(document).height(); + + // Native + document.documentElement.scrollHeight; + ``` + + + Element height + + ```js + // jQuery + $el.height(); + + // Native + // 与 jQuery 一致(一直为 content 区域的高度) + function getHeight(el) { + const styles = this.getComputedStyles(el); + const height = el.offsetHeight; + const borderTopWidth = parseFloat(styles.borderTopWidth); + const borderBottomWidth = parseFloat(styles.borderBottomWidth); + const paddingTop = parseFloat(styles.paddingTop); + const paddingBottom = parseFloat(styles.paddingBottom); + return height - borderBottomWidth - borderTopWidth - paddingTop - paddingBottom; + } + // 精确到整数(border-box 时为 height 值,content-box 时为 height + padding + border 值) + el.clientHeight; + // 精确到小数(border-box 时为 height 值,content-box 时为 height + padding + border 值) + el.getBoundingClientRect().height; + ``` + + + Iframe height + + $iframe .contents() 方法返回 iframe 的 contentDocument + + ```js + // jQuery + $('iframe').contents().height(); + + // Native + iframe.contentDocument.documentElement.scrollHeight; + ``` + +- [2.3](#2.3) Position & Offset + + + Position + + ```js + // jQuery + $el.position(); + + // Native + { left: el.offsetLeft, top: el.offsetTop } + ``` + + + Offset + + ```js + // jQuery + $el.offset(); + + // Native + function getOffset (el) { + const box = el.getBoundingClientRect(); + + return { + top: box.top + window.pageYOffset - document.documentElement.clientTop, + left: box.left + window.pageXOffset - document.documentElement.clientLeft + } + } + ``` + +- [2.4](#2.4) Scroll Top + + ```js + // jQuery + $(window).scrollTop(); + + // Native + (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop; + ``` + +**[⬆ 回到顶部](#目录)** + +## DOM Manipulation + +- [3.1](#3.1) Remove + ```js + // jQuery + $el.remove(); + + // Native + el.parentNode.removeChild(el); + ``` + +- [3.2](#3.2) Text + + + Get text + + ```js + // jQuery + $el.text(); + + // Native + el.textContent; + ``` + + + Set text + + ```js + // jQuery + $el.text(string); + + // Native + el.textContent = string; + ``` + +- [3.3](#3.3) HTML + + + Get HTML + + ```js + // jQuery + $el.html(); + + // Native + el.innerHTML; + ``` + + + Set HTML + + ```js + // jQuery + $el.html(htmlString); + + // Native + el.innerHTML = htmlString; + ``` + +- [3.4](#3.4) Append + + Append 插入到子节点的末尾 + + ```js + // jQuery + $el.append("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.appendChild(newEl); + ``` + +- [3.5](#3.5) Prepend + + ```js + // jQuery + $el.prepend("
hello
"); + + // Native + let newEl = document.createElement('div'); + newEl.setAttribute('id', 'container'); + newEl.innerHTML = 'hello'; + el.insertBefore(newEl, el.firstChild); + ``` + +- [3.6](#3.6) insertBefore + + 在选中元素前插入新节点 + + ```js + // jQuery + $newEl.insertBefore(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target); + ``` + +- [3.7](#3.7) insertAfter + + 在选中元素后插入新节点 + + ```js + // jQuery + $newEl.insertAfter(queryString); + + // Native + const target = document.querySelector(queryString); + target.parentNode.insertBefore(newEl, target.nextSibling); + ``` + +**[⬆ 回到顶部](#目录)** + +## Ajax + +用 [fetch](https://github.com/camsong/fetch-ie8) 和 [fetch-jsonp](https://github.com/camsong/fetch-jsonp) 替代 + +**[⬆ 回到顶部](#目录)** + +## Events + +完整地替代命名空间和事件代理,链接到 https://github.com/oneuijs/oui-dom-events + +- [5.1](#5.1) Bind an event with on + + ```js + // jQuery + $el.on(eventName, eventHandler); + + // Native + el.addEventListener(eventName, eventHandler); + ``` + +- [5.2](#5.2) Unbind an event with off + + ```js + // jQuery + $el.off(eventName, eventHandler); + + // Native + el.removeEventListener(eventName, eventHandler); + ``` + +- [5.3](#5.3) Trigger + + ```js + // jQuery + $(el).trigger('custom-event', {key1: 'data'}); + + // Native + if (window.CustomEvent) { + const event = new CustomEvent('custom-event', {detail: {key1: 'data'}}); + } else { + const event = document.createEvent('CustomEvent'); + event.initCustomEvent('custom-event', true, true, {key1: 'data'}); + } + + el.dispatchEvent(event); + ``` + +**[⬆ 回到顶部](#目录)** + +## Utilities + +- [6.1](#6.1) isArray + + ```js + // jQuery + $.isArray(range); + + // Native + Array.isArray(range); + ``` + +- [6.2](#6.2) Trim + + ```js + // jQuery + $.trim(string); + + // Native + string.trim(); + ``` + +- [6.3](#6.3) Object Assign + + 继承,使用 object.assign polyfill https://github.com/ljharb/object.assign + + ```js + // jQuery + $.extend({}, defaultOpts, opts); + + // Native + Object.assign({}, defaultOpts, opts); + ``` + +- [6.4](#6.4) Contains + + ```js + // jQuery + $.contains(el, child); + + // Native + el !== child && el.contains(child); + ``` + +**[⬆ 回到顶部](#目录)** + +## Alternatives + +* [你可能不需要 jQuery (You Might Not Need jQuery)](http://youmightnotneedjquery.com/) - 如何使用原生 JavaScript 实现通用事件,元素,ajax 等用法。 +* [npm-dom](http://github.com/npm-dom) 以及 [webmodules](http://github.com/webmodules) - 在 NPM 上提供独立 DOM 模块的组织 + +## Translations + +* [한국어](./README.ko-KR.md) +* [简体中文](./README.zh-CN.md) +* [Bahasa Melayu](./README-my.md) +* [Bahasa Indonesia](./README-id.md) +* [Português(PT-BR)](./README.pt-BR.md) +* [Tiếng Việt Nam](./README-vi.md) +* [Español](./README-es.md) +* [Русский](./README-ru.md) +* [Türkçe](./README-tr.md) +* [Italian](./README-it.md) + +## Browser Support + +![Chrome](https://raw.github.com/alrra/browser-logos/master/chrome/chrome_48x48.png) | ![Firefox](https://raw.github.com/alrra/browser-logos/master/firefox/firefox_48x48.png) | ![IE](https://raw.github.com/alrra/browser-logos/master/internet-explorer/internet-explorer_48x48.png) | ![Opera](https://raw.github.com/alrra/browser-logos/master/opera/opera_48x48.png) | ![Safari](https://raw.github.com/alrra/browser-logos/master/safari/safari_48x48.png) +--- | --- | --- | --- | --- | +Latest ✔ | Latest ✔ | 10+ ✔ | Latest ✔ | 6.1+ ✔ | + +# License + +MIT diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/git.git b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/git.git new file mode 100644 index 0000000..c115085 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/git.git @@ -0,0 +1 @@ +gitdir: ../.git/modules/You-Dont-Need-jQuery diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/karma.conf.js b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/karma.conf.js new file mode 100644 index 0000000..f935ae4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/karma.conf.js @@ -0,0 +1,92 @@ +// Karma configuration +// Generated on Sun Nov 22 2015 22:10:47 GMT+0800 (CST) +require('babel-core/register'); + +module.exports = function(config) { + config.set({ + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '.', + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['mocha'], + + // list of files / patterns to load in the browser + files: [ + './test/**/*.spec.js' + ], + + // list of files to exclude + exclude: [ + ], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'test/**/*.spec.js': ['webpack', 'sourcemap'] + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + coverageReporter: { + reporters: [ + {type: 'text'}, + {type: 'html', dir: 'coverage'}, + ] + }, + + webpackMiddleware: { + stats: 'minimal' + }, + + webpack: { + cache: true, + devtool: 'inline-source-map', + module: { + loaders: [{ + test: /\.jsx?$/, + loader: 'babel-loader', + exclude: /node_modules/ + }], + postLoaders: [{ + test: /\.js/, + exclude: /(test|node_modules)/, + loader: 'istanbul-instrumenter' + }], + }, + resolve: { + extensions: ['', '.js', '.jsx'] + } + }, + + // web server port + port: 9876, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Firefox'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + // singleRun: false, + + // Concurrency level + // how many browser should be started simultanous + // concurrency: Infinity, + + // plugins: ['karma-phantomjs-launcher', 'karma-sourcemap-loader', 'karma-webpack'] + }) +} diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/package.json b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/package.json new file mode 100644 index 0000000..734e6ca --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/package.json @@ -0,0 +1,53 @@ +{ + "name": "You-Dont-Need-jQuery", + "version": "1.0.0", + "description": "Examples of how to do query, style, dom, ajax, event etc like jQuery with plain javascript.", + "scripts": { + "test": "karma start --single-run", + "tdd": "karma start --auto-watch --no-single-run", + "test-cov": "karma start --auto-watch --single-run --reporters progress,coverage", + "lint": "eslint src test" + }, + "dependencies": {}, + "devDependencies": { + "babel-cli": "^6.2.0", + "babel-core": "^6.1.21", + "babel-eslint": "^4.1.5", + "babel-loader": "^6.2.0", + "babel-preset-es2015": "^6.1.18", + "babel-preset-stage-0": "^6.1.18", + "chai": "^3.4.1", + "eslint": "^1.9.0", + "eslint-config-airbnb": "^1.0.0", + "eslint-plugin-react": "^3.10.0", + "isparta": "^4.0.0", + "istanbul-instrumenter-loader": "^0.1.3", + "jquery": "^2.1.4", + "karma": "^0.13.15", + "karma-coverage": "^0.5.3", + "karma-firefox-launcher": "^0.1.7", + "karma-mocha": "^0.2.1", + "karma-sourcemap-loader": "^0.3.6", + "karma-webpack": "^1.7.0", + "mocha": "^2.3.4", + "webpack": "^1.12.9" + }, + "repository": { + "type": "git", + "url": "https://github.com/oneuijs/You-Dont-Need-jQuery.git" + }, + "keywords": [ + "convertion guide", + "jQuery", + "es6", + "es2015", + "babel", + "OneUI Group" + ], + "author": "OneUI Group", + "license": "MIT", + "bugs": { + "url": "https://github.com/oneuijs/You-Dont-Need-jQuery/issues" + }, + "homepage": "https://github.com/oneuijs/You-Dont-Need-jQuery" +} diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/README.md b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/README.md new file mode 100644 index 0000000..f0f3768 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/README.md @@ -0,0 +1,13 @@ +# Test cases for all the tips + +## Usage + +run all tests once +``` +npm run test +``` + +run tests on TDD(Test Driven Development) mode +``` +npm run tdd +``` diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/css.spec.js b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/css.spec.js new file mode 100644 index 0000000..99248f2 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/css.spec.js @@ -0,0 +1 @@ +// test for CSS related \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/dom.spec.js b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/dom.spec.js new file mode 100644 index 0000000..5bfc287 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/dom.spec.js @@ -0,0 +1 @@ +// test for CSS and style related \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/query.spec.js b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/query.spec.js new file mode 100644 index 0000000..f6b9c30 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/query.spec.js @@ -0,0 +1,71 @@ +// tests for Query Selector related +import { expect } from 'chai'; +import $ from 'jquery'; + +describe('query selector', () => { + describe('basic', () => { + beforeEach(() => { + document.body.innerHTML = ` + + `; + }); + + afterEach(() => { + const el = document.querySelector('#query-selector-test1'); + el.parentNode.removeChild(el); + }); + + it('1.0 Query by selector', () => { + const $els = $('li.item[data-role="red"]'); + const els = document.querySelectorAll('li.item[data-role="red"]'); + + expect($els.length).to.equal(2); + [].forEach.call($els, function($el, i) { + expect($el).to.equal(els[i]); + }); + }); + + it('1.1 Query by class', () => { + const $els = $('.item'); + const els = document.getElementsByClassName('item'); + + [].forEach.call($els, function($el, i) { + expect($el).to.equal(els[i]); + }); + }); + + it('1.2 Query by id', () => { + expect($('#nested-ul')[0]).to.equal(document.getElementById('nested-ul')); + }); + + it('1.3 Query by attribute', () => { + const $els = $('[data-role="blue"]'); + const els = document.querySelectorAll('[data-role="blue"]'); + + expect($els.length).to.equal(2); + [].forEach.call($els, function($el, i) { + expect($el).to.equal(els[i]); + }); + }); + + it('1.4 Query in descendents', () => { + const $els = $('#query-selector-test1').find('.item'); + const els = document.getElementById('query-selector-test1').querySelectorAll('.item'); + + expect($els.length).to.equal(4); + [].forEach.call($els, function($el, i) { + expect($el).to.equal(els[i]); + }); + }); + }); +}); \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/utilities.spec.js b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/utilities.spec.js new file mode 100644 index 0000000..d68e723 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/You-Dont-Need-jQuery/test/utilities.spec.js @@ -0,0 +1 @@ +// test for Utilities related \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/git.git/COMMIT_EDITMSG b/spec/fixtures/repo-with-submodules/git.git/COMMIT_EDITMSG new file mode 100644 index 0000000..1963847 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/COMMIT_EDITMSG @@ -0,0 +1,9 @@ +submodules +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# On branch master +# Changes to be committed: +# new file: .gitmodules +# new file: You-Dont-Need-jQuery +# new file: jstips +# diff --git a/spec/fixtures/repo-with-submodules/git.git/HEAD b/spec/fixtures/repo-with-submodules/git.git/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/repo-with-submodules/git.git/config b/spec/fixtures/repo-with-submodules/git.git/config new file mode 100644 index 0000000..ab57cc5 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/config @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true +[submodule "jstips"] + url = https://github.com/loverajoel/jstips +[submodule "You-Dont-Need-jQuery"] + url = https://github.com/oneuijs/You-Dont-Need-jQuery diff --git a/spec/fixtures/repo-with-submodules/git.git/description b/spec/fixtures/repo-with-submodules/git.git/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/applypatch-msg.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/applypatch-msg.sample new file mode 100755 index 0000000..a5d7b84 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/commit-msg.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/commit-msg.sample new file mode 100755 index 0000000..b58d118 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/post-update.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/post-update.sample new file mode 100755 index 0000000..ec17ec1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/pre-applypatch.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-applypatch.sample new file mode 100755 index 0000000..4142082 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/pre-commit.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-commit.sample new file mode 100755 index 0000000..68d62d5 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/pre-push.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-push.sample new file mode 100755 index 0000000..6187dbf --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + : + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo >&2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/pre-rebase.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-rebase.sample new file mode 100755 index 0000000..9773ed4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/prepare-commit-msg.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..f093a02 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/spec/fixtures/repo-with-submodules/git.git/hooks/update.sample b/spec/fixtures/repo-with-submodules/git.git/hooks/update.sample new file mode 100755 index 0000000..d847583 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/index b/spec/fixtures/repo-with-submodules/git.git/index new file mode 100644 index 0000000..20f2cdc Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/index differ diff --git a/spec/fixtures/repo-with-submodules/git.git/info/exclude b/spec/fixtures/repo-with-submodules/git.git/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/spec/fixtures/repo-with-submodules/git.git/logs/HEAD b/spec/fixtures/repo-with-submodules/git.git/logs/HEAD new file mode 100644 index 0000000..d41c1a1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/logs/HEAD @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 d3e073baf592c56614c68ead9e2cd0a3880140cd joshaber 1452185922 -0500 commit (initial): first +d3e073baf592c56614c68ead9e2cd0a3880140cd d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 joshaber 1452186239 -0500 commit: submodules diff --git a/spec/fixtures/repo-with-submodules/git.git/logs/refs/heads/master b/spec/fixtures/repo-with-submodules/git.git/logs/refs/heads/master new file mode 100644 index 0000000..d41c1a1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 d3e073baf592c56614c68ead9e2cd0a3880140cd joshaber 1452185922 -0500 commit (initial): first +d3e073baf592c56614c68ead9e2cd0a3880140cd d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 joshaber 1452186239 -0500 commit: submodules diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/COMMIT_EDITMSG b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/COMMIT_EDITMSG new file mode 100644 index 0000000..2ce36ac --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/COMMIT_EDITMSG @@ -0,0 +1,9 @@ +whitespace is nicespace +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# On branch master +# Your branch is up-to-date with 'origin/master'. +# +# Changes to be committed: +# modified: README.md +# diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/ORIG_HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/ORIG_HEAD new file mode 100644 index 0000000..b7cdcfd --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/ORIG_HEAD @@ -0,0 +1 @@ +2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/config b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/config new file mode 100644 index 0000000..6ce8d21 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/config @@ -0,0 +1,14 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + worktree = ../../../You-Dont-Need-jQuery + ignorecase = true + precomposeunicode = true +[remote "origin"] + url = https://github.com/oneuijs/You-Dont-Need-jQuery + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/description b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/description new file mode 100644 index 0000000..498b267 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/gitdir b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/gitdir new file mode 100644 index 0000000..6b8710a --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/gitdir @@ -0,0 +1 @@ +.git diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/applypatch-msg.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/applypatch-msg.sample new file mode 100755 index 0000000..a5d7b84 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/applypatch-msg.sample @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +commitmsg="$(git rev-parse --git-path hooks/commit-msg)" +test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} +: diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/commit-msg.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/commit-msg.sample new file mode 100755 index 0000000..b58d118 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/commit-msg.sample @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/post-update.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/post-update.sample new file mode 100755 index 0000000..ec17ec1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-applypatch.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-applypatch.sample new file mode 100755 index 0000000..4142082 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-commit.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-commit.sample new file mode 100755 index 0000000..68d62d5 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-push.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-push.sample new file mode 100755 index 0000000..6187dbf --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + : + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo >&2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-rebase.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-rebase.sample new file mode 100755 index 0000000..9773ed4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/prepare-commit-msg.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..f093a02 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/update.sample b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/update.sample new file mode 100755 index 0000000..d847583 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/index b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/index new file mode 100644 index 0000000..af63d76 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/index differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/info/exclude b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/HEAD new file mode 100644 index 0000000..8b2542f --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/HEAD @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a joshaber 1452186236 -0500 clone: from https://github.com/oneuijs/You-Dont-Need-jQuery +2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a a78b35a896b890f0a2a4f1f924c5739776415250 joshaber 1452202510 -0500 commit: whitespace is nicespace diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/heads/master b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/heads/master new file mode 100644 index 0000000..8b2542f --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/heads/master @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a joshaber 1452186236 -0500 clone: from https://github.com/oneuijs/You-Dont-Need-jQuery +2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a a78b35a896b890f0a2a4f1f924c5739776415250 joshaber 1452202510 -0500 commit: whitespace is nicespace diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/remotes/origin/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/remotes/origin/HEAD new file mode 100644 index 0000000..a76a6d2 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 2e9bbc77d60f20eb462ead5b2ac7405b62b9b90a joshaber 1452186236 -0500 clone: from https://github.com/oneuijs/You-Dont-Need-jQuery diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/objects/a7/8b35a896b890f0a2a4f1f924c5739776415250 b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/objects/a7/8b35a896b890f0a2a4f1f924c5739776415250 new file mode 100644 index 0000000..756c94c --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/You-Dont-Need-jQuery/objects/a7/8b35a896b890f0a2a4f1f924c5739776415250 @@ -0,0 +1,2 @@ +xQ +0D)remDfk#)m73aF\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/post-update.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/post-update.sample new file mode 100755 index 0000000..ec17ec1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/post-update.sample @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-applypatch.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-applypatch.sample new file mode 100755 index 0000000..4142082 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-applypatch.sample @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +precommit="$(git rev-parse --git-path hooks/pre-commit)" +test -x "$precommit" && exec "$precommit" ${1+"$@"} +: diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-commit.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-commit.sample new file mode 100755 index 0000000..68d62d5 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-commit.sample @@ -0,0 +1,49 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ASCII filenames set this variable to true. +allownonascii=$(git config --bool hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ASCII filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + cat <<\EOF +Error: Attempt to add a non-ASCII file name. + +This can cause problems if you want to work with people on other platforms. + +To be portable it is advisable to rename the file. + +If you know what you are doing you can disable this check using: + + git config hooks.allownonascii true +EOF + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against -- diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-push.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-push.sample new file mode 100755 index 0000000..6187dbf --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-push.sample @@ -0,0 +1,53 @@ +#!/bin/sh + +# An example hook script to verify what is about to be pushed. Called by "git +# push" after it has checked the remote status, but before anything has been +# pushed. If this script exits with a non-zero status nothing will be pushed. +# +# This hook is called with the following parameters: +# +# $1 -- Name of the remote to which the push is being done +# $2 -- URL to which the push is being done +# +# If pushing without using a named remote those arguments will be equal. +# +# Information about the commits which are being pushed is supplied as lines to +# the standard input in the form: +# +# +# +# This sample shows how to prevent push of commits where the log message starts +# with "WIP" (work in progress). + +remote="$1" +url="$2" + +z40=0000000000000000000000000000000000000000 + +while read local_ref local_sha remote_ref remote_sha +do + if [ "$local_sha" = $z40 ] + then + # Handle delete + : + else + if [ "$remote_sha" = $z40 ] + then + # New branch, examine all commits + range="$local_sha" + else + # Update to existing branch, examine new commits + range="$remote_sha..$local_sha" + fi + + # Check for WIP commit + commit=`git rev-list -n 1 --grep '^WIP' "$range"` + if [ -n "$commit" ] + then + echo >&2 "Found WIP commit in $local_ref, not pushing" + exit 1 + fi + fi +done + +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-rebase.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-rebase.sample new file mode 100755 index 0000000..9773ed4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/pre-rebase.sample @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +exit 0 + +################################################################ + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/prepare-commit-msg.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/prepare-commit-msg.sample new file mode 100755 index 0000000..f093a02 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/prepare-commit-msg.sample @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/update.sample b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/update.sample new file mode 100755 index 0000000..d847583 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/hooks/update.sample @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 )" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "usage: $0 " >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0 diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/index b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/index new file mode 100644 index 0000000..bb46086 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/index differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/info/exclude b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/info/exclude new file mode 100644 index 0000000..a5196d1 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/HEAD new file mode 100644 index 0000000..4dc0ab4 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/HEAD @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 9f0218b7652b622afea799a4723490c43e1af1fe joshaber 1452186187 -0500 clone: from https://github.com/loverajoel/jstips +9f0218b7652b622afea799a4723490c43e1af1fe 9f0218b7652b622afea799a4723490c43e1af1fe joshaber 1452201852 -0500 checkout: moving from master to test +9f0218b7652b622afea799a4723490c43e1af1fe 0525ef667328cb1f86b1ddf523db4a064e1590fa joshaber 1452201881 -0500 commit: whitespace is nicespace diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/master b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/master new file mode 100644 index 0000000..e87df84 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/master @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 9f0218b7652b622afea799a4723490c43e1af1fe joshaber 1452186187 -0500 clone: from https://github.com/loverajoel/jstips diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/test b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/test new file mode 100644 index 0000000..97fc9fe --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/heads/test @@ -0,0 +1,2 @@ +0000000000000000000000000000000000000000 9f0218b7652b622afea799a4723490c43e1af1fe joshaber 1452201852 -0500 branch: Created from HEAD +9f0218b7652b622afea799a4723490c43e1af1fe 0525ef667328cb1f86b1ddf523db4a064e1590fa joshaber 1452201881 -0500 commit: whitespace is nicespace diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/remotes/origin/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/remotes/origin/HEAD new file mode 100644 index 0000000..e87df84 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/logs/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 9f0218b7652b622afea799a4723490c43e1af1fe joshaber 1452186187 -0500 clone: from https://github.com/loverajoel/jstips diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/05/25ef667328cb1f86b1ddf523db4a064e1590fa b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/05/25ef667328cb1f86b1ddf523db4a064e1590fa new file mode 100644 index 0000000..4535652 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/05/25ef667328cb1f86b1ddf523db4a064e1590fa @@ -0,0 +1,2 @@ +x] }{ ,?bliZחݷ7IuK6fQ`u8h2RVlI dY2 2͊,ݦEzc:@( +}K{gQ|x_)1~F|QBI \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/1a/dd860234dad4a8bf59340363e9c88bb0457cb7 b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/1a/dd860234dad4a8bf59340363e9c88bb0457cb7 new file mode 100644 index 0000000..e523c5a Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/1a/dd860234dad4a8bf59340363e9c88bb0457cb7 differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/5b/35953562dbb4f2debe88fcc9ac1805064e1e5a b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/5b/35953562dbb4f2debe88fcc9ac1805064e1e5a new file mode 100644 index 0000000..3065a65 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/5b/35953562dbb4f2debe88fcc9ac1805064e1e5a differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.idx b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.idx new file mode 100644 index 0000000..bc142e9 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.idx differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.pack b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.pack new file mode 100644 index 0000000..75c5b19 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/objects/pack/pack-e568a55e02b6b7b75582924204669e4f3ed5276f.pack differ diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/packed-refs b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/packed-refs new file mode 100644 index 0000000..1ffeea8 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/packed-refs @@ -0,0 +1,2 @@ +# pack-refs with: peeled fully-peeled +9f0218b7652b622afea799a4723490c43e1af1fe refs/remotes/origin/master diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/master b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/master new file mode 100644 index 0000000..6f5160b --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/master @@ -0,0 +1 @@ +9f0218b7652b622afea799a4723490c43e1af1fe diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/test b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/test new file mode 100644 index 0000000..71704a9 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/heads/test @@ -0,0 +1 @@ +0525ef667328cb1f86b1ddf523db4a064e1590fa diff --git a/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/remotes/origin/HEAD b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/remotes/origin/HEAD new file mode 100644 index 0000000..6efe28f --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/modules/jstips/refs/remotes/origin/HEAD @@ -0,0 +1 @@ +ref: refs/remotes/origin/master diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/3e/2fe2f8226faab789f70d6d8a7231e4f2bd54df b/spec/fixtures/repo-with-submodules/git.git/objects/3e/2fe2f8226faab789f70d6d8a7231e4f2bd54df new file mode 100644 index 0000000..c4e87d5 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/3e/2fe2f8226faab789f70d6d8a7231e4f2bd54df differ diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/40/f4e79926a85134d4c905d04e70573b6616f3bc b/spec/fixtures/repo-with-submodules/git.git/objects/40/f4e79926a85134d4c905d04e70573b6616f3bc new file mode 100644 index 0000000..18f4ee3 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/40/f4e79926a85134d4c905d04e70573b6616f3bc differ diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/50/b89367d8f0acd312ef5aa012eac20a75c91351 b/spec/fixtures/repo-with-submodules/git.git/objects/50/b89367d8f0acd312ef5aa012eac20a75c91351 new file mode 100644 index 0000000..7abb776 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/50/b89367d8f0acd312ef5aa012eac20a75c91351 differ diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/54/3b9bebdc6bd5c4b22136034a95dd097a57d3dd b/spec/fixtures/repo-with-submodules/git.git/objects/54/3b9bebdc6bd5c4b22136034a95dd097a57d3dd new file mode 100644 index 0000000..b57931d Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/54/3b9bebdc6bd5c4b22136034a95dd097a57d3dd differ diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/d2/b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 b/spec/fixtures/repo-with-submodules/git.git/objects/d2/b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 new file mode 100644 index 0000000..b0a2530 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/d2/b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 differ diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/d3/e073baf592c56614c68ead9e2cd0a3880140cd b/spec/fixtures/repo-with-submodules/git.git/objects/d3/e073baf592c56614c68ead9e2cd0a3880140cd new file mode 100644 index 0000000..61165ee --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/objects/d3/e073baf592c56614c68ead9e2cd0a3880140cd @@ -0,0 +1 @@ +xA E]sJbL0h1MݽZe2z@r̒< %Ljzm] $i+olç[q|o'GhIk5י?G*}5 \ No newline at end of file diff --git a/spec/fixtures/repo-with-submodules/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/spec/fixtures/repo-with-submodules/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000..7112238 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/spec/fixtures/repo-with-submodules/git.git/refs/heads/master b/spec/fixtures/repo-with-submodules/git.git/refs/heads/master new file mode 100644 index 0000000..3507a23 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/git.git/refs/heads/master @@ -0,0 +1 @@ +d2b0ad9cbc6f6c4372e8956e5cc5af771b2342e5 diff --git a/spec/fixtures/repo-with-submodules/jstips/CONTRIBUTING.md b/spec/fixtures/repo-with-submodules/jstips/CONTRIBUTING.md new file mode 100644 index 0000000..e4f33e8 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/jstips/CONTRIBUTING.md @@ -0,0 +1,20 @@ +# How to submit your tip +To submit a tip to the list, fork the repository and add your tip to the top of the list of tips in the `README.md` file. You may want to use a [topic branch](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) for each tip. + +Use the format below when writing your tip. Your tip should be readable in less than two minutes. You may add links to other sites or videos that give more insight if you wish. + +Once your tip is ready, [issue a pull request](https://help.github.com/articles/using-pull-requests/) and your tip will be reviewed. Every day one tip will be merged from the available pull requests. + +# Tip format + +## #01(number) - Title + +> yyyy-mm-dd(date) by @username + +This is my awesome tip! + +# Notes + +Leave the date and the tip number empty. When we decide merge the pull request you will add them and squash your commits. + +Remember: New tips must be added to the *top* of the list of [tips](https://github.com/loverajoel/jstips#tips-list) in the `README.md` file. diff --git a/spec/fixtures/repo-with-submodules/jstips/README.md b/spec/fixtures/repo-with-submodules/jstips/README.md new file mode 100644 index 0000000..1add860 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/jstips/README.md @@ -0,0 +1,324 @@ +![header](https://raw.githubusercontent.com/loverajoel/jstips/master/resources/jstips-header-blog.gif) + + +# Introducing Javascript Tips +> New year, new project. **A JS tip per day!** + +With great excitement, I introduce these short and useful daily Javascript tips that will allow you to improve your code writing. With less than 2 minutes each day, you will be able to read about performance, frameworks, conventions, hacks, interview questions and all the items that the future of this awesome language holds for us. + +At midday, no matter if it is a weekend or a holiday, a tip will be posted and tweeted. + +### Can you help us enrich it? +Please feel free to send us a PR with your own Javascript tip to be published here. +Any improvements or suggestions are more than welcome! +[Click to see the instructions](https://github.com/loverajoel/jstips/blob/master/CONTRIBUTING.md) + +### Let’s keep in touch +To get updates, watch the repo and follow the [Twitter account](https://twitter.com/tips_js), only one tweet will be sent per day. It is a deal! +> Don't forget to Star the repo, as this will help to promote the project! + +# Tips list + +## #06 - Writing a single method for arrays or single elements + +> 2016-01-06 by [@mattfxyz](https://twitter.com/mattfxyz) + +Rather than writing separate methods to handle an array and a single element parameter, write your functions so they can handle both. This is similar to how some of jQuery's functions work (`css` will modify everything matched by the selector). + +You just have to concat everything into an array first. `Array.concat` will accept an array or a single element. + +```javascript +function printUpperCase(words) { + var elements = [].concat(words); + for (var i = 0; i < elements.length; i++) { + console.log(elements[i].toUpperCase()); + } +} +``` + +`printUpperCase` is now ready to accept a single node or an array of nodes as it's parameter. + +```javascript +printUpperCase("cactus"); +// => CACTUS +printUpperCase(["cactus", "bear", "potato"]); +// => CACTUS +// BEAR +// POTATO +``` + +## #05 - Differences between `undefined` and `null` + +> 2016-01-05 by [@loverajoel](https://twitter.com/loverajoel) + +- `undefined` means a variable has not been declared, or has been declared but has not yet been assigned a value +- `null` is an assignment value that means "no value" +- Javascript sets unassigned variables with a default value of `undefined` +- Javascript never sets a value to `null`. It is used by programmers to indicate that a `var` has no value. +- `undefined` is not valid in JSON while `null` is +- `undefined` typeof is `undefined` +- `null` typeof is an `object` +- Both are primitives +- You can know if a variable is [undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined) + + ```javascript + typeof variable === "undefined" +``` +- You can check if a variable is [null](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null) + + ```javascript + variable === null +``` +- The **equality** operator considers them equal, but the **identity** doesn't + + ```javascript + null == undefined // true + + null === undefined // false +``` + +## #04 - Sorting strings with accented characters + +> 2016-01-04 by [@loverajoel](https://twitter.com/loverajoel) + +Javascript has a native method **[sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)** that allows sorting arrays. Doing a simple `array.sort()` will treat each array entry as a string and sort it alphabetically. Also you can provide your [own custom sorting](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Parameters) function. + +```javascript +['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); +// ["Buenos Aires", "Mumbai", "New York", "Shanghai"] +``` + +But when you try order an array of non ASCII characters like this `['é', 'a', 'ú', 'c']`, you will obtain a strange result `['c', 'e', 'á', 'ú']`. That happens because sort works only with english language. + +See the next example: + +```javascript +// Spanish +['único','árbol', 'cosas', 'fútbol'].sort(); +// ["cosas", "fútbol", "árbol", "único"] // bad order + +// German +['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); +// ["Wann", "Woche", "wäre", "wöchentlich"] // bad order +``` + +Fortunately, there are two ways to overcome this behavior [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) and [Intl.Collator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Collator) provided by ECMAScript Internationalization API. + +> Both methods have their own custom parameters in order to configure it to work adequately. + +### Using `localeCompare()` + +```javascript +['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { + return a.localeCompare(b); +}); +// ["árbol", "cosas", "fútbol", "único"] + +['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { + return a.localeCompare(b); +}); +// ["Wann", "wäre", "Woche", "wöchentlich"] +``` + +### Using `Intl.Collator()` + +```javascript +['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); +// ["árbol", "cosas", "fútbol", "único"] + +['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); +// ["Wann", "wäre", "Woche", "wöchentlich"] +``` + +- For each method you can customize the location. +- According to [Firefox](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare#Performance) Intl.Collator is faster when comparing large numbers of strings. + +So when you are working with arrays of strings in a language other than English, remember to use this method to avoid unexpected sorting. + +## #03 - Improve Nested Conditionals +> 2016-01-03 by [AlbertoFuente](https://github.com/AlbertoFuente) + +How can we improve and make more efficient nested `if` statement on javascript. + +```javascript +if (color) { + if (color === 'black') { + printBlackBackground(); + } else if (color === 'red') { + printRedBackground(); + } else if (color === 'blue') { + printBlueBackground(); + } else if (color === 'green') { + printGreenBackground(); + } else { + printYellowBackground(); + } +} +``` + +One way to improve the nested `if` statement would be using the `switch` statement. Although it is less verbose and is more ordered, It's not recommended to use it because it's so difficult to debug errors, here's [why](https://toddmotto.com/deprecating-the-switch-statement-for-object-literals/). + +```javascript +switch(color) { + case 'black': + printBlackBackground(); + break; + case 'red': + printRedBackground(); + break; + case 'blue': + printBlueBackground(); + break; + case 'green': + printGreenBackground(); + break; + default: + printYellowBackground(); +} +``` + +But what if we have a conditional with several checks in each statement? In this case, if we like to do less verbose and more ordered, we can use the conditional `switch`. +If we pass `true` as parameter to the `switch` statement, It allows us to put a conditional in each case. + +```javascript +switch(true) { + case (typeof color === 'string' && color === 'black'): + printBlackBackground(); + break; + case (typeof color === 'string' && color === 'red'): + printRedBackground(); + break; + case (typeof color === 'string' && color === 'blue'): + printBlueBackground(); + break; + case (typeof color === 'string' && color === 'green'): + printGreenBackground(); + break; + case (typeof color === 'string' && color === 'yellow'): + printYellowBackground(); + break; +} +``` + +But we must always avoid having several checks in every condition, avoiding use of `switch` as far as possible and take into account that the most efficient way to do this is through an `object`. + +```javascript +var colorObj = { + 'black': printBlackBackground, + 'red': printRedBackground, + 'blue': printBlueBackground, + 'green': printGreenBackground, + 'yellow': printYellowBackground +}; + +if (color && colorObj.hasOwnProperty(color)) { + colorObj[color](); +} +``` + +Here you can find more information about [this](http://www.nicoespeon.com/en/2015/01/oop-revisited-switch-in-js/). + +## #02 - ReactJs - Keys in children components are important + +> 2016-01-02 by [@loverajoel](https://twitter.com/loverajoel) + + +The [key](https://facebook.github.io/react/docs/multiple-components.html#dynamic-children) is an attribute that you must pass to all components created dynamically from an array. It's unique and constant id that React use for identify each component in the DOM and know that it's a different component and not the same one. Using keys will ensure that the child component is preserved and not recreated and prevent that weird things happens. + +> Key is not really about performance, it's more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity [Paul O’Shannessy](https://github.com/facebook/react/issues/1342#issuecomment-39230939) + +- Use an exisiting unique value of the object. +- Define the keys in the parent components, not in child components + + ```javascript + //bad + ... + render() { +
{{item.name}}
+ } + ... + + //good + + ``` +- [Use the array index is a bad practice.](https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318#.76co046o9) +- `random()` will not work + + ```javascript + //bad + + ``` + +- You can create your own unique id, be sure that the method be fast and attach it to your object. +- When the amount of child are big or involve expensive components, use keys has performance improvements. +- [You must provide the key attribute for all children of ReactCSSTransitionGroup.](http://docs.reactjs-china.com/react/docs/animation.html) + +## #1 - AngularJs: `$digest` vs `$apply` + +> 2016-01-01 by [@loverajoel](https://twitter.com/loverajoel) + +One of the most appreciated features of AngularJs is the two way data binding. In order to make this work AngularJs evaluate the changes between the model and the view through of cycles(`$digest`). You need to understand this concept in order to understand how the framework works under the hood. + +Angular evaluate each watcher whenever one event was fired, this is the known `$digest` cycle. +Sometimes you have to force to run a new cycle manually and you must choose the correct option because this phase is one of the most influential in terms of performance. + +### `$apply` +This core method lets you to start the digestion cycle explicitly, that means that all watchers are checked, the entire application starts the `$digest loop`. Internally after execute an optional function parameter, call internally to `$rootScope.$digest();`. + +### `$digest` +In this case the `$digest` method starts the `$digest` cycle for the current scope and its children. You should notice that the parents scopes will not be checked + and not be affected. + +### Recommendations +- Use `$apply` or `$digest` only when browser DOM events have triggered outside of AngularJS. +- Pass a function expression to `$apply`, this have a error handling mechanism and allow integrate changes in the digest cycle + + ```javascript + $scope.$apply(() => { + $scope.tip = 'Javascript Tip'; + }); + ``` + +- If only needs update the current scope or its children use `$digest`, and prevent a new digest cycle for the whole application. The performance benefit it's self evident +- `$apply()` is hard process for the machine and can lead to performance issues when having a lot of binding. +- If you are using >AngularJS 1.2.X, use `$evalAsync` is a core method that will evaluate the expression during the current cycle or the next. This can improve your application's performance. + + +## #0 - Insert item inside an Array +> 2015-12-29 + +Insert an item into an existing array is a daily common task. You can add elements to the end of an array using push, to the beginning using unshift, or the middle using splice. + +But those are known methods, doesn't mean there isn't a more performant way, here we go... + +Add a element at the end of the array is easy with push(), but there is a way more performant. + +```javascript +var arr = [1,2,3,4,5]; + +arr.push(6); +arr[arr.length] = 6; // 43% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1 +``` +Both methods modify the original array. Don't believe me? Check the [jsperf](http://jsperf.com/push-item-inside-an-array) + +Now we are trying to add an item to the beginning of the array + +```javascript +var arr = [1,2,3,4,5]; + +arr.unshift(0); +[0].concat(arr); // 98% faster in Chrome 47.0.2526.106 on Mac OS X 10.11.1 +``` +Here is a little bit detail, unshift edit the original array, concat return a new array. [jsperf](http://jsperf.com/unshift-item-inside-an-array) + +Add items at the middle of an array is easy with splice and is the most performant way to do it. + +```javascript +var items = ['one', 'two', 'three', 'four']; +items.splice(items.length / 2, 0, 'hello'); +``` + +I tried to run these tests in various Browsers and OS and the results were similar. I hope this tips will be useful for you and encourage to perform your own tests! + +### License +[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png)](http://creativecommons.org/publicdomain/zero/1.0/) diff --git a/spec/fixtures/repo-with-submodules/jstips/git.git b/spec/fixtures/repo-with-submodules/jstips/git.git new file mode 100644 index 0000000..c07b090 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/jstips/git.git @@ -0,0 +1 @@ +gitdir: ../.git/modules/jstips diff --git a/spec/fixtures/repo-with-submodules/jstips/resources/jstips-header-blog.gif b/spec/fixtures/repo-with-submodules/jstips/resources/jstips-header-blog.gif new file mode 100644 index 0000000..10f1d49 Binary files /dev/null and b/spec/fixtures/repo-with-submodules/jstips/resources/jstips-header-blog.gif differ diff --git a/spec/fixtures/repo-with-submodules/jstips/resources/log.js b/spec/fixtures/repo-with-submodules/jstips/resources/log.js new file mode 100644 index 0000000..39b2f02 --- /dev/null +++ b/spec/fixtures/repo-with-submodules/jstips/resources/log.js @@ -0,0 +1,3 @@ +(() => { + console.log('Hello world'); +})(); \ No newline at end of file diff --git a/spec/fixtures/working-dir/.gitignore b/spec/fixtures/working-dir/.gitignore new file mode 100644 index 0000000..23238ea --- /dev/null +++ b/spec/fixtures/working-dir/.gitignore @@ -0,0 +1,2 @@ +poop +ignored.txt diff --git a/spec/fixtures/working-dir/a.txt b/spec/fixtures/working-dir/a.txt new file mode 100644 index 0000000..e69de29 diff --git a/spec/fixtures/working-dir/git.git/HEAD b/spec/fixtures/working-dir/git.git/HEAD new file mode 100644 index 0000000..cb089cd --- /dev/null +++ b/spec/fixtures/working-dir/git.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/spec/fixtures/working-dir/git.git/config b/spec/fixtures/working-dir/git.git/config new file mode 100644 index 0000000..af10792 --- /dev/null +++ b/spec/fixtures/working-dir/git.git/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true diff --git a/spec/fixtures/working-dir/git.git/index b/spec/fixtures/working-dir/git.git/index new file mode 100644 index 0000000..e49f052 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/index differ diff --git a/spec/fixtures/working-dir/git.git/objects/06/15f9a45968b3515e0a202530ef9f61aba26b6c b/spec/fixtures/working-dir/git.git/objects/06/15f9a45968b3515e0a202530ef9f61aba26b6c new file mode 100644 index 0000000..8a3a49f Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/06/15f9a45968b3515e0a202530ef9f61aba26b6c differ diff --git a/spec/fixtures/working-dir/git.git/objects/16/735fb793d7b038818219c4b8c6295346e20eef b/spec/fixtures/working-dir/git.git/objects/16/735fb793d7b038818219c4b8c6295346e20eef new file mode 100644 index 0000000..e5d8eb9 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/16/735fb793d7b038818219c4b8c6295346e20eef differ diff --git a/spec/fixtures/working-dir/git.git/objects/52/f56457b6fca045ce41bb9d32e6ca79d23192af b/spec/fixtures/working-dir/git.git/objects/52/f56457b6fca045ce41bb9d32e6ca79d23192af new file mode 100644 index 0000000..03280f1 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/52/f56457b6fca045ce41bb9d32e6ca79d23192af differ diff --git a/spec/fixtures/working-dir/git.git/objects/5b/24ab4c3baadf534242b1acc220c8fa051b9b20 b/spec/fixtures/working-dir/git.git/objects/5b/24ab4c3baadf534242b1acc220c8fa051b9b20 new file mode 100644 index 0000000..27cfeb8 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/5b/24ab4c3baadf534242b1acc220c8fa051b9b20 differ diff --git a/spec/fixtures/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 b/spec/fixtures/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 new file mode 100644 index 0000000..ba1f06f Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 differ diff --git a/spec/fixtures/working-dir/git.git/objects/66/dc9051da651c15d98d017a88658263cab28f02 b/spec/fixtures/working-dir/git.git/objects/66/dc9051da651c15d98d017a88658263cab28f02 new file mode 100644 index 0000000..8e13deb Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/66/dc9051da651c15d98d017a88658263cab28f02 differ diff --git a/spec/fixtures/working-dir/git.git/objects/8a/9c86f1cb1f14b8f436eb91f4b052c8802ca99e b/spec/fixtures/working-dir/git.git/objects/8a/9c86f1cb1f14b8f436eb91f4b052c8802ca99e new file mode 100644 index 0000000..62c86e4 --- /dev/null +++ b/spec/fixtures/working-dir/git.git/objects/8a/9c86f1cb1f14b8f436eb91f4b052c8802ca99e @@ -0,0 +1 @@ +xInB1)σf vbcQne=, ķ9r&V!quH.)i6Ί|tu޺}-(+lUΉf_pUQIcxˀxo`wz}~ulmY(+QJ vN1iHL \ No newline at end of file diff --git a/spec/fixtures/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/spec/fixtures/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000..7112238 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/spec/fixtures/working-dir/git.git/objects/ec/5e386905ff2d36e291086a1207f2585aaa8920 b/spec/fixtures/working-dir/git.git/objects/ec/5e386905ff2d36e291086a1207f2585aaa8920 new file mode 100644 index 0000000..4010ee8 Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/ec/5e386905ff2d36e291086a1207f2585aaa8920 differ diff --git a/spec/fixtures/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 b/spec/fixtures/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 new file mode 100644 index 0000000..eaf6eef Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/ef/046e9eecaa5255ea5e9817132d4001724d6ae1 differ diff --git a/spec/fixtures/working-dir/git.git/objects/fe/bde178cdf35e9df6279d87aa27590c6d92e354 b/spec/fixtures/working-dir/git.git/objects/fe/bde178cdf35e9df6279d87aa27590c6d92e354 new file mode 100644 index 0000000..fde11be Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/fe/bde178cdf35e9df6279d87aa27590c6d92e354 differ diff --git a/spec/fixtures/working-dir/git.git/objects/ff/c8218bd2240a0cb92f6f02548d45784428349b b/spec/fixtures/working-dir/git.git/objects/ff/c8218bd2240a0cb92f6f02548d45784428349b new file mode 100644 index 0000000..919f08d Binary files /dev/null and b/spec/fixtures/working-dir/git.git/objects/ff/c8218bd2240a0cb92f6f02548d45784428349b differ diff --git a/spec/fixtures/working-dir/git.git/refs/heads/master b/spec/fixtures/working-dir/git.git/refs/heads/master new file mode 100644 index 0000000..85900d5 --- /dev/null +++ b/spec/fixtures/working-dir/git.git/refs/heads/master @@ -0,0 +1 @@ +8a9c86f1cb1f14b8f436eb91f4b052c8802ca99e