diff --git a/core/COPYRIGHT.txt b/core/COPYRIGHT.txt
index 618e3de..4acb710 100644
--- a/core/COPYRIGHT.txt
+++ b/core/COPYRIGHT.txt
@@ -22,8 +22,6 @@ license, including:
 
 Javascript
 
-  Farbtastic - Copyright (c) 2010 Matt Farina
-
   HTML5 Shiv - Copyright (c) 2012  Alexander Farkas, Jonathan Neal, Paul Irish,
     and John-David Dalton
 
@@ -42,10 +40,10 @@ Javascript
   jQuery Mousewheel - Copyright (c) 2010 Brandon Aaron
     (http://brandonaaron.net)
 
-  jQuery Metadata - Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer,
+  jQuery Metadata - Copyright (c) 2006 John Resig, Yehuda Katz, J�rn Zaefferer,
     Paul McLanahan
 
-  jQuery Once - Copyright (c) 2009 Konstantin Käfer
+  jQuery Once - Copyright (c) 2009 Konstantin K�fer
 
   jQuery UI - Copyright (c) 2012 by the original authors
     (http://jqueryui.com/about)
diff --git a/core/assets/vendor/farbtastic/farbtastic.css b/core/assets/vendor/farbtastic/farbtastic.css
deleted file mode 100644
index 25a68eb..0000000
--- a/core/assets/vendor/farbtastic/farbtastic.css
+++ /dev/null
@@ -1,36 +0,0 @@
-
-.farbtastic {
-  position: relative;
-}
-.farbtastic * {
-  position: absolute;
-  cursor: crosshair;
-}
-.farbtastic,
-.farbtastic .wheel {
-  width: 195px;
-  height: 195px;
-}
-.farbtastic .color,
-.farbtastic .overlay {
-  top: 47px;
-  left: 47px;
-  width: 101px;
-  height: 101px;
-}
-.farbtastic .wheel {
-  background: url(wheel.png) no-repeat;
-  width: 195px;
-  height: 195px;
-}
-.farbtastic .overlay {
-  background: url(mask.png) no-repeat;
-}
-.farbtastic .marker {
-  width: 17px;
-  height: 17px;
-  margin: -8px 0 0 -8px;
-  overflow: hidden;
-  background: url(marker.png) no-repeat;
-}
-
diff --git a/core/assets/vendor/farbtastic/farbtastic.js b/core/assets/vendor/farbtastic/farbtastic.js
deleted file mode 100644
index 10c9e76..0000000
--- a/core/assets/vendor/farbtastic/farbtastic.js
+++ /dev/null
@@ -1,8 +0,0 @@
-(function(e){e.fn.farbtastic=function(f){e.farbtastic(this,f);return this};e.farbtastic=function(f,l){f=e(f).get(0);return f.farbtastic||(f.farbtastic=new e._farbtastic(f,l))};e._farbtastic=function(f,l){var a=this;e(f).html('<div class="farbtastic"><div class="color"></div><div class="wheel"></div><div class="overlay"></div><div class="h-marker marker"></div><div class="sl-marker marker"></div></div>');var k=e(".farbtastic",f);a.wheel=e(".wheel",f).get(0);a.radius=84;a.square=100;a.width=194;navigator.appVersion.match(/MSIE [0-6]\./)&&
-e("*",k).each(function(){if(this.currentStyle.backgroundImage!="none"){var b=this.currentStyle.backgroundImage;b=this.currentStyle.backgroundImage.substring(5,b.length-2);e(this).css({backgroundImage:"none",filter:"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+b+"')"})}});a.linkTo=function(b){typeof a.callback=="object"&&e(a.callback).unbind("keyup",a.updateValue);a.color=null;if(typeof b=="function")a.callback=b;else if(typeof b=="object"||typeof b=="string"){a.callback=
-e(b);a.callback.bind("keyup",a.updateValue);a.callback.get(0).value&&a.setColor(a.callback.get(0).value)}return this};a.updateValue=function(){this.value&&this.value!=a.color&&a.setColor(this.value)};a.setColor=function(b){var c=a.unpack(b);if(a.color!=b&&c){a.color=b;a.rgb=c;a.hsl=a.RGBToHSL(a.rgb);a.updateDisplay()}return this};a.setHSL=function(b){a.hsl=b;a.rgb=a.HSLToRGB(b);a.color=a.pack(a.rgb);a.updateDisplay();return this};a.widgetCoords=function(b){var c=e(a.wheel).offset();return{x:b.pageX-
-c.left-a.width/2,y:b.pageY-c.top-a.width/2}};a.mousedown=function(b){if(!document.dragging){e(document).bind("mousemove",a.mousemove).bind("mouseup",a.mouseup);document.dragging=true}var c=a.widgetCoords(b);a.circleDrag=Math.max(Math.abs(c.x),Math.abs(c.y))*2>a.square;a.mousemove(b);return false};a.mousemove=function(b){var c=a.widgetCoords(b);if(a.circleDrag){b=Math.atan2(c.x,-c.y)/6.28;if(b<0)b+=1;a.setHSL([b,a.hsl[1],a.hsl[2]])}else{b=Math.max(0,Math.min(1,-(c.x/a.square)+0.5));c=Math.max(0,Math.min(1,
--(c.y/a.square)+0.5));a.setHSL([a.hsl[0],b,c])}return false};a.mouseup=function(){e(document).unbind("mousemove",a.mousemove);e(document).unbind("mouseup",a.mouseup);document.dragging=false};a.updateDisplay=function(){var b=a.hsl[0]*6.28;e(".h-marker",k).css({left:Math.round(Math.sin(b)*a.radius+a.width/2)+"px",top:Math.round(-Math.cos(b)*a.radius+a.width/2)+"px"});e(".sl-marker",k).css({left:Math.round(a.square*(0.5-a.hsl[1])+a.width/2)+"px",top:Math.round(a.square*(0.5-a.hsl[2])+a.width/2)+"px"});
-e(".color",k).css("backgroundColor",a.pack(a.HSLToRGB([a.hsl[0],1,0.5])));if(typeof a.callback=="object"){e(a.callback).css({backgroundColor:a.color,color:a.hsl[2]>0.5?"#000":"#fff"});e(a.callback).each(function(){if(this.value&&this.value!=a.color)this.value=a.color})}else typeof a.callback=="function"&&a.callback.call(a,a.color)};a.pack=function(b){var c=Math.round(b[0]*255),d=Math.round(b[1]*255);b=Math.round(b[2]*255);return"#"+(c<16?"0":"")+c.toString(16)+(d<16?"0":"")+d.toString(16)+(b<16?"0":
-"")+b.toString(16)};a.unpack=function(b){if(b.length==7)return[parseInt("0x"+b.substring(1,3))/255,parseInt("0x"+b.substring(3,5))/255,parseInt("0x"+b.substring(5,7))/255];else if(b.length==4)return[parseInt("0x"+b.substring(1,2))/15,parseInt("0x"+b.substring(2,3))/15,parseInt("0x"+b.substring(3,4))/15]};a.HSLToRGB=function(b){var c,d=b[0];c=b[1];b=b[2];c=b<=0.5?b*(c+1):b+c-b*c;b=b*2-c;return[this.hueToRGB(b,c,d+0.33333),this.hueToRGB(b,c,d),this.hueToRGB(b,c,d-0.33333)]};a.hueToRGB=function(b,c,
-d){d=d<0?d+1:d>1?d-1:d;if(d*6<1)return b+(c-b)*d*6;if(d*2<1)return c;if(d*3<2)return b+(c-b)*(0.66666-d)*6;return b};a.RGBToHSL=function(b){var c,d,m,g,h=b[0],i=b[1],j=b[2];c=Math.min(h,Math.min(i,j));b=Math.max(h,Math.max(i,j));d=b-c;g=(c+b)/2;m=0;if(g>0&&g<1)m=d/(g<0.5?2*g:2-2*g);c=0;if(d>0){if(b==h&&b!=i)c+=(i-j)/d;if(b==i&&b!=j)c+=2+(j-h)/d;if(b==j&&b!=h)c+=4+(h-i)/d;c/=6}return[c,m,g]};e("*",k).mousedown(a.mousedown);a.setColor("#000000");l&&a.linkTo(l)}})(jQuery);
diff --git a/core/assets/vendor/farbtastic/marker.png b/core/assets/vendor/farbtastic/marker.png
deleted file mode 100644
index f9773d1..0000000
--- a/core/assets/vendor/farbtastic/marker.png
+++ /dev/null
@@ -1,5 +0,0 @@
-PNG
-
-   IHDR         dq   	pHYs  ."  ."ݒ   +tEXtCreation Time vr 14 jul 2006 13:31:23 +0100Cg  0IDAT(+q zzB$Vf$RppM. njR;-4r$nB]W&=OOp ]DIg~#L"QJLqyf568Fskئ>;mĲ|ahִdV4/L<tQ|US
-8<Dqv;JN!Iwz%*VV*Dpy/ԩ/1\{`s%V|&CypELͨGJ7ǖqH3J;bpǠ
-Eeg'pE'SFL8    IENDB`
\ No newline at end of file
diff --git a/core/assets/vendor/farbtastic/mask.png b/core/assets/vendor/farbtastic/mask.png
deleted file mode 100644
index 0fc9cbe..0000000
--- a/core/assets/vendor/farbtastic/mask.png
+++ /dev/null
@@ -1,16 +0,0 @@
-PNG
-
-   IHDR   e   e   T|-  IDATx]R:l	
-d8TQ7[7; }^c)qRj/4_Dw+\F]hztG;J:\F>vpcnd`±sVsɵǁD$h'=]as'BfOWd Leds(L׊GWHBרVFa]W6Ev&xM,8O1};Bc 
-JvY_#eTW	ʴd>*Q<ZL̛q)'5Y|=iWOYXѵ؋I0GQW5ذD8F֊c(X%KIFu]ڲxZ&[Qp&NxCꚲyI%&JH#YtŖ˪'@zU%zI	V%9 :'_2˂*%C',"#
-/a×gYVh˴d*,ߐ:YD}A8H#zr1*kwѰ%%~T )
-<f!V@bx,A}R}&8:SIR< dIDY	
-@2rf<#TM(SŔMURB/w]DJ@ M/|	fSFD1wTckINrȜk!
-N#?pT/Bp4I	Zq)O1b*_(|!|ؔ>C	cUҰJaq|# ~!{hi~gxLdt#+{| ~#:̣)-QUU8VVː&A#;U=Pz241:9N\*
-AB#Q1q'Ò'y!'Q0_5S{NvGgWyH!'c.'Sqs2MsclV + a0N&?)o	Z6`9Y٨9x*@ʎ	"0~aPO,)(ZƮ̔r1˛ՒC?& #坨l9QuꬬkRS*+p)7g&P;3GH	vVy
-htT(vme0w.xRp)V}iin
-S-1{'w<}m!`q|	U#SS VhxA}˺Ҝ&N d7Q碝+Rޓ!k		?=H¡URnDqq
-$cPAdbqP,(4"T?&;H@P(0{^^Gtg/O^1FR P%.c|B=0&7F! ^6viBҚT%);QX*']OA;V|MKB N)yS)/i &\ThBHơ i4En Jݣ\^DFA،'D wYjS!?sQJwM1}_K\~$bVr7zJjۊ3|zJb(2`J
-DW]ǀȮ#|]Dkg#h $X!lkgR"
-fWUUy?#z a!'Xi0}vegTkc>Q
-/2Rd^yJE<zJe+O<"|Tڲh ~LfA `w8*|])a@߱ڧn0;> K}9se5[    IENDB`
\ No newline at end of file
diff --git a/core/assets/vendor/farbtastic/wheel.png b/core/assets/vendor/farbtastic/wheel.png
deleted file mode 100644
index 4a905e9..0000000
--- a/core/assets/vendor/farbtastic/wheel.png
+++ /dev/null
@@ -1,49 +0,0 @@
-PNG
-
-   IHDR         ?  -IDATx^m]u}_!1+V@lQlu&bJPNN[A*`e E*%(D*DQVS׿i̜k~3Zg9'{?'{kI'~wޙz.mٚL[ܗdU*kT<Q
-VYWƐ"<:$їRE><M @#1z 'RO;mg	j]u2<Sd*xW7%oz$d@B]BsrFCnP)5鿎k!7b@!BIx|W4wں ",5ParĄIfJotmNުNN-F^H0/#'@%BS%@6`檜|ӾߴY9y[B}$OIx@W\ɑ6`.UdA"M!@F4xwFLXh/5,(x|LXb7]`+W9Vv2lAdW?<);
-]7)rӄ<O2<6_vW~/eЖ>[-[cfd[Ča9&b KADʢCX}ȲļLa3"[Fq/%Q[`p&~CW9Nx]٦/zN)JvVLNbh_ %e'_
-H iB̖,mydvˤ)Dvr>l{)CUE3e<ڔWx+2[壒G%0NK:t#-D"@q\F\	aVQDAmj몼2U'&ZeJ}Q6h
-RFe?zjݔq9#/nfHdQQ72bPMJYdk჆t\=kdxf|(~@:T'@*>7L5:T87sa]d|dg>tbҤ4@rn(`(hSp_a:zFd?>]GAJ Q>!&h5S6v慖sdٹ3xGH_ڃD0>C`]kjSXX~$e (Binjd.yQ7@@v6h_$@nύ)+R4yV^<@~q'O_hOˆ*jY9ytڤ&gVhnP>F<_*"N2K>>$@R7H2bj|q'`Ї2C;y7`7a>v6pKX\JS1O2}.A+ɼOd  X#bq=a#Tʓ /0dK?<ٶJi~F)Yf]>oD'CC[hH6hi-$j>*qqSz1r$/^%9/ܼĄ#I> ƛ]9?Fl S#iM-Z  h1~Zc9סK¡¾vTKsY}ʕG![1'%mxIׅ+W].ܕ9b/Q9LX(&A~BDd{gwfIG"ZZ%1}W(\t-%c01~(̅485z- Um:C:81J/|gK~]]j[?ldZ㙼#GjlS"N嶑/%%C9^D2=@=)jҽX{~8緞_13$'?)-v\yxeg5r)5QSH_b9<|kr#¾peM+Vbʐ-"N!Z]
-Rs["]A,shgYG]k;G%%!1 M,\osh[7˙:´߉dU !֟FK]׻Je*sBy(I^;a-&ZB!,C! #K	ǻ;ֺJ8IB4<ɞjÛ/!F*ckTЫu50|Ib֪pP0o9@83?ݱUboџYY#8D'@^C|tbx7 O"qޫrdP!~i{
-,g_(W ?Dv#u5ue.ԔI!T3#g̒!eg{&0#t5w3Ngb%*%I!l%|ƃ~7drbq -CronFw#VB8*3?9ras0(/GQ\wFwȋdgRef{2jRNƷv8rozSz~#qiF(P Vj=5.xTy]uwp\)YfYKI0G?gxh5r[U^--,έ$NibQ#BnM	ʽEt5]m*Jjڎ(90WaՔJy%EVS2KsZRNՔ%%̑DA,Us+*{lnH2dy'N!d-AIҰhrε6"" ^'fGH.U٭؈ń8SB:x+є@RX6՚X}b3ab8u.Ph HmMZmP*D>R?JnBL45DPj$iBIMkAx&"V<uIvZg{og'<Os'~NmMQ
-rӗշ@œߪ{U燂zi FJϵZ?_ay:9 \5@%>އO/
-t@\?%^%s*]3XH4^Vw9}Lr5·' 5p6(LVN]&emJJ$U49|Ixo/H
-i}nTAk(a&<b:l"ݩjߪA.a6U*m	q4$MA,,;N޺䜯b^gyD|.Vߜ 25?rIa DQ섥`,3Hs5^
-sR3o*Nslb谤A8W*T6Egtk jȏ)`c& P@7y'z-t+3}P쁐>1ˁ?]axIITB!L¹{+I:	Ji}S}p3a+:P*`½|TFQ] y~RN HۄQzɀ4X_'NBh奔S| L\ByƁZ4  5Pt4E"v=k2cP,폶_!lT`2W,[	|5%Q?/ab+0(ڙU@ Rd`v~yW R ZkY VS-sy\+t`}~nhS0. g *LC)^NRv6i+ Ec>*DamȁEϋ°
-[5J|锚[<I}Q)l# 0:4ϜBnTDxr¾cfnk/õXk:NR=aB~=|SХ=if]vА͂>A2 +MFaf
-`@ rѬuЏ!~ hiJ (p7;Io+__*n9=BՇGI_3<Jٝ#
-@#h@4U2A8zJyHaw\un6yX+P}{"n럏}"GX` (WH;'XEep \nY)/Kr_	?.^vpy[$)lVcv[
-Q`|$\	Xs7Ji늁Htp~46*D[46 Ղa \ytTPz0~VM Nyɧ[3ʯ"l4PXOX'FAQ9
-4^
-r/[ݿ`7z ~,*$ ӐiM6D[ҜK#p	,$c|h1ZZJχD`Jx^mm9#QC#0O
-eUws^2=J&IF
-b@Nl[e C % A*{VE12 wU{DBT>+X0lFX+< HlLwo y?X}8LȔDv|qkF X %"%6NizN_@U}SbEH  Lad?њ]/ R\\.Uk4RUo>8P):IsD@F)zh|Fڱpl 
-yH `Gɨ:[aW74k6+uR&Ȯs L3	oa, ʤm,y! w 뼼}2k0]2ʝgߪ_HkcK~V	hD;Ek[	ǔ_GNA, 1`xDxEApJ&w>08Y94SX(2@\P_}
-ۀٱ"갲27E"_2YѼktw~w![u0>'th1LJM1COE]j)7+VV^^	7mjo4ZZe@%	x6n|S4wzm?'WJCn|)s+.uʟ ƗÎ	<';A2)<Kh"<pX֗o] t`P>;Jq	*`{gGY%yA~|ĶPC(
-~ed7qWCIdU, 7AmR5Y`Coj[Ji}Mɜ3}%w~f&ik9;c|]XWZMcCm2@Ε^X4S	++`d)
-ոdiI`>A" W%U	$>A66.׀U:`p)ΈP`glJ㔡G%Yy()HU' 3z֥@_rKᥱI%(:7QI03 .DlPl62cv,GeSN^o`JCGHC{t7ƈ3TY@[Z C
-@GeFDIU&I=3/>l"k8G~3qcgkuIp"rVIGI*H?]ޗƿT`Ks T4AeC #S`'0%=E#^XeP*1	߹+2gC* L@j9`e뫿)2 #K#,$e /2>* tn7~zҔ1̌FS}{Glx'V[2d+!4Pwagӝgec_oehFoP|7;fjJ葄#F"CC(\^bLĘe(s3>^iU޵$r"P2Kb=6˰_3/? *лE:gє!iFd
- (ٕNB+Fd_!6<bl1]kxK[%Cm$xR9=Cl=+yŴ}etDmT!-ycTpeHD\ZaAh'ä]
-}@գ#z4I%CCF({4I
--C7 g$Q0zX(CslwC`_KUu#Z5T+1IAgjo1ڽeky2#*CO!BJ-4+ >>2@=-C4$fϰdJk5i3G"IZ3$%
-*`"m3BpsWyb4y-?ƎP-Y2-K#	lQ3.tϰ &8Il2`B`x**W8d2!NђaW3_̑,Bct ,	6q43D&3t!=Bpdd*XV@Vl
-?VXI./adIBbum{<iAA2ˀK.Z2Dp;"/E;ao|!&>{V3Ig#,3/HA ed*XRAm 18t<6x7}d pZz~tl5eS'm|8{I&pR˔^0D)`C]_3I&
-ƫL$1`Zx+Iցd2k#ĕ!mϻ_aKȗS>	'o2L&ye<`P"PYܗ0 `T2!h-<x{lXfvD0p7#|TpB bo	@oqe "JHdj`'C}RjxRg^hw3Y0_ 
-_76[0>@^c]t!5|"N`deL a/<:k!lg 2d2gj8> eB/&w	8+'CL<NKK2
-.)`'t("<dˠZZ-ȱq`#md2§4T$$:ΖA gʥ4+2d29V R2| 
-x6W; Nsq%d2DX_?~E:p2oAbsza=C26 }CDgQe BA)0d5Vd#j笅sh"B+jIIa[7`3[i Y|'qsPߟM&CWGXX'2'Cgf[Eid2>SC"E*1k5"fҡ*jbrL/ְ6b.6_	܉CK]5;)2	+<Bv16?O[K+"\y4L;.Wa_;3B_!Ό`bν^	>DG5Lz)*z %#Xu/w=2<OEh^u P2P^e`o-(z !pcu_u
-.*a=Ga)G
-Z~x0Qk	\r<GE_,(Q!D@"z7F~)-fk2\(LoC@RlҁGج$n|vK J
-J>\.'#K¼C$5F5RM¿cpn!FC6wi	J|9×\(BsV(W+,]B{j9|d).KP:+ަIU=j:2`$°F#0HjI SJ,v!Yʛ@Bd4o ^fI	o# )i̥{sY-G;XBHdM#+L"\aWIPNXƀ9QN'#`m( 6z[=O̓\Ka8	;gx=l&7J%: HR	 naD
-z TsӲ*cI@Lp+ke(oA9$=uӃބ`f9_ -DAeKsh] `بz쿋5l"QP3znރ*;wsBnB(M"*i*H*dJ[haB-3-"JaJR(*:r@$ !IL'9g'9MwuZy> @DddzK@쭼R&!Ȱ|yL@̖S)B Y~R}_&]z"/!D#h/I׈%CFHR;C\w}aؤG>%ByPT:vsR}[Ⱥ@ϫg󩖨Or	&طWC=)I0,D~ jbߺDMWAELrdT=7T8M2+_fiOMGKZ˦MXQ1V=N=ɄHOrw9%_Xm#KٴJk/lB*_og?	yl o=,\)IoM&Y 7e8\@
-LQä/2Qm?i%GTEfoc~Ԝ~Xa#/ʤ/&&ihz @xIQ3jFELQZ8$G[=IZ(h!q<a(&L?	ImarIjIz@tz.ZFf-Ib[Dg7ٲ/u-N}%ZgO-@,!n'0d):ޮ/uHˣcT(LWX8'45FGp&Oe.DfORIT&D{
-SϨ킼=.y7"Zg` IR\2KxbGahDS~F/*[RJf_3__FGlFqf)IO>;4~J}F_=jU4`7Nө}b*, 6 Jkx19e_764Dm%IH&Ig득Z=	S}
-aCoҜ/}vfGdsXIHW٢$IZW%.jELɶ2IrjH#9<@aGLWx$}Ru*T,$S$tZ}os꯹fH)꧟Ŀ%.ơ~2r咨˥X ~ٌO0YrBt6;TWRHP8Nc̭oز@R?!s炲2`Lǁ]QXbj}8,BGP=p
-ĭ'9ә-:݇ϛQL'gۙj'yYd('Ġ=uDqb4<HKUL|P'[ΩsV}[*.u LG(QNk:' bP3Vx278Fap#;mGDɡo>ڏ<%<XCZ98bwrA mV*-di	Z-:%ypۢrSb[/WYca.6Jiǅ$4
-X%S?3hБah:i2~+(IEqX: =F\-,Du+Buz@C&m}^R^ZZu#Pg}G`dc
-C{04h枴v#& ҙQ#JZfbiS5=}9<=#|Q2V. {I!LDLf@1)i"h&Q#4T7. s%BHP/qڤ'_iᐒ.Q@7}Q  :YU_^UCiM[$c;iIܯ$a*5hٕ+M}fT5~w%pcIx!BO[Uy7JP@ P5բ mZ"UR&q;CГ|'& .U}Ih*{4UAUf/gֹ[2Jd ]>%[vڼXm$[G.}H	iqq;Гa{MU;o?#qݭKϋГaKDxwf_hݚv4kcH
-4aqgz2HrUbDۘ(?v:e	TaW z2ӕqdDˑ-eDWk-q5b7D\pE͑|^8V6:W
-t:#^_FNHL#6n.r9BETV'ڶ>b툱t7s"
-z2})EA&9*ǝ'F2tGNcƻ_.=v)_EO	'JJQ!E=	)%(\(nf2QKwy*{*џ%*bV^F05:eW1SL*_\gyWۦ:	^%.ļY'C1𜜯tic6GtSy9
-Y5=z 7pnsG.Lp<q8թqbIXE<F<Jztۿ'Co9bk"8ka8`R$^#~Izsx,[}ГGD Z 䜬ijk	1	l+z"c`5?xXAm2ѣG=zg9    IENDB`
\ No newline at end of file
diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index a6d963c..5f37c0e 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -341,22 +341,6 @@ jquery.cookie:
   dependencies:
     - core/jquery
 
-jquery.farbtastic:
-  remote: https://github.com/mattfarina/farbtastic
-  # @todo Ping @robloach or @mattfarina to retroactively create this release.
-  version: 1.2
-  license:
-    name: GNU-GPL-2.0-or-later
-    url: https://github.com/mattfarina/farbtastic/blob/master/LICENSE.txt
-    gpl-compatible: true
-  js:
-    assets/vendor/farbtastic/farbtastic.js: { minified: true }
-  css:
-    component:
-      assets/vendor/farbtastic/farbtastic.css: {}
-  dependencies:
-    - core/jquery
-
 jquery.form:
   remote: https://github.com/malsup/form
   version: 3.50
diff --git a/core/modules/color/color.js b/core/modules/color/color.js
index f9eebf2..39b6add 100644
--- a/core/modules/color/color.js
+++ b/core/modules/color/color.js
@@ -9,34 +9,19 @@
 
   Drupal.behaviors.color = {
     attach: function (context, settings) {
-      var i, j, colors;
       // This behavior attaches by ID, so is only valid once on a page.
-      var form = $(context).find('#system-theme-settings .color-form').once('color');
-      if (form.length === 0) {
+      var $form = $(context).find('#system-theme-settings .color-form').once('color');
+      if ($form.length === 0) {
         return;
       }
-      var inputs = [];
-      var hooks = [];
-      var locks = [];
-      var focused = null;
-
-      // Add Farbtastic.
-      $('<div id="placeholder"></div>').once('color').prependTo(form);
-      var farb = $.farbtastic('#placeholder');
-
-      // Decode reference colors to HSL.
-      var reference = settings.color.reference;
-      for (i in reference) {
-        if (reference.hasOwnProperty(i)) {
-          reference[i] = farb.RGBToHSL(farb.unpack(reference[i]));
-        }
-      }
 
       // Build a preview.
       var height = [];
       var width = [];
+      var $editScheme = $form.find('#edit-scheme');
+
       // Loop through all defined gradients.
-      for (i in settings.gradients) {
+      for (var i in settings.gradients) {
         if (settings.gradients.hasOwnProperty(i)) {
           // Add element to display the gradient.
           $('#preview').once('color').append('<div id="gradient-' + i + '"></div>');
@@ -48,153 +33,52 @@
           // Add rows (or columns for horizontal gradients).
           // Each gradient line should have a height (or width for horizontal
           // gradients) of 10px (because we divided the height/width by 10 above).
-          for (j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
+          for (var j = 0; j < (settings.gradients[i].direction === 'vertical' ? height[i] : width[i]); ++j) {
             gradient.append('<div class="gradient-line"></div>');
           }
         }
       }
 
       // Set up colorScheme selector.
-      form.find('#edit-scheme').on('change', function () {
+      $editScheme.on('change', function () {
         var schemes = settings.color.schemes, colorScheme = this.options[this.selectedIndex].value;
         if (colorScheme !== '' && schemes[colorScheme]) {
           // Get colors of active scheme.
-          colors = schemes[colorScheme];
+          var colors = schemes[colorScheme];
           for (var fieldName in colors) {
             if (colors.hasOwnProperty(fieldName)) {
-              callback($('#edit-palette-' + fieldName), colors[fieldName], false, true);
+              setScheme($('#edit-palette-' + fieldName), colors[fieldName], true);
             }
           }
           preview();
         }
       });
 
-      /**
-       * Renders the preview.
-       */
-      function preview() {
-        Drupal.color.callback(context, settings, form, farb, height, width);
-      }
-
-      /**
-       * Shifts a given color, using a reference pair (ref in HSL).
-       *
-       * This algorithm ensures relative ordering on the saturation and luminance
-       * axes is preserved, and performs a simple hue shift.
-       *
-       * It is also symmetrical. If: shift_color(c, a, b) === d, then
-       * shift_color(d, b, a) === c.
-       */
-      function shift_color(given, ref1, ref2) {
-        var d;
-        // Convert to HSL.
-        given = farb.RGBToHSL(farb.unpack(given));
-
-        // Hue: apply delta.
-        given[0] += ref2[0] - ref1[0];
-
-        // Saturation: interpolate.
-        if (ref1[1] === 0 || ref2[1] === 0) {
-          given[1] = ref2[1];
-        }
-        else {
-          d = ref1[1] / ref2[1];
-          if (d > 1) {
-            given[1] /= d;
-          }
-          else {
-            given[1] = 1 - (1 - given[1]) * d;
-          }
-        }
-
-        // Luminance: interpolate.
-        if (ref1[2] === 0 || ref2[2] === 0) {
-          given[2] = ref2[2];
-        }
-        else {
-          d = ref1[2] / ref2[2];
-          if (d > 1) {
-            given[2] /= d;
-          }
-          else {
-            given[2] = 1 - (1 - given[2]) * d;
-          }
-        }
-
-        return farb.pack(farb.HSLToRGB(given));
-      }
+      // Trigger callback on input change
+      $form.on('change keyup', '[data-drupal-color]', function () {
+        // Reset the color scheme selector.
+        $editScheme.val('_none');
+        preview();
+      });
 
       /**
-       * Callback for Farbtastic when a new color is chosen.
+       * Callback when a new scheme is chosen.
        */
-      function callback(input, color, propagate, colorScheme) {
-        var matched;
-        // Set background/foreground colors.
-        $(input).css({
-          backgroundColor: color,
-          'color': farb.RGBToHSL(farb.unpack(color))[2] > 0.5 ? '#000' : '#fff'
-        });
-
+      function setScheme(input, color, colorScheme) {
         // Change input value.
         if ($(input).val() && $(input).val() !== color) {
           $(input).val(color);
 
-          // Update locked values.
-          if (propagate) {
-            i = input.i;
-            for (j = i + 1; ; ++j) {
-              if (!locks[j - 1] || $(locks[j - 1]).is('.unlocked')) {
-                break;
-              }
-              matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
-              callback(inputs[j], matched, false);
-            }
-            for (j = i - 1; ; --j) {
-              if (!locks[j] || $(locks[j]).is('.unlocked')) {
-                break;
-              }
-              matched = shift_color(color, reference[input.key], reference[inputs[j].key]);
-              callback(inputs[j], matched, false);
-            }
-
-            // Update preview.
-            preview();
-          }
-
-          // Reset colorScheme selector.
-          if (!colorScheme) {
-            resetScheme();
-          }
+          // Update preview.
+          preview();
         }
       }
 
       /**
-       * Resets the color scheme selector.
-       */
-      function resetScheme() {
-        form.find('#edit-scheme').each(function () {
-          this.selectedIndex = this.options.length - 1;
-        });
-      }
-
-      /**
-       * Focuses Farbtastic on a particular field.
+       * Renders the preview.
        */
-      function focus(e) {
-        var input = e.target;
-        // Remove old bindings.
-        if (focused) {
-          $(focused).off('keyup', farb.updateValue)
-            .off('keyup', preview).off('keyup', resetScheme)
-            .parent().removeClass('item-selected');
-        }
-
-        // Add new bindings.
-        focused = input;
-        farb.linkTo(function (color) { callback(input, color, true, false); });
-        farb.setColor(input.value);
-        $(focused).on('keyup', farb.updateValue).on('keyup', preview).on('keyup', resetScheme)
-          .parent().addClass('item-selected');
+      function preview() {
+        Drupal.color.callback(context, settings, $form, height, width);
       }
 
       // Initialize color fields.
diff --git a/core/modules/color/color.libraries.yml b/core/modules/color/color.libraries.yml
index b2eb8d6..5518d30 100644
--- a/core/modules/color/color.libraries.yml
+++ b/core/modules/color/color.libraries.yml
@@ -6,7 +6,6 @@ drupal.color:
     - core/jquery
     - core/drupal
     - core/jquery.once
-    - core/jquery.farbtastic
     - color/drupal.color.preview
 
 drupal.color.preview:
diff --git a/core/modules/color/color.module b/core/modules/color/color.module
index 98678f7..bff95a8 100644
--- a/core/modules/color/color.module
+++ b/core/modules/color/color.module
@@ -188,7 +188,7 @@ function color_scheme_form($complete_form, FormStateInterface $form_state, $them
   $base = drupal_get_path('module', 'color');
   $info = color_get_info($theme);
 
-  $info['schemes'][''] = array('title' => t('Custom'), 'colors' => array());
+  $info['schemes']['_none'] = array('title' => t('Custom'), 'colors' => array());
   $color_sets = array();
   $schemes = array();
   foreach ($info['schemes'] as $key => $scheme) {
@@ -254,7 +254,7 @@ function color_scheme_form($complete_form, FormStateInterface $form_state, $them
         '#value_callback' => 'color_palette_color_value',
         '#default_value' => $value,
         '#size' => 8,
-        '#attributes' => array('dir' => LanguageInterface::DIRECTION_LTR),
+        '#attributes' => array('dir' => 'ltr', 'data-drupal-color' => $name),
       );
     }
   }
@@ -372,7 +372,8 @@ function color_scheme_form_submit($form, FormStateInterface $form_state) {
   $config = \Drupal::configFactory()->getEditable('color.theme.' . $theme);
 
   // Resolve palette.
-  if ($scheme != '') {
+  $palette = $form_state['values']['palette'];
+  if ($form_state['values']['scheme'] != '_none') {
     foreach ($palette as $key => $color) {
       if (isset($info['schemes'][$scheme]['colors'][$key])) {
         $palette[$key] = $info['schemes'][$scheme]['colors'][$key];
diff --git a/core/modules/color/css/color.admin.css b/core/modules/color/css/color.admin.css
index 5da8c35..0bf3bb5 100644
--- a/core/modules/color/css/color.admin.css
+++ b/core/modules/color/css/color.admin.css
@@ -5,9 +5,6 @@
 .color-form {
   max-width: 50em;
 }
-.farbtastic {
-  margin: 0 auto;
-}
 .color-form .form-item {
   overflow: hidden;
   margin: 0.5em 0;
@@ -20,9 +17,7 @@
 }
 .color-form .form-text {
   float: left; /* LTR */
-  width: 86%;
-  text-align: center;
-  cursor: pointer;
+  width: auto;
 }
 [dir="rtl"] .color-form .form-text {
   float: right;
@@ -80,10 +75,7 @@
     float: left;
   }
   .color-form .form-item {
-    margin: 0.5em 195px 0.5em 0; /* LTR */
-  }
-  [dir="rtl"] .color-form .form-item {
-    margin: 0.5em 0 0.5em 195px;
+    margin: 0.5em;
   }
   .color-form label {
     float: left; /* LTR */
@@ -94,7 +86,7 @@
     float: right;
     clear: right;
   }
-  .color-form .form-text,
+  .color-form .text,
   .color-form .form-select {
     float: left; /* LTR */
     width: auto;
diff --git a/core/modules/color/preview.js b/core/modules/color/preview.js
index 4c99ffa..30f43c3 100644
--- a/core/modules/color/preview.js
+++ b/core/modules/color/preview.js
@@ -8,8 +8,7 @@
   "use strict";
 
   Drupal.color = {
-    callback: function (context, settings, form, farb, height, width) {
-      var accum, delta;
+    callback: function (context, settings, form, height, width) {
       // Solid background.
       form.find('#preview').css('backgroundColor', form.find('#palette input[name="palette[base]"]').val());
 
@@ -23,15 +22,15 @@
             accum[k] += delta[k];
           }
         }
-        element.style.backgroundColor = farb.pack(accum);
+        element.style.backgroundColor = accum;
       }
 
       // Set up gradients if there are some.
       var color_start, color_end;
       for (var i in settings.gradients) {
         if (settings.gradients.hasOwnProperty(i)) {
-          color_start = farb.unpack(form.find('#palette input[name="palette[' + settings.gradients[i].colors[0] + ']"]').val());
-          color_end = farb.unpack(form.find('#palette input[name="palette[' + settings.gradients[i].colors[1] + ']"]').val());
+          color_start = form.find('#palette input[name="palette[' + settings.gradients[i].colors[0] + ']"]').val();
+          color_end = form.find('#palette input[name="palette[' + settings.gradients[i].colors[1] + ']"]').val();
           if (color_start && color_end) {
             delta = [];
             for (var j in color_start) {
diff --git a/core/modules/color/src/Tests/ColorConfigSchemaTest.php b/core/modules/color/src/Tests/ColorConfigSchemaTest.php
index 0a4fb97..ea1b7ba 100644
--- a/core/modules/color/src/Tests/ColorConfigSchemaTest.php
+++ b/core/modules/color/src/Tests/ColorConfigSchemaTest.php
@@ -47,7 +47,7 @@ protected function setUp() {
    */
   function testValidColorConfigSchema() {
     $settings_path = 'admin/appearance/settings/bartik';
-    $edit['scheme'] = '';
+    $edit['scheme'] = '_none';
     $edit['palette[bg]'] = '#123456';
     $this->drupalPostForm($settings_path, $edit, t('Save configuration'));
   }
diff --git a/core/modules/color/src/Tests/ColorTest.php b/core/modules/color/src/Tests/ColorTest.php
index 3456dca..8ee1949 100644
--- a/core/modules/color/src/Tests/ColorTest.php
+++ b/core/modules/color/src/Tests/ColorTest.php
@@ -66,7 +66,7 @@ protected function setUp() {
       ),
       'color_test_theme' => array(
         'palette_input' => 'palette[bg]',
-        'scheme' => 'custom',
+        'scheme' => '_none',
         'scheme_color' => '#3b3b3b',
       ),
     );
@@ -114,7 +114,7 @@ function _testColor($theme, $test_values) {
     $this->drupalGet($settings_path);
     $this->assertResponse(200);
     $this->assertUniqueText('Color set');
-    $edit['scheme'] = '';
+    $edit['scheme'] = '_none';
     $edit[$test_values['palette_input']] = '#123456';
     $this->drupalPostForm($settings_path, $edit, t('Save configuration'));
 
@@ -162,8 +162,8 @@ function testValidColor() {
       ->save();
     $settings_path = 'admin/appearance/settings/bartik';
 
-    $this->drupalLogin($this->bigUser);
-    $edit['scheme'] = '';
+    $this->drupalLogin($this->big_user);
+    $edit['scheme'] = '_none';
 
     foreach ($this->colorTests as $color => $is_valid) {
       $edit['palette[bg]'] = $color;
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
new file mode 100644
index 0000000..dd16e11
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Common/JavaScriptTest.php
@@ -0,0 +1,727 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Common\JavaScriptTest.
+ */
+
+namespace Drupal\system\Tests\Common;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\simpletest\DrupalUnitTestBase;
+use Drupal\Component\Utility\Crypt;
+
+/**
+ * Tests the JavaScript system.
+ */
+class JavaScriptTest extends DrupalUnitTestBase {
+
+  /**
+   * Enable Language and SimpleTest in the test environment.
+   *
+   * @var array
+   */
+  public static $modules = array('language', 'simpletest', 'common_test', 'system');
+
+  /**
+   * Stores configured value for JavaScript preprocessing.
+   */
+  protected $preprocess_js = NULL;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'JavaScript',
+      'description' => 'Tests the JavaScript system.',
+      'group' => 'Common',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+    // There are dependencies in drupal_get_js() on the theme layer so we need
+    // to initialize it.
+    drupal_theme_initialize();
+
+    // Disable preprocessing
+    $config = \Drupal::config('system.performance');
+    $this->preprocess_js = $config->get('js.preprocess');
+    $config->set('js.preprocess', 0);
+    $config->save();
+
+    // Reset _drupal_add_js() statics before each test.
+    drupal_static_reset('_drupal_add_js');
+  }
+
+  function tearDown() {
+    // Restore configured value for JavaScript preprocessing.
+    $config = \Drupal::config('system.performance');
+    $config->set('js.preprocess', $this->preprocess_js);
+    $config->save();
+    parent::tearDown();
+  }
+
+  /**
+   * Tests that default JavaScript is empty.
+   */
+  function testDefault() {
+    $this->assertEqual(array(), _drupal_add_js(), 'Default JavaScript is empty.');
+  }
+
+  /**
+   * Tests adding a JavaScript file.
+   */
+  function testAddFile() {
+    $attached['#attached']['js']['core/misc/collapse.js'] = array();
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertTrue(array_key_exists('core/misc/collapse.js', $javascript), 'JavaScript files are correctly added.');
+  }
+
+  /**
+   * Tests adding settings.
+   */
+  function testAddSetting() {
+    // Add a file in order to test default settings.
+    $attached['#attached']['library'][] = 'core/drupalSettings';
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $last_settings = reset($javascript['settings']['data']);
+    $this->assertTrue(array_key_exists('currentPath', $last_settings['path']), 'The current path JavaScript setting is set correctly.');
+
+    $javascript = _drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
+    $last_settings = end($javascript['settings']['data']);
+    $this->assertEqual(280342800, $last_settings['dries'], 'JavaScript setting is set correctly.');
+    $this->assertEqual('rocks', $last_settings['drupal'], 'The other JavaScript setting is set correctly.');
+  }
+
+  /**
+   * Tests adding an external JavaScript File.
+   */
+  function testAddExternal() {
+    $attached['#attached']['js']['http://example.com/script.js'] = array('type' => 'external');
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), 'Added an external JavaScript file.');
+  }
+
+  /**
+   * Tests adding JavaScript files with additional attributes.
+   */
+  function testAttributes() {
+    $default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
+
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['http://example.com/script.js'] = array(
+      'type' => 'external',
+      'attributes' => array('defer' => 'defer'),
+    );
+    $attached['#attached']['js']['core/misc/collapse.js'] = array(
+      'attributes' => array('defer' => 'defer'),
+    );
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+
+    $expected_1 = '<script src="http://example.com/script.js" defer="defer"></script>';
+    $expected_2 = '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '" defer="defer"></script>';
+
+    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute.');
+    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute.');
+  }
+
+  /**
+   * Tests that attributes are maintained when JS aggregation is enabled.
+   */
+  function testAggregatedAttributes() {
+    // Enable aggregation.
+    \Drupal::config('system.performance')->set('js.preprocess', 1)->save();
+
+    $default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
+
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['http://example.com/script.js'] = array(
+      'type' => 'external',
+      'attributes' => array('defer' => 'defer'),
+    );
+    $attached['#attached']['js']['core/misc/collapse.js'] = array(
+      'attributes' => array('defer' => 'defer'),
+    );
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+
+    $expected_1 = '<script src="http://example.com/script.js" defer="defer"></script>';
+    $expected_2 = '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '" defer="defer"></script>';
+
+    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered external JavaScript with correct defer attribute with aggregation enabled.');
+    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered internal JavaScript with correct defer attribute with aggregation enabled.');
+  }
+
+  /**
+   * Tests drupal_get_js() for JavaScript settings.
+   */
+  function testHeaderSetting() {
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/drupalSettings';
+    drupal_render($attached);
+
+    $javascript = drupal_get_js('header');
+    $this->assertTrue(strpos($javascript, 'basePath') > 0, 'Rendered JavaScript header returns basePath setting.');
+    $this->assertTrue(strpos($javascript, 'scriptPath') > 0, 'Rendered JavaScript header returns scriptPath setting.');
+    $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, 'Rendered JavaScript header returns pathPrefix setting.');
+    $this->assertTrue(strpos($javascript, 'currentPath') > 0, 'Rendered JavaScript header returns currentPath setting.');
+
+    // Only the second of these two entries should appear in drupalSettings.
+    $attached = array();
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTest' => 'commonTestShouldNotAppear'),
+    );
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTest' => 'commonTestShouldAppear'),
+    );
+    // Only the second of these entries should appear in drupalSettings.
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestJsArrayLiteral' => array('commonTestJsArrayLiteralOldValue')),
+    );
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestJsArrayLiteral' => array('commonTestJsArrayLiteralNewValue')),
+    );
+    // Only the second of these two entries should appear in drupalSettings.
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestJsObjectLiteral' => array('key' => 'commonTestJsObjectLiteralOldValue')),
+    );
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestJsObjectLiteral' => array('key' => 'commonTestJsObjectLiteralNewValue')),
+    );
+    // Real world test case: multiple elements in a render array are adding the
+    // same (or nearly the same) JavaScript settings. When merged, they should
+    // contain all settings and not duplicate some settings.
+    $settings_one = array('moduleName' => array('ui' => array('button A', 'button B'), 'magical flag' => 3.14159265359));
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestRealWorldIdentical' => $settings_one),
+    );
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestRealWorldIdentical' => $settings_one),
+    );
+    $settings_two = array('moduleName' => array('ui' => array('button A', 'button B'), 'magical flag' => 3.14159265359, 'thingiesOnPage' => array('id1' => array())));
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestRealWorldAlmostIdentical' => $settings_two),
+    );
+    $settings_two = array('moduleName' => array('ui' => array('button C', 'button D'), 'magical flag' => 3.14, 'thingiesOnPage' => array('id2' => array())));
+    $attached['#attached']['js'][] = array(
+      'type' => 'setting',
+      'data' => array('commonTestRealWorldAlmostIdentical' => $settings_two),
+    );
+
+    drupal_render($attached);
+    $javascript = drupal_get_js('header');
+
+    // Test whether _drupal_add_js can be used to override a previous setting.
+    $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, 'Rendered JavaScript header returns custom setting.');
+    $this->assertTrue(strpos($javascript, 'commonTestShouldNotAppear') === FALSE, '_drupal_add_js() correctly overrides a custom setting.');
+
+    // Test whether _drupal_add_js can be used to add and override a JavaScript
+    // array literal (an indexed PHP array) values.
+    $array_override = strpos($javascript, 'commonTestJsArrayLiteralNewValue') > 0 && strpos($javascript, 'commonTestJsArrayLiteralOldValue') === FALSE;
+    $this->assertTrue($array_override, '_drupal_add_js() correctly overrides settings within an array literal (indexed array).');
+
+    // Test whether _drupal_add_js can be used to add and override a JavaScript
+    // object literal (an associate PHP array) values.
+    $associative_array_override = strpos($javascript, 'commonTestJsObjectLiteralNewValue') > 0 && strpos($javascript, 'commonTestJsObjectLiteralOldValue') === FALSE;
+    $this->assertTrue($associative_array_override, '_drupal_add_js() correctly overrides settings within an object literal (associative array).');
+
+    // Parse the generated drupalSettings <script> back to a PHP representation.
+    $startToken = 'drupalSettings = ';
+    $endToken = '}';
+    $start = strpos($javascript, $startToken) + strlen($startToken);
+    $end = strrpos($javascript, $endToken);
+    $json  = drupal_substr($javascript, $start, $end - $start + 1);
+    $parsed_settings = Json::decode($json);
+
+    // Test whether the two real world cases are handled correctly.
+    $settings_two['moduleName']['thingiesOnPage']['id1'] = array();
+    $this->assertIdentical($settings_one, $parsed_settings['commonTestRealWorldIdentical'], '_drupal_add_js handled real world test case 1 correctly.');
+    $this->assertEqual($settings_two, $parsed_settings['commonTestRealWorldAlmostIdentical'], '_drupal_add_js handled real world test case 2 correctly.');
+  }
+
+  /**
+   * Tests to see if resetting the JavaScript empties the cache.
+   */
+  function testReset() {
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array();
+    drupal_render($attached);
+    drupal_static_reset('_drupal_add_js');
+    $this->assertEqual(array(), _drupal_add_js(), 'Resetting the JavaScript correctly empties the cache.');
+  }
+
+  /**
+   * Tests adding inline scripts.
+   */
+  function testAddInline() {
+    $inline = 'jQuery(function () { });';
+    $attached['#attached']['library'][] = 'core/jquery';
+    $attached['#attached']['js'][] = array(
+      'type' => 'inline',
+      'data' => $inline,
+      'attributes' => array('defer' => 'defer'),
+    );
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertTrue(array_key_exists('core/assets/vendor/jquery/jquery.js', $javascript), 'jQuery is added when inline scripts are added.');
+    $data = end($javascript);
+    $this->assertEqual($inline, $data['data'], 'Inline JavaScript is correctly added to the footer.');
+  }
+
+  /**
+   * Tests rendering an external JavaScript file.
+   */
+  function testRenderExternal() {
+    $external = 'http://example.com/example.js';
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js'][] = array(
+      'type' => 'external',
+      'data' => $external,
+    );
+    drupal_render($attached);
+
+    $javascript = drupal_get_js();
+    // Local files have a base_path() prefix, external files should not.
+    $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, 'Rendering an external JavaScript file.');
+  }
+
+  /**
+   * Tests drupal_get_js() with a footer scope.
+   */
+  function testFooterHTML() {
+    $inline = 'jQuery(function () { });';
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js'][] = array(
+      'type' => 'inline',
+      'data' => $inline,
+      'scope' => 'footer',
+      'attributes' => array('defer' => 'defer'),
+    );
+    drupal_render($attached);
+
+    $javascript = drupal_get_js('footer');
+    $this->assertTrue(strpos($javascript, $inline) > 0, 'Rendered JavaScript footer returns the inline code.');
+  }
+
+  /**
+   * Tests _drupal_add_js() sets preproccess to FALSE when cache is also FALSE.
+   */
+  function testNoCache() {
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('cache' => FALSE);
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertFalse($javascript['core/misc/collapse.js']['preprocess'], 'Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.');
+  }
+
+  /**
+   * Tests adding a JavaScript file with a different group.
+   */
+  function testDifferentGroup() {
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('group' => JS_THEME);
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertEqual($javascript['core/misc/collapse.js']['group'], JS_THEME, 'Adding a JavaScript file with a different group caches the given group.');
+  }
+
+  /**
+   * Tests adding a JavaScript file with a different weight.
+   */
+  function testDifferentWeight() {
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('weight' => 2);
+    drupal_render($attached);
+    $javascript = _drupal_add_js();
+    $this->assertEqual($javascript['core/misc/collapse.js']['weight'], 2, 'Adding a JavaScript file with a different weight caches the given weight.');
+  }
+
+  /**
+   * Tests adding JavaScript within conditional comments.
+   *
+   * @see drupal_pre_render_conditional_comments()
+   */
+  function testBrowserConditionalComments() {
+    $default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
+
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array(
+      'browsers' => array('IE' => 'lte IE 8', '!IE' => FALSE),
+    );
+    $attached['#attached']['js'][] = array(
+      'type' => 'inline',
+      'data' => 'jQuery(function () { });',
+      'browsers' => array('IE' => FALSE),
+    );
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+
+    $expected_1 = "<!--[if lte IE 8]>\n" . '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>' . "\n<![endif]-->";
+    $expected_2 = "<!--[if !IE]><!-->\n" . '<script>' . "\n<!--//--><![CDATA[//><!--\n" . 'jQuery(function () { });' . "\n//--><!]]>\n" . '</script>' . "\n<!--<![endif]-->";
+
+    $this->assertTrue(strpos($javascript, $expected_1) > 0, 'Rendered JavaScript within downlevel-hidden conditional comments.');
+    $this->assertTrue(strpos($javascript, $expected_2) > 0, 'Rendered JavaScript within downlevel-revealed conditional comments.');
+  }
+
+  /**
+   * Tests JavaScript versioning.
+   */
+  function testVersionQueryString() {
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('version' => 'foo');
+    $attached['#attached']['js']['core/misc/ajax.js'] = array('version' => 'bar');
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'core/misc/collapse.js?v=foo') > 0 && strpos($javascript, 'core/misc/ajax.js?v=bar') > 0 , 'JavaScript version identifiers correctly appended to URLs');
+  }
+
+  /**
+   * Tests JavaScript grouping and aggregation.
+   */
+  function testAggregation() {
+    $default_query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
+
+    // To optimize aggregation, items with the 'every_page' option are ordered
+    // ahead of ones without. The order of JavaScript execution must be the
+    // same regardless of whether aggregation is enabled, so ensure this
+    // expected order, first with aggregation off.
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/ajax.js'] = array();
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('every_page' => TRUE);
+    $attached['#attached']['js']['core/misc/autocomplete.js'] = array();
+    $attached['#attached']['js']['core/misc/batch.js'] = array('every_page' => TRUE);
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+    $expected = implode("\n", array(
+      '<script src="' . file_create_url('core/misc/collapse.js') . '?' . $default_query_string . '"></script>',
+      '<script src="' . file_create_url('core/misc/batch.js') . '?' . $default_query_string . '"></script>',
+      '<script src="' . file_create_url('core/misc/ajax.js') . '?' . $default_query_string . '"></script>',
+      '<script src="' . file_create_url('core/misc/autocomplete.js') . '?' . $default_query_string . '"></script>',
+    ));
+    $this->assertTrue(strpos($javascript, $expected) > 0, 'Unaggregated JavaScript is added in the expected group order.');
+
+    // Now ensure that with aggregation on, one file is made for the
+    // 'every_page' files, and one file is made for the others.
+    drupal_static_reset('_drupal_add_js');
+    $config = \Drupal::config('system.performance');
+    $config->set('js.preprocess', 1);
+    $config->save();
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/ajax.js'] = array();
+    $attached['#attached']['js']['core/misc/collapse.js'] = array('every_page' => TRUE);
+    $attached['#attached']['js']['core/misc/autocomplete.js'] = array();
+    $attached['#attached']['js']['core/misc/batch.js'] = array('every_page' => TRUE);
+    drupal_render($attached);
+    $js_items = _drupal_add_js();
+    $javascript = drupal_get_js();
+    $expected = implode("\n", array(
+      '<script src="' . $this->calculateAggregateFilename(array('core/misc/collapse.js' => $js_items['core/misc/collapse.js'], 'core/misc/batch.js' => $js_items['core/misc/batch.js'])) . '"></script>',
+      '<script src="' . $this->calculateAggregateFilename(array('core/misc/ajax.js' => $js_items['core/misc/ajax.js'], 'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js'])) . '"></script>',
+    ));
+    $this->assertTrue(strpos($javascript, $expected) !== FALSE, 'JavaScript is aggregated in the expected groups and order.');
+  }
+
+  /**
+   * Tests JavaScript aggregation when files are added to a different scope.
+   */
+  function testAggregationOrder() {
+    // Enable JavaScript aggregation.
+    \Drupal::config('system.performance')->set('js.preprocess', 1)->save();
+    drupal_static_reset('_drupal_add_js');
+
+    // Add two JavaScript files to the current request and build the cache.
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['core/misc/ajax.js'] = array();
+    $attached['#attached']['js']['core/misc/autocomplete.js'] = array();
+    drupal_render($attached);
+
+    $js_items = _drupal_add_js();
+    $scripts_html = array(
+      '#type' => 'scripts',
+      '#items' => array(
+        'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
+        'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
+      )
+    );
+    drupal_render($scripts_html);
+
+    // Store the expected key for the first item in the cache.
+    $cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
+    $expected_key = $cache[0];
+
+    // Reset variables and add a file in a different scope first.
+    \Drupal::state()->delete('system.js_cache_files');
+    drupal_static_reset('_drupal_add_js');
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/drupal';
+    $attached['#attached']['js']['some/custom/javascript_file.js'] = array('scope' => 'footer');
+    $attached['#attached']['js']['core/misc/ajax.js'] = array();
+    $attached['#attached']['js']['core/misc/autocomplete.js'] = array();
+    drupal_render($attached);
+
+    // Rebuild the cache.
+    $js_items = _drupal_add_js();
+    $scripts_html = array(
+      '#type' => 'scripts',
+      '#items' => array(
+        'core/misc/ajax.js' => $js_items['core/misc/ajax.js'],
+        'core/misc/autocomplete.js' => $js_items['core/misc/autocomplete.js']
+      )
+    );
+    drupal_render($scripts_html);
+
+    // Compare the expected key for the first file to the current one.
+    $cache = array_keys(\Drupal::state()->get('system.js_cache_files') ?: array());
+    $key = $cache[0];
+    $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.');
+  }
+
+  /**
+   * Tests JavaScript ordering.
+   */
+  function testRenderOrder() {
+    $shared_options = array(
+      'type' => 'inline',
+      'scope' => 'footer',
+    );
+    // Add a bunch of JavaScript in strange ordering.
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight 5 #1");})(jQuery);',
+      'weight' => 5,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight 0 #1");})(jQuery);',
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight 0 #2");})(jQuery);',
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight -8 #1");})(jQuery);',
+      'weight' => -8,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight -8 #2");})(jQuery);',
+      'weight' => -8,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight -8 #3");})(jQuery);',
+      'weight' => -8,
+    );
+    $attached['#attached']['js']['http://example.com/example.js?Weight -5 #1'] = array(
+      'type' => 'external',
+      'scope' => 'footer',
+      'weight' => -5,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight -8 #4");})(jQuery);',
+      'weight' => -8,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight 5 #2");})(jQuery);',
+      'weight' => 5,
+    );
+    $attached['#attached']['js'][] = $shared_options + array(
+      'data' => '(function($){alert("Weight 0 #3");})(jQuery);',
+    );
+    drupal_render($attached);
+
+    // Construct the expected result from the regex.
+    $expected = array(
+      "-8 #1",
+      "-8 #2",
+      "-8 #3",
+      "-8 #4",
+      "-5 #1", // The external script.
+      "0 #1",
+      "0 #2",
+      "0 #3",
+      "5 #1",
+      "5 #2",
+    );
+
+    // Retrieve the rendered JavaScript and test against the regex.
+    $js = drupal_get_js('footer');
+    $matches = array();
+    if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
+      $result = $matches[1];
+    }
+    else {
+      $result = array();
+    }
+    $this->assertIdentical($result, $expected, 'JavaScript is added in the expected weight order.');
+  }
+
+  /**
+   * Tests rendering the JavaScript with a file's weight above jQuery's.
+   */
+  function testRenderDifferentWeight() {
+    // JavaScript files are sorted first by group, then by the 'every_page'
+    // flag, then by weight (see drupal_sort_css_js()), so to test the effect of
+    // weight, we need the other two options to be the same.
+    $attached['#attached']['library'][] = 'core/jquery';
+    $attached['#attached']['js']['core/misc/collapse.js'] = array(
+      'group' => JS_LIBRARY,
+      'every_page' => TRUE,
+      'weight' => -21,
+    );
+    drupal_render($attached);
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'core/misc/collapse.js') < strpos($javascript, 'core/assets/vendor/jquery/jquery.js'), 'Rendering a JavaScript file above jQuery.');
+  }
+
+  /**
+   * Tests altering a JavaScript's weight via hook_js_alter().
+   *
+   * @see simpletest_js_alter()
+   */
+  function testAlter() {
+    // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
+    $attached['#attached']['js']['core/misc/tableselect.js'] = array();
+    $attached['#attached']['js'][drupal_get_path('module', 'simpletest') . '/simpletest.js'] = array('weight' => 9999);
+    drupal_render($attached);
+
+    // Render the JavaScript, testing if simpletest.js was altered to be before
+    // tableselect.js. See simpletest_js_alter() to see where this alteration
+    // takes place.
+    $javascript = drupal_get_js();
+    $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'core/misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
+  }
+
+  /**
+   * Adds a library to the page and tests for both its JavaScript and its CSS.
+   */
+  function testLibraryRender() {
+    $attached = array();
+    $attached['#attached']['library'][] = 'core/jquery.ui';
+    drupal_render($attached);
+    $scripts = drupal_get_js();
+    $styles = drupal_get_css();
+    $this->assertTrue(strpos($scripts, 'core/assets/vendor/jquery.ui/ui/jquery.ui.core.js'), 'JavaScript of library was added to the page.');
+    $this->assertTrue(strpos($styles, 'core/assets/vendor/jquery.ui/themes/base/jquery.ui.core.css'), 'Stylesheet of library was added to the page.');
+  }
+
+  /**
+   * Adds a JavaScript library to the page and alters it.
+   *
+   * @see common_test_library_info_alter()
+   */
+  function testLibraryAlter() {
+    // Verify that common_test altered the title of jQuery UI.
+    /** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
+    $library_discovery = \Drupal::service('library.discovery');
+    $library = $library_discovery->getLibraryByName('core', 'jquery.ui');
+    $this->assertEqual($library['version'], '0.0', 'Registered libraries were altered.');
+
+    // common_test_library_info_alter() also added a dependency on jQuery Form.
+    $attached['#attached']['library'][] = 'core/jquery.ui';
+    drupal_render($attached);
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'core/assets/vendor/jquery-form/jquery.form.js'), 'Altered library dependencies are added to the page.');
+  }
+
+  /**
+   * Tests that multiple modules can implement the same library.
+   *
+   * @see common_test.library.yml
+   */
+  function testLibraryNameConflicts() {
+    /** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
+    $library_discovery = \Drupal::service('library.discovery');
+    $jquery_ui = $library_discovery->getLibraryByName('common_test', 'jquery.ui');
+    $this->assertEqual($jquery_ui['version'], '0.1', 'Alternative libraries can be added to the page.');
+  }
+
+  /**
+   * Tests non-existing libraries.
+   */
+  function testLibraryUnknown() {
+    /** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
+    $library_discovery = \Drupal::service('library.discovery');
+    $result = $library_discovery->getLibraryByName('unknown', 'unknown');
+    $this->assertFalse($result, 'Unknown library returned FALSE.');
+    drupal_static_reset('drupal_get_library');
+
+    $attached['#attached']['library'][] = 'unknown/unknown';
+    drupal_render($attached);
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'unknown') === FALSE, 'Unknown library was not added to the page.');
+  }
+
+  /**
+   * Tests the addition of libraries through the #attached['library'] property.
+   */
+  function testAttachedLibrary() {
+    $element['#attached']['library'][] = 'core/jquery.ui';
+    drupal_render($element);
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, 'core/assets/vendor/jquery.ui/ui/jquery.ui.core.js'), 'The attached_library property adds the additional libraries.');
+  }
+
+  /**
+   * Tests retrieval of libraries via drupal_get_library().
+   */
+  function testGetLibrary() {
+    /** @var \Drupal\Core\Asset\LibraryDiscoveryInterface $library_discovery */
+    $library_discovery = \Drupal::service('library.discovery');
+    // Retrieve all libraries registered by a module.
+    $libraries = $library_discovery->getLibrariesByExtension('common_test');
+    $this->assertTrue(isset($libraries['jquery.ui']), 'Retrieved all module libraries.');
+    // Retrieve all libraries for a module not declaring any libraries.
+    // Note: This test installs language module.
+    $libraries = $library_discovery->getLibrariesByExtension('dblog');
+    $this->assertEqual($libraries, array(), 'Retrieving libraries from a module not declaring any libraries returns an empty array.');
+
+    // Retrieve a specific library by module and name.
+    $jquery_ui = $library_discovery->getLibraryByName('common_test', 'jquery.ui');
+    $this->assertEqual($jquery_ui['version'], '0.1', 'Retrieved a single library.');
+    // Retrieve a non-existing library by module and name.
+    $jquery_ui = $library_discovery->getLibraryByName('common_test', 'foo');
+    $this->assertIdentical($jquery_ui, FALSE, 'Retrieving a non-existing library returns FALSE.');
+  }
+
+  /**
+   * Tests JavaScript files that have querystrings attached get added right.
+   */
+  function testAddJsFileWithQueryString() {
+    $js = drupal_get_path('module', 'node') . '/node.js';
+    _drupal_add_js($js);
+
+    $query_string = $this->container->get('state')->get('system.css_js_query_string') ?: '0';
+    $scripts = drupal_get_js();
+    $this->assertTrue(strpos($scripts, $js . '?' . $query_string), 'Query string was appended correctly to JS.');
+  }
+
+  /**
+   * Calculates the aggregated file URI of a group of JavaScript assets.
+   *
+   * @param array $js_assets
+   *   A group of JavaScript assets.
+   * @return string
+   *   A file URI.
+   *
+   * @see testAggregation()
+   * @see testAggregationOrder()
+   */
+  protected function calculateAggregateFilename($js_assets) {
+    $data = '';
+    foreach ($js_assets as $js_asset) {
+      $data .= file_get_contents($js_asset['data']) . ";\n";
+    }
+    return file_create_url('public://js/js_' . Crypt::hashBase64($data) . '.js');
+  }
+
+}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index b0eadb5..bed9ce5 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -16,7 +16,1502 @@
  */
 
 /**
- * Flush all persistent and static caches.
+ * Defines one or more hooks that are exposed by a module.
+ *
+ * Normally hooks do not need to be explicitly defined. However, by declaring a
+ * hook explicitly, a module may define a "group" for it. Modules that implement
+ * a hook may then place their implementation in either $module.module or in
+ * $module.$group.inc. If the hook is located in $module.$group.inc, then that
+ * file will be automatically loaded when needed.
+ * In general, hooks that are rarely invoked and/or are very large should be
+ * placed in a separate include file, while hooks that are very short or very
+ * frequently called should be left in the main module file so that they are
+ * always available.
+ *
+ * @return
+ *   An associative array whose keys are hook names and whose values are an
+ *   associative array containing:
+ *   - group: A string defining the group to which the hook belongs. The module
+ *     system will determine whether a file with the name $module.$group.inc
+ *     exists, and automatically load it when required.
+ *
+ * See system_hook_info() for all hook groups defined by Drupal core.
+ *
+ * @see hook_hook_info_alter().
+ */
+function hook_hook_info() {
+  $hooks['token_info'] = array(
+    'group' => 'tokens',
+  );
+  $hooks['tokens'] = array(
+    'group' => 'tokens',
+  );
+  return $hooks;
+}
+
+/**
+ * Perform periodic actions.
+ *
+ * Modules that require some commands to be executed periodically can
+ * implement hook_cron(). The engine will then call the hook whenever a cron
+ * run happens, as defined by the administrator. Typical tasks managed by
+ * hook_cron() are database maintenance, backups, recalculation of settings
+ * or parameters, automated mailing, and retrieving remote data.
+ *
+ * Short-running or non-resource-intensive tasks can be executed directly in
+ * the hook_cron() implementation.
+ *
+ * Long-running tasks and tasks that could time out, such as retrieving remote
+ * data, sending email, and intensive file tasks, should use the queue API
+ * instead of executing the tasks directly. To do this, first define one or
+ * more queues via hook_queue_info(). Then, add items that need to be
+ * processed to the defined queues.
+ */
+function hook_cron() {
+  // Short-running operation example, not using a queue:
+  // Delete all expired records since the last cron run.
+  $expires = \Drupal::state()->get('mymodule.cron_last_run', REQUEST_TIME);
+  db_delete('mymodule_table')
+    ->condition('expires', $expires, '>=')
+    ->execute();
+  \Drupal::state()->set('mymodule.cron_last_run', REQUEST_TIME);
+
+  // Long-running operation example, leveraging a queue:
+  // Fetch feeds from other sites.
+  $result = db_query('SELECT * FROM {aggregator_feed} WHERE checked + refresh < :time AND refresh <> :never', array(
+    ':time' => REQUEST_TIME,
+    ':never' => AGGREGATOR_CLEAR_NEVER,
+  ));
+  $queue = \Drupal::queue('aggregator_feeds');
+  foreach ($result as $feed) {
+    $queue->createItem($feed);
+  }
+}
+
+/**
+ * Alter available data types for typed data wrappers.
+ *
+ * @param array $data_types
+ *   An array of data type information.
+ *
+ * @see hook_data_type_info()
+ */
+function hook_data_type_info_alter(&$data_types) {
+  $data_types['email']['class'] = '\Drupal\mymodule\Type\Email';
+}
+
+/**
+ * Declare queues holding items that need to be run periodically.
+ *
+ * While there can be only one hook_cron() process running at the same time,
+ * there can be any number of processes defined here running. Because of
+ * this, long running tasks are much better suited for this API. Items queued
+ * in hook_cron() might be processed in the same cron run if there are not many
+ * items in the queue, otherwise it might take several requests, which can be
+ * run in parallel.
+ *
+ * You can create queues, add items to them, claim them, etc without declaring
+ * the queue in this hook if you want, however, you need to take care of
+ * processing the items in the queue in that case.
+ *
+ * @return
+ *   An associative array where the key is the queue name and the value is
+ *   again an associative array. Possible keys are:
+ *   - 'worker callback': A PHP callable to call that is an implementation of
+ *     callback_queue_worker().
+ *   - 'cron': (optional) An associative array containing the optional key:
+ *     - 'time': (optional) How much time Drupal cron should spend on calling
+ *       this worker in seconds. Defaults to 15.
+ *     If the cron key is not defined, the queue will not be processed by cron,
+ *     and must be processed by other means.
+ *
+ * @see hook_cron()
+ * @see hook_queue_info_alter()
+ */
+function hook_queue_info() {
+  $queues['aggregator_feeds'] = array(
+    'title' => t('Aggregator refresh'),
+    'worker callback' => array('Drupal\my_module\MyClass', 'aggregatorRefresh'),
+    // Only needed if this queue should be processed by cron.
+    'cron' => array(
+      'time' => 60,
+    ),
+  );
+  return $queues;
+}
+
+/**
+ * Alter cron queue information before cron runs.
+ *
+ * Called by \Drupal\Core\Cron to allow modules to alter cron queue settings
+ * before any jobs are processesed.
+ *
+ * @param array $queues
+ *   An array of cron queue information.
+ *
+ * @see hook_queue_info()
+ * @see \Drupal\Core\Cron
+ */
+function hook_queue_info_alter(&$queues) {
+  // This site has many feeds so let's spend 90 seconds on each cron run
+  // updating feeds instead of the default 60.
+  $queues['aggregator_feeds']['cron']['time'] = 90;
+}
+
+/**
+ * Work on a single queue item.
+ *
+ * Callback for hook_queue_info().
+ *
+ * @param $queue_item_data
+ *   The data that was passed to \Drupal\Core\Queue\QueueInterface::createItem()
+ *   when the item was queued.
+ *
+ * @throws \Exception
+ *   The worker callback may throw an exception to indicate there was a problem.
+ *   The cron process will log the exception, and leave the item in the queue to
+ *   be processed again later.
+ *
+ * @see \Drupal\Core\Cron::run()
+ */
+function callback_queue_worker($queue_item_data) {
+  $node = node_load($queue_item_data);
+  $node->title = 'Updated title';
+  $node->save();
+}
+
+/**
+ * Allows modules to declare their own Form API element types and specify their
+ * default values.
+ *
+ * This hook allows modules to declare their own form element types and to
+ * specify their default values. The values returned by this hook will be
+ * merged with the elements returned by form constructor implementations and so
+ * can return defaults for any Form APIs keys in addition to those explicitly
+ * documented by \Drupal\Core\Render\ElementInfoInterface::getInfo().
+ *
+ * @return array
+ *   An associative array with structure identical to that of the return value
+ *   of \Drupal\Core\Render\ElementInfoInterface::getInfo().
+ *
+ * @see hook_element_info_alter()
+ * @see system_element_info()
+ */
+function hook_element_info() {
+  $types['filter_format'] = array(
+    '#input' => TRUE,
+  );
+  return $types;
+}
+
+/**
+ * Alter the element type information returned from modules.
+ *
+ * A module may implement this hook in order to alter the element type defaults
+ * defined by a module.
+ *
+ * @param array $types
+ *   An associative array with structure identical to that of the return value
+ *   of \Drupal\Core\Render\ElementInfoInterface::getInfo().
+ *
+ * @see hook_element_info()
+ */
+function hook_element_info_alter(array &$types) {
+  // Decrease the default size of textfields.
+  if (isset($types['textfield']['#size'])) {
+    $types['textfield']['#size'] = 40;
+  }
+}
+
+/**
+ * Perform necessary alterations to the JavaScript before it is presented on
+ * the page.
+ *
+ * @param $javascript
+ *   An array of all JavaScript being presented on the page.
+ *
+ * @see _drupal_add_js()
+ * @see drupal_get_js()
+ * @see drupal_js_defaults()
+ */
+function hook_js_alter(&$javascript) {
+  // Swap out jQuery to use an updated version of the library.
+  $javascript['core/assets/vendor/jquery/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js';
+}
+
+/**
+ * Alters the JavaScript/CSS library registry.
+ *
+ * Allows certain, contributed modules to update libraries to newer versions
+ * while ensuring backwards compatibility. In general, such manipulations should
+ * only be done by designated modules, since most modules that integrate with a
+ * certain library also depend on the API of a certain library version.
+ *
+ * @param $libraries
+ *   The JavaScript/CSS libraries provided by $module. Keyed by internal library
+ *   name and passed by reference.
+ * @param $module
+ *   The name of the module that registered the libraries.
+ */
+function hook_library_info_alter(&$libraries, $module) {
+  // Update jQuery UI to version 2.0.
+  if ($module == 'core' && isset($libraries['jquery.ui'])) {
+    // Verify existing version is older than the one we are updating to.
+    if (version_compare($libraries['jquery.ui']['version'], '2.0', '<')) {
+      // Update the existing jQuery UI to version 2.0.
+      $libraries['jquery.ui']['version'] = '2.0';
+      // To accurately replace library files, the order of files and the options
+      // of each file have to be retained; e.g., like this:
+      $old_path = 'assets/vendor/jquery.ui';
+      // Since the replaced library files are no longer located in a directory
+      // relative to the original extension, specify an absolute path (relative
+      // to DRUPAL_ROOT / base_path()) to the new location.
+      $new_path = '/' . drupal_get_path('module', 'jquery.ui_update') . '/js';
+      $new_js = array();
+      $replacements = array(
+        $old_path . '/jquery.ui.js' => $new_path . '/jquery.ui-2.0.js',
+      );
+      foreach ($libraries['jquery.ui']['js'] as $source => $options) {
+        if (isset($replacements[$source])) {
+          $new_js[$replacements[$source]] = $options;
+        }
+        else {
+          $new_js[$source] = $options;
+        }
+      }
+      $libraries['jquery.ui']['js'] = $new_js;
+    }
+  }
+}
+
+/**
+ * Alters a JavaScript/CSS library before it is attached.
+ *
+ * Allows modules and themes to dynamically attach further assets to a library
+ * when it is added to the page; e.g., to add JavaScript settings.
+ *
+ * This hook is only invoked once per library and page.
+ *
+ * @param array $library
+ *   The JavaScript/CSS library that is being added.
+ * @param string $extension
+ *   The name of the extension that registered the library.
+ * @param string $name
+ *   The name of the library.
+ *
+ * @see _drupal_add_library()
+ */
+function hook_library_alter(array &$library, $name) {
+  if ($name == 'core/jquery.ui.datepicker') {
+    // Note: If the added assets do not depend on additional request-specific
+    // data supplied here, consider to statically register it directly via
+    // hook_library_info_alter() already.
+    $library['dependencies'][] = 'locale/drupal.locale.datepicker';
+
+    $language_interface = \Drupal::languageManager()->getCurrentLanguage();
+    $settings['jquery']['ui']['datepicker'] = array(
+      'isRTL' => $language_interface->direction == Language::DIRECTION_RTL,
+      'firstDay' => \Drupal::config('system.date')->get('first_day'),
+    );
+    $library['js'][] = array(
+      'type' => 'setting',
+      'data' => $settings,
+    );
+  }
+}
+
+/**
+ * Alter CSS files before they are output on the page.
+ *
+ * @param $css
+ *   An array of all CSS items (files and inline CSS) being requested on the page.
+ *
+ * @see _drupal_add_css()
+ * @see drupal_get_css()
+ */
+function hook_css_alter(&$css) {
+  // Remove defaults.css file.
+  unset($css[drupal_get_path('module', 'system') . '/defaults.css']);
+}
+
+/**
+ * Alter the Ajax command data that is sent to the client.
+ *
+ * @param \Drupal\Core\Ajax\CommandInterface[] $data
+ *   An array of all the rendered commands that will be sent to the client.
+ *
+ * @see \Drupal\Core\Ajax\AjaxResponse::ajaxRender()
+ */
+function hook_ajax_render_alter(array &$data) {
+  // Inject any new status messages into the content area.
+  $status_messages = array('#theme' => 'status_messages');
+  $command = new \Drupal\Core\Ajax\PrependCommand('#block-system-main .content', drupal_render($status_messages));
+  $data[] = $command->render();
+}
+
+/**
+ * Add elements to a page before it is rendered.
+ *
+ * Use this hook when you want to add elements at the page level. For your
+ * additions to be printed, they have to be placed below a top level array key
+ * of the $page array that has the name of a region of the active theme.
+ *
+ * By default, valid region keys are 'page_top', 'header', 'sidebar_first',
+ * 'content', 'sidebar_second' and 'page_bottom'. To get a list of all regions
+ * of the active theme, use system_region_list($theme). Note that $theme is a
+ * global variable.
+ *
+ * If you want to alter the elements added by other modules or if your module
+ * depends on the elements of other modules, use hook_page_alter() instead which
+ * runs after this hook.
+ *
+ * @param $page
+ *   Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_alter()
+ * @see drupal_render_page()
+ */
+function hook_page_build(&$page) {
+  $path = drupal_get_path('module', 'foo');
+  // Add JavaScript/CSS assets to all pages.
+  // @see drupal_process_attached()
+  $page['#attached']['js'][$path . '/foo.js'] = array('every_page' => TRUE);
+  $page['#attached']['css'][$path . '/foo.base.css'] = array('every_page' => TRUE);
+  $page['#attached']['css'][$path . '/foo.theme.css'] = array('every_page' => TRUE);
+
+  // Add a special CSS file to a certain page only.
+  if (drupal_is_front_page()) {
+    $page['#attached']['css'][] = $path . '/foo.front.css';
+  }
+
+  // Append a standard disclaimer to the content region on a node detail page.
+  if (\Drupal::request()->attributes->get('node')) {
+    $page['content']['disclaimer'] = array(
+      '#markup' => t('Acme, Inc. is not responsible for the contents of this sample code.'),
+      '#weight' => 25,
+    );
+  }
+}
+
+/**
+ * Alter links for menus.
+ *
+ * @param array $links
+ *   The link definitions to be altered.
+ *
+ * @return array
+ *   An array of default menu links. Each link has a key that is the machine
+ *   name, which must be unique. By default, use the route name as the
+ *   machine name. In cases where multiple links use the same route name, such
+ *   as two links to the same page in different menus, or two links using the
+ *   same route name but different route parameters, the suggested machine name
+ *   patten is the route name followed by a dot and a unique suffix. For
+ *   example, an additional logout link might have a machine name of
+ *   user.logout.navigation, and default links provided to edit the article and
+ *   page content types could use machine names node.type_edit.article and
+ *   node.type_edit.page. Since the machine name may be arbitrary, you should
+ *   never write code that assumes it is identical to the route name.
+ *
+ *   The value corresponding to each machine name key is an associative array
+ *   that may contain the following key-value pairs:
+ *   - title: (required) The untranslated title of the menu link.
+ *   - description: The untranslated description of the link.
+ *   - route_name: (optional) The route name to be used to build the path.
+ *     Either a route_name or a link_path must be provided.
+ *   - route_parameters: (optional) The route parameters to build the path.
+ *   - link_path: (optional) If you have an external link use link_path instead
+ *     of providing a route_name.
+ *   - parent: (optional) The machine name of the link that is this link's menu
+ *     parent.
+ *   - weight: (optional) An integer that determines the relative position of
+ *     items in the menu; higher-weighted items sink. Defaults to 0. Menu items
+ *     with the same weight are ordered alphabetically.
+ *   - menu_name: (optional) The machine name of a menu to put the link in, if
+ *     not the default Tools menu.
+ *   - expanded: (optional) If set to TRUE, and if a menu link is provided for
+ *     this menu item (as a result of other properties), then the menu link is
+ *     always expanded, equivalent to its 'always expanded' checkbox being set
+ *     in the UI.
+ *   - options: (optional) An array of options to be passed to l() when
+ *     generating a link from this menu item.
+ */
+function hook_menu_link_defaults_alter(&$links) {
+  // Change the weight and title of the user.logout link.
+  $links['user.logout']['weight'] = -10;
+  $links['user.logout']['title'] = 'Logout';
+}
+
+/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Additional tabs or actions may be added.
+ *
+ * Each tab or action is an associative array containing:
+ * - #theme: The theme function to use to render.
+ * - #link: An associative array containing:
+ *   - title: The localized title of the link.
+ *   - href: The system path to link to.
+ *   - localized_options: An array of options to pass to l().
+ * - #weight: The link's weight compared to other links.
+ * - #active: Whether the link should be marked as 'active'.
+ *
+ * @param array $data
+ *   An associative array containing:
+ *   - actions: A list of of actions keyed by their href, each one being an
+ *     associative array as described above.
+ *   - tabs: A list of (up to 2) tab levels that contain a list of of tabs keyed
+ *     by their href, each one being an associative array as described above.
+ * @param string $route_name
+ *   The route name of the page.
+ */
+function hook_menu_local_tasks(&$data, $route_name) {
+  // Add an action linking to node/add to all pages.
+  $data['actions']['node/add'] = array(
+    '#theme' => 'menu_local_action',
+    '#link' => array(
+      'title' => t('Add content'),
+      'href' => 'node/add',
+      'localized_options' => array(
+        'attributes' => array(
+          'title' => t('Add content'),
+        ),
+      ),
+    ),
+  );
+
+  // Add a tab linking to node/add to all pages.
+  $data['tabs'][0]['node/add'] = array(
+    '#theme' => 'menu_local_task',
+    '#link' => array(
+      'title' => t('Example tab'),
+      'href' => 'node/add',
+      'localized_options' => array(
+        'attributes' => array(
+          'title' => t('Add content'),
+        ),
+      ),
+    ),
+  );
+}
+
+/**
+ * Alter tabs and actions displayed on the page before they are rendered.
+ *
+ * This hook is invoked by menu_local_tasks(). The system-determined tabs and
+ * actions are passed in by reference. Existing tabs or actions may be altered.
+ *
+ * @param array $data
+ *   An associative array containing tabs and actions. See
+ *   hook_menu_local_tasks() for details.
+ * @param string $route_name
+ *   The route name of the page.
+ *
+ * @see hook_menu_local_tasks()
+ */
+function hook_menu_local_tasks_alter(&$data, $route_name) {
+}
+
+/**
+ * Alter local actions plugins.
+ *
+ * @param array $local_actions
+ *   The array of local action plugin definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Menu\LocalActionInterface
+ * @see \Drupal\Core\Menu\LocalActionManager
+ */
+function hook_menu_local_actions_alter(&$local_actions) {
+}
+
+/**
+ * Alter local tasks plugins.
+ *
+ * @param array $local_tasks
+ *   The array of local tasks plugin definitions, keyed by plugin ID.
+ *
+ * @see \Drupal\Core\Menu\LocalTaskInterface
+ * @see \Drupal\Core\Menu\LocalTaskManager
+ */
+function hook_local_tasks_alter(&$local_tasks) {
+  // Remove a specified local task plugin.
+  unset($local_tasks['example_plugin_id']);
+}
+
+/**
+ * Alter contextual links before they are rendered.
+ *
+ * This hook is invoked by
+ * \Drupal\Core\Menu\ContextualLinkManager::getContextualLinkPluginsByGroup().
+ * The system-determined contextual links are passed in by reference. Additional
+ * links may be added and existing links can be altered.
+ *
+ * Each contextual link contains the following entries:
+ * - title: The localized title of the link.
+ * - route_name: The route name of the link.
+ * - route_parameters: The route parameters of the link.
+ * - localized_options: An array of options to pass to url().
+ * - (optional) weight: The weight of the link, which is used to sort the links.
+ *
+ *
+ * @param array $links
+ *   An associative array containing contextual links for the given $group,
+ *   as described above. The array keys are used to build CSS class names for
+ *   contextual links and must therefore be unique for each set of contextual
+ *   links.
+ * @param string $group
+ *   The group of contextual links being rendered.
+ * @param array $route_parameters.
+ *   The route parameters passed to each route_name of the contextual links.
+ *   For example:
+ *   @code
+ *   array('node' => $node->id())
+ *   @endcode
+ *
+ * @see \Drupal\Core\Menu\ContextualLinkManager
+ */
+function hook_contextual_links_alter(array &$links, $group, array $route_parameters) {
+  if ($group == 'menu') {
+    // Dynamically use the menu name for the title of the menu_edit contextual
+    // link.
+    $menu = \Drupal::entityManager()->getStorage('menu')->load($route_parameters['menu']);
+    $links['menu_edit']['title'] = t('Edit menu: !label', array('!label' => $menu->label()));
+  }
+}
+
+/**
+ * Alter the plugin definition of contextual links.
+ *
+ * @param array $contextual_links
+ *   An array of contextual_links plugin definitions, keyed by contextual link
+ *   ID. Each entry contains the following keys:
+ *     - title: The displayed title of the link
+ *     - route_name: The route_name of the contextual link to be displayed
+ *     - group: The group under which the contextual links should be added to.
+ *       Possible values are e.g. 'node' or 'menu'.
+ *
+ * @see \Drupal\Core\Menu\ContextualLinkManager
+ */
+function hook_contextual_links_plugins_alter(array &$contextual_links) {
+  $contextual_links['menu_edit']['title'] = 'Edit the menu';
+}
+
+/**
+ * Perform alterations before a page is rendered.
+ *
+ * Use this hook when you want to remove or alter elements at the page
+ * level, or add elements at the page level that depend on an other module's
+ * elements (this hook runs after hook_page_build().
+ *
+ * If you are making changes to entities such as forms, menus, or user
+ * profiles, use those objects' native alter hooks instead (hook_form_alter(),
+ * for example).
+ *
+ * The $page array contains top level elements for each block region:
+ * @code
+ *   $page['page_top']
+ *   $page['header']
+ *   $page['sidebar_first']
+ *   $page['content']
+ *   $page['sidebar_second']
+ *   $page['page_bottom']
+ * @endcode
+ *
+ * The 'content' element contains the main content of the current page, and its
+ * structure will vary depending on what module is responsible for building the
+ * page. Some legacy modules may not return structured content at all: their
+ * pre-rendered markup will be located in $page['content']['main']['#markup'].
+ *
+ * Pages built by Drupal's core Node module use a standard structure:
+ *
+ * @code
+ *   // Node body.
+ *   $page['content']['system_main']['nodes'][$nid]['body']
+ *   // Array of links attached to the node (add comments, read more).
+ *   $page['content']['system_main']['nodes'][$nid]['links']
+ *   // The node entity itself.
+ *   $page['content']['system_main']['nodes'][$nid]['#node']
+ *   // The results pager.
+ *   $page['content']['system_main']['pager']
+ * @endcode
+ *
+ * Blocks may be referenced by their module/delta pair within a region:
+ * @code
+ *   // The login block in the first sidebar region.
+ *   $page['sidebar_first']['user_login']['#block'];
+ * @endcode
+ *
+ * @param $page
+ *   Nested array of renderable elements that make up the page.
+ *
+ * @see hook_page_build()
+ * @see drupal_render_page()
+ */
+function hook_page_alter(&$page) {
+  // Add help text to the user login block.
+  $page['sidebar_first']['user_login']['help'] = array(
+    '#weight' => -10,
+    '#markup' => t('To post comments or add content, you first have to log in.'),
+  );
+}
+
+/**
+ * Perform alterations before a form is rendered.
+ *
+ * One popular use of this hook is to add form elements to the node form. When
+ * altering a node form, the node entity can be retrieved by invoking
+ * $form_state['controller']->getEntity().
+ *
+ * In addition to hook_form_alter(), which is called for all forms, there are
+ * two more specific form hooks available. The first,
+ * hook_form_BASE_FORM_ID_alter(), allows targeting of a form/forms via a base
+ * form (if one exists). The second, hook_form_FORM_ID_alter(), can be used to
+ * target a specific form directly.
+ *
+ * The call order is as follows: all existing form alter functions are called
+ * for module A, then all for module B, etc., followed by all for any base
+ * theme(s), and finally for the theme itself. The module order is determined
+ * by system weight, then by module name.
+ *
+ * Within each module, form alter hooks are called in the following order:
+ * first, hook_form_alter(); second, hook_form_BASE_FORM_ID_alter(); third,
+ * hook_form_FORM_ID_alter(). So, for each module, the more general hooks are
+ * called first followed by the more specific.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form. The arguments
+ *   that \Drupal::formBuilder()->getForm() was originally called with are
+ *   available in the array $form_state['build_info']['args'].
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see forms_api_reference.html
+ */
+function hook_form_alter(&$form, &$form_state, $form_id) {
+  if (isset($form['type']) && $form['type']['#value'] . '_node_settings' == $form_id) {
+    $upload_enabled_types = \Drupal::config('mymodule.settings')->get('upload_enabled_types');
+    $form['workflow']['upload_' . $form['type']['#value']] = array(
+      '#type' => 'radios',
+      '#title' => t('Attachments'),
+      '#default_value' => in_array($form['type']['#value'], $upload_enabled_types) ? 1 : 0,
+      '#options' => array(t('Disabled'), t('Enabled')),
+    );
+    // Add a custom submit handler to save the array of types back to the config file.
+    $form['actions']['submit']['#submit'][] = 'mymodule_upload_enabled_types_submit';
+  }
+}
+
+/**
+ * Provide a form-specific alteration instead of the global hook_form_alter().
+ *
+ * Modules can implement hook_form_FORM_ID_alter() to modify a specific form,
+ * rather than implementing hook_form_alter() and checking the form ID, or
+ * using long switch statements to alter multiple forms.
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form. The arguments
+ *   that \Drupal::formBuilder()->getForm() was originally called with are
+ *   available in the array $form_state['build_info']['args'].
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_BASE_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ * @see forms_api_reference.html
+ */
+function hook_form_FORM_ID_alter(&$form, &$form_state, $form_id) {
+  // Modification for the form with the given form ID goes here. For example, if
+  // FORM_ID is "user_register_form" this code would run only on the user
+  // registration form.
+
+  // Add a checkbox to registration form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
+ * Provide a form-specific alteration for shared ('base') forms.
+ *
+ * By default, when \Drupal::formBuilder()->getForm() is called, Drupal looks
+ * for a function with the same name as the form ID, and uses that function to
+ * build the form. In contrast, base forms allow multiple form IDs to be mapped
+ * to a single base (also called 'factory') form function.
+ *
+ * Modules can implement hook_form_BASE_FORM_ID_alter() to modify a specific
+ * base form, rather than implementing hook_form_alter() and checking for
+ * conditions that would identify the shared form constructor.
+ *
+ * To identify the base form ID for a particular form (or to determine whether
+ * one exists) check the $form_state. The base form ID is stored under
+ * $form_state['build_info']['base_form_id'].
+ *
+ * Form alter hooks are called in the following order: hook_form_alter(),
+ * hook_form_BASE_FORM_ID_alter(), hook_form_FORM_ID_alter(). See
+ * hook_form_alter() for more details.
+ *
+ * @param $form
+ *   Nested array of form elements that comprise the form.
+ * @param $form_state
+ *   A keyed array containing the current state of the form.
+ * @param $form_id
+ *   String representing the name of the form itself. Typically this is the
+ *   name of the function that generated the form.
+ *
+ * @see hook_form_alter()
+ * @see hook_form_FORM_ID_alter()
+ * @see drupal_prepare_form()
+ */
+function hook_form_BASE_FORM_ID_alter(&$form, &$form_state, $form_id) {
+  // Modification for the form with the given BASE_FORM_ID goes here. For
+  // example, if BASE_FORM_ID is "node_form", this code would run on every
+  // node form, regardless of node type.
+
+  // Add a checkbox to the node form about agreeing to terms of use.
+  $form['terms_of_use'] = array(
+    '#type' => 'checkbox',
+    '#title' => t("I agree with the website's terms and conditions."),
+    '#required' => TRUE,
+  );
+}
+
+/**
+ * Alter an email message created with the drupal_mail() function.
+ *
+ * hook_mail_alter() allows modification of email messages created and sent
+ * with drupal_mail(). Usage examples include adding and/or changing message
+ * text, message fields, and message headers.
+ *
+ * Email messages sent using functions other than drupal_mail() will not
+ * invoke hook_mail_alter(). For example, a contributed module directly
+ * calling the drupal_mail_system()->mail() or PHP mail() function
+ * will not invoke this hook. All core modules use drupal_mail() for
+ * messaging, it is best practice but not mandatory in contributed modules.
+ *
+ * @param $message
+ *   An array containing the message data. Keys in this array include:
+ *  - 'id':
+ *     The drupal_mail() id of the message. Look at module source code or
+ *     drupal_mail() for possible id values.
+ *  - 'to':
+ *     The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *  - 'from':
+ *     The address the message will be marked as being from, which is
+ *     either a custom address or the site-wide default email address.
+ *  - 'subject':
+ *     Subject of the email to be sent. This must not contain any newline
+ *     characters, or the email may not be sent properly.
+ *  - 'body':
+ *     An array of strings containing the message text. The message body is
+ *     created by concatenating the individual array strings into a single text
+ *     string using "\n\n" as a separator.
+ *  - 'headers':
+ *     Associative array containing mail headers, such as From, Sender,
+ *     MIME-Version, Content-Type, etc.
+ *  - 'params':
+ *     An array of optional parameters supplied by the caller of drupal_mail()
+ *     that is used to build the message before hook_mail_alter() is invoked.
+ *  - 'language':
+ *     The language object used to build the message before hook_mail_alter()
+ *     is invoked.
+ *  - 'send':
+ *     Set to FALSE to abort sending this email message.
+ *
+ * @see drupal_mail()
+ */
+function hook_mail_alter(&$message) {
+  if ($message['id'] == 'modulename_messagekey') {
+    if (!example_notifications_optin($message['to'], $message['id'])) {
+      // If the recipient has opted to not receive such messages, cancel
+      // sending.
+      $message['send'] = FALSE;
+      return;
+    }
+    $message['body'][] = "--\nMail sent out from " . \Drupal::config('system.site')->get('name');
+  }
+}
+
+/**
+ * Alter the registry of modules implementing a hook.
+ *
+ * This hook is invoked during \Drupal::moduleHandler()->getImplementations().
+ * A module may implement this hook in order to reorder the implementing
+ * modules, which are otherwise ordered by the module's system weight.
+ *
+ * Note that hooks invoked using \Drupal::moduleHandler->alter() can have
+ * multiple variations(such as hook_form_alter() and hook_form_FORM_ID_alter()).
+ * \Drupal::moduleHandler->alter() will call all such variants defined by a
+ * single module in turn. For the purposes of hook_module_implements_alter(),
+ * these variants are treated as a single hook. Thus, to ensure that your
+ * implementation of hook_form_FORM_ID_alter() is called at the right time,
+ * you will have to change the order of hook_form_alter() implementation in
+ * hook_module_implements_alter().
+ *
+ * @param $implementations
+ *   An array keyed by the module's name. The value of each item corresponds
+ *   to a $group, which is usually FALSE, unless the implementation is in a
+ *   file named $module.$group.inc.
+ * @param $hook
+ *   The name of the module hook being implemented.
+ */
+function hook_module_implements_alter(&$implementations, $hook) {
+  if ($hook == 'rdf_mapping') {
+    // Move my_module_rdf_mapping() to the end of the list.
+    // \Drupal::moduleHandler()->getImplementations()
+    // iterates through $implementations with a foreach loop which PHP iterates
+    // in the order that the items were added, so to move an item to the end of
+    // the array, we remove it and then add it.
+    $group = $implementations['my_module'];
+    unset($implementations['my_module']);
+    $implementations['my_module'] = $group;
+  }
+}
+
+/**
+ * Perform alterations to the breadcrumb built by the BreadcrumbManager.
+ *
+ * @param array $breadcrumb
+ *   An array of breadcrumb link a tags, returned by the breadcrumb manager
+ *   build method, for example
+ *   @code
+ *     array('<a href="/">Home</a>');
+ *   @endcode
+ * @param array $attributes
+ *   Attributes representing the current page, coming from
+ *   \Drupal::request()->attributes.
+ * @param array $context
+ *   May include the following key:
+ *   - builder: the instance of
+ *     \Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface that constructed this
+ *     breadcrumb, or NULL if no builder acted based on the current attributes.
+ */
+function hook_system_breadcrumb_alter(array &$breadcrumb, array $attributes, array $context) {
+  // Add an item to the end of the breadcrumb.
+  $breadcrumb[] = Drupal::l(t('Text'), 'example_route_name');
+}
+
+/**
+ * Alter the information parsed from module and theme .info.yml files
+ *
+ * This hook is invoked in _system_rebuild_module_data() and in
+ * _system_rebuild_theme_data(). A module may implement this hook in order to
+ * add to or alter the data generated by reading the .info.yml file with
+ * \Drupal\Core\Extension\InfoParser.
+ *
+ * @param array $info
+ *   The .info.yml file contents, passed by reference so that it can be altered.
+ * @param \Drupal\Core\Extension\Extension $file
+ *   Full information about the module or theme.
+ * @param string $type
+ *   Either 'module' or 'theme', depending on the type of .info.yml file that
+ *   was passed.
+ */
+function hook_system_info_alter(array &$info, \Drupal\Core\Extension\Extension $file, $type) {
+  // Only fill this in if the .info.yml file does not define a 'datestamp'.
+  if (empty($info['datestamp'])) {
+    $info['datestamp'] = $file->getMTime();
+  }
+}
+
+/**
+ * Define user permissions.
+ *
+ * This hook can supply permissions that the module defines, so that they
+ * can be selected on the user permissions page and used to grant or restrict
+ * access to actions the module performs.
+ *
+ * Permissions are checked using user_access().
+ *
+ * For a detailed usage example, see page_example.module.
+ *
+ * @return
+ *   An array whose keys are permission names and whose corresponding values
+ *   are arrays containing the following key-value pairs:
+ *   - title: The human-readable name of the permission, to be shown on the
+ *     permission administration page. This should be wrapped in the t()
+ *     function so it can be translated.
+ *   - description: (optional) A description of what the permission does. This
+ *     should be wrapped in the t() function so it can be translated.
+ *   - restrict access: (optional) A boolean which can be set to TRUE to
+ *     indicate that site administrators should restrict access to this
+ *     permission to trusted users. This should be used for permissions that
+ *     have inherent security risks across a variety of potential use cases
+ *     (for example, the "administer filters" and "bypass node access"
+ *     permissions provided by Drupal core). When set to TRUE, a standard
+ *     warning message defined in user_admin_permissions() and output via
+ *     theme_user_permission_description() will be associated with the
+ *     permission and displayed with it on the permission administration page.
+ *     Defaults to FALSE.
+ *   - warning: (optional) A translated warning message to display for this
+ *     permission on the permission administration page. This warning overrides
+ *     the automatic warning generated by 'restrict access' being set to TRUE.
+ *     This should rarely be used, since it is important for all permissions to
+ *     have a clear, consistent security warning that is the same across the
+ *     site. Use the 'description' key instead to provide any information that
+ *     is specific to the permission you are defining.
+ *
+ * @see theme_user_permission_description()
+ */
+function hook_permission() {
+  return array(
+    'administer my module' =>  array(
+      'title' => t('Administer my module'),
+      'description' => t('Perform administration tasks for my module.'),
+    ),
+  );
+}
+
+/**
+ * Register a module or theme's theme implementations.
+ *
+ * The implementations declared by this hook have several purposes:
+ * - They can specify how a particular render array is to be rendered as HTML.
+ *   This is usually the case if the theme function is assigned to the render
+ *   array's #theme property.
+ * - They can return HTML for default calls to _theme().
+ * - They can return HTML for calls to _theme() for a theme suggestion.
+ *
+ * @param array $existing
+ *   An array of existing implementations that may be used for override
+ *   purposes. This is primarily useful for themes that may wish to examine
+ *   existing implementations to extract data (such as arguments) so that
+ *   it may properly register its own, higher priority implementations.
+ * @param $type
+ *   Whether a theme, module, etc. is being processed. This is primarily useful
+ *   so that themes tell if they are the actual theme being called or a parent
+ *   theme. May be one of:
+ *   - 'module': A module is being checked for theme implementations.
+ *   - 'base_theme_engine': A theme engine is being checked for a theme that is
+ *     a parent of the actual theme being used.
+ *   - 'theme_engine': A theme engine is being checked for the actual theme
+ *     being used.
+ *   - 'base_theme': A base theme is being checked for theme implementations.
+ *   - 'theme': The actual theme in use is being checked.
+ * @param $theme
+ *   The actual name of theme, module, etc. that is being being processed.
+ * @param $path
+ *   The directory path of the theme or module, so that it doesn't need to be
+ *   looked up.
+ *
+ * @return array
+ *   An associative array of information about theme implementations. The keys
+ *   on the outer array are known as "theme hooks". For simple theme
+ *   implementations for regular calls to _theme(), the theme hook is the first
+ *   argument. For theme suggestions, instead of the array key being the base
+ *   theme hook, the key is a theme suggestion name with the format
+ *   'base_hook_name__sub_hook_name'. For render elements, the key is the
+ *   machine name of the render element. The array values are themselves arrays
+ *   containing information about the theme hook and its implementation. Each
+ *   information array must contain either a 'variables' element (for _theme()
+ *   calls) or a 'render element' element (for render elements), but not both.
+ *   The following elements may be part of each information array:
+ *   - variables: Used for _theme() call items only: an array of variables,
+ *     where the array keys are the names of the variables, and the array
+ *     values are the default values if they are not passed into _theme().
+ *     Template implementations receive each array key as a variable in the
+ *     template file (so they must be legal PHP/Twig variable names). Function
+ *     implementations are passed the variables in a single $variables function
+ *     argument.
+ *   - render element: Used for render element items only: the name of the
+ *     renderable element or element tree to pass to the theme function. This
+ *     name is used as the name of the variable that holds the renderable
+ *     element or tree in preprocess and process functions.
+ *   - file: The file the implementation resides in. This file will be included
+ *     prior to the theme being rendered, to make sure that the function or
+ *     preprocess function (as needed) is actually loaded; this makes it
+ *     possible to split theme functions out into separate files quite easily.
+ *   - path: Override the path of the file to be used. Ordinarily the module or
+ *     theme path will be used, but if the file will not be in the default
+ *     path, include it here. This path should be relative to the Drupal root
+ *     directory.
+ *   - template: If specified, this theme implementation is a template, and
+ *     this is the template file without an extension. Do not put .html.twig on
+ *     this file; that extension will be added automatically by the default
+ *     rendering engine (which is Twig). If 'path' above is specified, the
+ *     template should also be in this path.
+ *   - function: If specified, this will be the function name to invoke for
+ *     this implementation. If neither 'template' nor 'function' is specified,
+ *     a default function name will be assumed. For example, if a module
+ *     registers the 'node' theme hook, 'theme_node' will be assigned to its
+ *     function. If the chameleon theme registers the node hook, it will be
+ *     assigned 'chameleon_node' as its function.
+ *   - base hook: Used for _theme() suggestions only: the base theme hook name.
+ *     Instead of this suggestion's implementation being used directly, the base
+ *     hook will be invoked with this implementation as its first suggestion.
+ *     The base hook's files will be included and the base hook's preprocess
+ *     functions will be called in place of any suggestion's preprocess
+ *     functions. If an implementation of hook_theme_suggestions_HOOK() (where
+ *     HOOK is the base hook) changes the suggestion order, a different
+ *     suggestion may be used in place of this suggestion. If after
+ *     hook_theme_suggestions_HOOK() this suggestion remains the first
+ *     suggestion, then this suggestion's function or template will be used to
+ *     generate the output for _theme().
+ *   - pattern: A regular expression pattern to be used to allow this theme
+ *     implementation to have a dynamic name. The convention is to use __ to
+ *     differentiate the dynamic portion of the theme. For example, to allow
+ *     forums to be themed individually, the pattern might be: 'forum__'. Then,
+ *     when the forum is themed, call:
+ *     @code
+ *     _theme(array('forum__' . $tid, 'forum'), $forum)
+ *     @endcode
+ *   - preprocess functions: A list of functions used to preprocess this data.
+ *     Ordinarily this won't be used; it's automatically filled in. By default,
+ *     for a module this will be filled in as template_preprocess_HOOK. For
+ *     a theme this will be filled in as twig_preprocess and
+ *     twig_preprocess_HOOK as well as themename_preprocess and
+ *     themename_preprocess_HOOK.
+ *   - override preprocess functions: Set to TRUE when a theme does NOT want
+ *     the standard preprocess functions to run. This can be used to give a
+ *     theme FULL control over how variables are set. For example, if a theme
+ *     wants total control over how certain variables in the page.html.twig are
+ *     set, this can be set to true. Please keep in mind that when this is used
+ *     by a theme, that theme becomes responsible for making sure necessary
+ *     variables are set.
+ *   - type: (automatically derived) Where the theme hook is defined:
+ *     'module', 'theme_engine', or 'theme'.
+ *   - theme path: (automatically derived) The directory path of the theme or
+ *     module, so that it doesn't need to be looked up.
+ *
+ * @see hook_theme_registry_alter()
+ */
+function hook_theme($existing, $type, $theme, $path) {
+  return array(
+    'forum_display' => array(
+      'variables' => array('forums' => NULL, 'topics' => NULL, 'parents' => NULL, 'tid' => NULL, 'sortby' => NULL, 'forum_per_page' => NULL),
+    ),
+    'forum_list' => array(
+      'variables' => array('forums' => NULL, 'parents' => NULL, 'tid' => NULL),
+    ),
+    'forum_icon' => array(
+      'variables' => array('new_posts' => NULL, 'num_posts' => 0, 'comment_mode' => 0, 'sticky' => 0),
+    ),
+    'status_report' => array(
+      'render element' => 'requirements',
+      'file' => 'system.admin.inc',
+    ),
+  );
+}
+
+/**
+ * Alter the theme registry information returned from hook_theme().
+ *
+ * The theme registry stores information about all available theme hooks,
+ * including which callback functions those hooks will call when triggered,
+ * what template files are exposed by these hooks, and so on.
+ *
+ * Note that this hook is only executed as the theme cache is re-built.
+ * Changes here will not be visible until the next cache clear.
+ *
+ * The $theme_registry array is keyed by theme hook name, and contains the
+ * information returned from hook_theme(), as well as additional properties
+ * added by \Drupal\Core\Theme\Registry::processExtension().
+ *
+ * For example:
+ * @code
+ * $theme_registry['user'] = array(
+ *   'variables' => array(
+ *     'account' => NULL,
+ *   ),
+ *   'template' => 'core/modules/user/user',
+ *   'file' => 'core/modules/user/user.pages.inc',
+ *   'type' => 'module',
+ *   'theme path' => 'core/modules/user',
+ *   'preprocess functions' => array(
+ *     0 => 'template_preprocess',
+ *     1 => 'template_preprocess_user_profile',
+ *   ),
+ * );
+ * @endcode
+ *
+ * @param $theme_registry
+ *   The entire cache of theme registry information, post-processing.
+ *
+ * @see hook_theme()
+ * @see \Drupal\Core\Theme\Registry::processExtension()
+ */
+function hook_theme_registry_alter(&$theme_registry) {
+  // Kill the next/previous forum topic navigation links.
+  foreach ($theme_registry['forum_topic_navigation']['preprocess functions'] as $key => $value) {
+    if ($value == 'template_preprocess_forum_topic_navigation') {
+      unset($theme_registry['forum_topic_navigation']['preprocess functions'][$key]);
+    }
+  }
+}
+
+/**
+ * Alter the default, hook-independent variables for all templates.
+ *
+ * Allows modules to provide additional default template variables or manipulate
+ * existing. This hook is invoked from template_preprocess() after basic default
+ * template variables have been set up and before the next template preprocess
+ * function is invoked.
+ *
+ * Note that the default template variables are statically cached within a
+ * request. When adding a template variable that depends on other context, it is
+ * your responsibility to appropriately reset the static cache in
+ * template_preprocess() when needed:
+ * @code
+ * drupal_static_reset('template_preprocess');
+ * @endcode
+ *
+ * See user_template_preprocess_default_variables_alter() for an example.
+ *
+ * @param array $variables
+ *   An associative array of default template variables, as set up by
+ *   _template_preprocess_default_variables(). Passed by reference.
+ *
+ * @see template_preprocess()
+ * @see _template_preprocess_default_variables()
+ */
+function hook_template_preprocess_default_variables_alter(&$variables) {
+  $variables['is_admin'] = user_access('access administration pages');
+}
+
+/**
+ * Log an event message.
+ *
+ * This hook allows modules to route log events to custom destinations, such as
+ * SMS, Email, pager, syslog, ...etc.
+ *
+ * @param array $log_entry
+ *   An associative array containing the following keys:
+ *   - type: The type of message for this entry.
+ *   - user: The user object for the user who was logged in when the event
+ *     happened.
+ *   - uid: The user ID for the user who was logged in when the event happened.
+ *   - request_uri: The request URI for the page the event happened in.
+ *   - referer: The page that referred the user to the page where the event
+ *     occurred.
+ *   - ip: The IP address where the request for the page came from.
+ *   - timestamp: The UNIX timestamp of the date/time the event occurred.
+ *   - severity: The severity of the message; one of the following values as
+ *     defined in @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink
+ *     - WATCHDOG_EMERGENCY: Emergency, system is unusable.
+ *     - WATCHDOG_ALERT: Alert, action must be taken immediately.
+ *     - WATCHDOG_CRITICAL: Critical conditions.
+ *     - WATCHDOG_ERROR: Error conditions.
+ *     - WATCHDOG_WARNING: Warning conditions.
+ *     - WATCHDOG_NOTICE: Normal but significant conditions.
+ *     - WATCHDOG_INFO: Informational messages.
+ *     - WATCHDOG_DEBUG: Debug-level messages.
+ *   - link: An optional link provided by the module that called the watchdog()
+ *     function.
+ *   - message: The text of the message to be logged. Variables in the message
+ *     are indicated by using placeholder strings alongside the variables
+ *     argument to declare the value of the placeholders. See t() for
+ *     documentation on how the message and variable parameters interact.
+ *   - variables: An array of variables to be inserted into the message on
+ *     display. Will be NULL or missing if a message is already translated or if
+ *     the message is not possible to translate.
+ */
+function hook_watchdog(array $log_entry) {
+  global $base_url;
+  $language_interface = \Drupal::languageManager()->getCurrentLanguage();
+
+  $severity_list = array(
+    WATCHDOG_EMERGENCY     => t('Emergency'),
+    WATCHDOG_ALERT     => t('Alert'),
+    WATCHDOG_CRITICAL     => t('Critical'),
+    WATCHDOG_ERROR       => t('Error'),
+    WATCHDOG_WARNING   => t('Warning'),
+    WATCHDOG_NOTICE    => t('Notice'),
+    WATCHDOG_INFO      => t('Info'),
+    WATCHDOG_DEBUG     => t('Debug'),
+  );
+
+  $to = 'someone@example.com';
+  $params = array();
+  $params['subject'] = t('[@site_name] @severity_desc: Alert from your web site', array(
+    '@site_name' => \Drupal::config('system.site')->get('name'),
+    '@severity_desc' => $severity_list[$log_entry['severity']],
+  ));
+
+  $params['message']  = "\nSite:         @base_url";
+  $params['message'] .= "\nSeverity:     (@severity) @severity_desc";
+  $params['message'] .= "\nTimestamp:    @timestamp";
+  $params['message'] .= "\nType:         @type";
+  $params['message'] .= "\nIP Address:   @ip";
+  $params['message'] .= "\nRequest URI:  @request_uri";
+  $params['message'] .= "\nReferrer URI: @referer_uri";
+  $params['message'] .= "\nUser:         (@uid) @name";
+  $params['message'] .= "\nLink:         @link";
+  $params['message'] .= "\nMessage:      \n\n@message";
+
+  $params['message'] = t($params['message'], array(
+    '@base_url'      => $base_url,
+    '@severity'      => $log_entry['severity'],
+    '@severity_desc' => $severity_list[$log_entry['severity']],
+    '@timestamp'     => format_date($log_entry['timestamp']),
+    '@type'          => $log_entry['type'],
+    '@ip'            => $log_entry['ip'],
+    '@request_uri'   => $log_entry['request_uri'],
+    '@referer_uri'   => $log_entry['referer'],
+    '@uid'           => $log_entry['uid'],
+    '@name'          => $log_entry['user']->name,
+    '@link'          => strip_tags($log_entry['link']),
+    '@message'       => strip_tags($log_entry['message']),
+  ));
+
+  drupal_mail('emaillog', 'entry', $to, $language_interface->id, $params);
+}
+
+/**
+ * Prepare a message based on parameters; called from drupal_mail().
+ *
+ * Note that hook_mail(), unlike hook_mail_alter(), is only called on the
+ * $module argument to drupal_mail(), not all modules.
+ *
+ * @param $key
+ *   An identifier of the mail.
+ * @param $message
+ *   An array to be filled in. Elements in this array include:
+ *   - id: An ID to identify the mail sent. Look at module source code
+ *     or drupal_mail() for possible id values.
+ *   - to: The address or addresses the message will be sent to. The
+ *     formatting of this string must comply with RFC 2822.
+ *   - subject: Subject of the e-mail to be sent. This must not contain any
+ *     newline characters, or the mail may not be sent properly. drupal_mail()
+ *     sets this to an empty string when the hook is invoked.
+ *   - body: An array of lines containing the message to be sent. Drupal will
+ *     format the correct line endings for you. drupal_mail() sets this to an
+ *     empty array when the hook is invoked.
+ *   - from: The address the message will be marked as being from, which is
+ *     set by drupal_mail() to either a custom address or the site-wide
+ *     default email address when the hook is invoked.
+ *   - headers: Associative array containing mail headers, such as From,
+ *     Sender, MIME-Version, Content-Type, etc. drupal_mail() pre-fills
+ *     several headers in this array.
+ * @param $params
+ *   An array of parameters supplied by the caller of drupal_mail().
+ */
+function hook_mail($key, &$message, $params) {
+  $account = $params['account'];
+  $context = $params['context'];
+  $variables = array(
+    '%site_name' => \Drupal::config('system.site')->get('name'),
+    '%username' => user_format_name($account),
+  );
+  if ($context['hook'] == 'taxonomy') {
+    $entity = $params['entity'];
+    $vocabulary = entity_load('taxonomy_vocabulary', $entity->id());
+    $variables += array(
+      '%term_name' => $entity->name,
+      '%term_description' => $entity->description,
+      '%term_id' => $entity->id(),
+      '%vocabulary_name' => $vocabulary->name,
+      '%vocabulary_description' => $vocabulary->description,
+      '%vocabulary_id' => $vocabulary->id(),
+    );
+  }
+
+  // Node-based variable translation is only available if we have a node.
+  if (isset($params['node'])) {
+    /** @var \Drupal\node\NodeInterface $node */
+    $node = $params['node'];
+    $variables += array(
+      '%uid' => $node->getOwnerId(),
+      '%node_url' => url('node/' . $node->id(), array('absolute' => TRUE)),
+      '%node_type' => node_get_type_label($node),
+      '%title' => $node->getTitle(),
+      '%teaser' => $node->teaser,
+      '%body' => $node->body,
+    );
+  }
+  $subject = strtr($context['subject'], $variables);
+  $body = strtr($context['message'], $variables);
+  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);
+  $message['body'][] = drupal_html_to_text($body);
+}
+
+/**
+ * Rebuild data based upon refreshed caches.
+ *
+ * This hook allows your module to rebuild its data based on the latest/current
+ * module data. It runs after hook_cache_flush() and after all module data has
+ * been updated.
+ *
+ * This hook is only invoked after the system has been completely cleared;
+ * i.e., all previously cached data is known to be gone and every API in the
+ * system is known to return current information, so your module can safely rely
+ * on all available data to rebuild its own.
+ *
+ * @see hook_cache_flush()
+ * @see drupal_flush_all_caches()
+ */
+function hook_rebuild() {
+  $themes = list_themes();
+  foreach ($themes as $theme) {
+    _block_rehash($theme->getName());
+  }
+}
+
+/**
+ * Perform necessary actions before a module is installed.
+ *
+ * @param string $module
+ *   The name of the module about to be installed.
+ */
+function hook_module_preinstall($module) {
+  mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions after modules are installed.
+ *
+ * This function differs from hook_install() in that it gives all other modules
+ * a chance to perform actions when a module is installed, whereas
+ * hook_install() is only called on the module actually being installed. See
+ * \Drupal\Core\Extension\ModuleHandler::install() for a detailed description of
+ * the order in which install hooks are invoked.
+ *
+ * @param $modules
+ *   An array of the modules that were installed.
+ *
+ * @see \Drupal\Core\Extension\ModuleHandler::install()
+ * @see hook_install()
+ */
+function hook_modules_installed($modules) {
+  if (in_array('lousy_module', $modules)) {
+    \Drupal::state()->set('mymodule.lousy_module_compatibility', TRUE);
+  }
+}
+
+/**
+ * Perform necessary actions before a module is uninstalled.
+ *
+ * @param string $module
+ *   The name of the module about to be uninstalled.
+ */
+function hook_module_preuninstall($module) {
+  mymodule_cache_clear();
+}
+
+/**
+ * Perform necessary actions after modules are uninstalled.
+ *
+ * This function differs from hook_uninstall() in that it gives all other
+ * modules a chance to perform actions when a module is uninstalled, whereas
+ * hook_uninstall() is only called on the module actually being uninstalled.
+ *
+ * It is recommended that you implement this hook if your module stores
+ * data that may have been set by other modules.
+ *
+ * @param $modules
+ *   An array of the modules that were uninstalled.
+ *
+ * @see hook_uninstall()
+ * @see hook_modules_disabled()
+ */
+function hook_modules_uninstalled($modules) {
+  if (in_array('lousy_module', $modules)) {
+    \Drupal::state()->delete('mymodule.lousy_module_compatibility');
+  }
+  mymodule_cache_rebuild();
+}
+
+/**
+ * Registers PHP stream wrapper implementations associated with a module.
+ *
+ * Provide a facility for managing and querying user-defined stream wrappers
+ * in PHP. PHP's internal stream_get_wrappers() doesn't return the class
+ * registered to handle a stream, which we need to be able to find the handler
+ * for class instantiation.
+ *
+ * If a module registers a scheme that is already registered with PHP, it will
+ * be unregistered and replaced with the specified class.
+ *
+ * @return
+ *   A nested array, keyed first by scheme name ("public" for "public://"),
+ *   then keyed by the following values:
+ *   - 'name' A short string to name the wrapper.
+ *   - 'class' A string specifying the PHP class that implements the
+ *     Drupal\Core\StreamWrapper\StreamWrapperInterface interface.
+ *   - 'description' A string with a short description of what the wrapper does.
+ *   - 'type' (Optional) A bitmask of flags indicating what type of streams this
+ *     wrapper will access - local or remote, readable and/or writeable, etc.
+ *     Many shortcut constants are defined in file.inc. Defaults to
+ *     STREAM_WRAPPERS_NORMAL which includes all of these bit flags:
+ *     - STREAM_WRAPPERS_READ
+ *     - STREAM_WRAPPERS_WRITE
+ *     - STREAM_WRAPPERS_VISIBLE
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers_alter()
+ * @see system_stream_wrappers()
+ */
+function hook_stream_wrappers() {
+  return array(
+    'public' => array(
+      'name' => t('Public files'),
+      'class' => 'Drupal\Core\StreamWrapper\PublicStream',
+      'description' => t('Public local files served by the webserver.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    ),
+    'private' => array(
+      'name' => t('Private files'),
+      'class' => 'Drupal\Core\StreamWrapper\PrivateStream',
+      'description' => t('Private local files served by Drupal.'),
+      'type' => STREAM_WRAPPERS_LOCAL_NORMAL,
+    ),
+    'temp' => array(
+      'name' => t('Temporary files'),
+      'class' => 'Drupal\Core\StreamWrapper\TemporaryStream',
+      'description' => t('Temporary local files for upload and previews.'),
+      'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
+    ),
+    'cdn' => array(
+      'name' => t('Content delivery network files'),
+      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
+      'class' => 'MyModuleCDNStream',
+      'description' => t('Files served by a content delivery network.'),
+      // 'type' can be omitted to use the default of STREAM_WRAPPERS_NORMAL
+    ),
+    'youtube' => array(
+      'name' => t('YouTube video'),
+      // @todo: Fix the name of this class when we decide on module PSR-0 usage.
+      'class' => 'MyModuleYouTubeStream',
+      'description' => t('Video streamed from YouTube.'),
+      // A module implementing YouTube integration may decide to support using
+      // the YouTube API for uploading video, but here, we assume that this
+      // particular module only supports playing YouTube video.
+      'type' => STREAM_WRAPPERS_READ_VISIBLE,
+    ),
+  );
+}
+
+/**
+ * Alters the list of PHP stream wrapper implementations.
+ *
+ * @see file_get_stream_wrappers()
+ * @see hook_stream_wrappers()
+ */
+function hook_stream_wrappers_alter(&$wrappers) {
+  // Change the name of private files to reflect the performance.
+  $wrappers['private']['name'] = t('Slow files');
+}
+
+/**
+ * Control access to private file downloads and specify HTTP headers.
+ *
+ * This hook allows modules enforce permissions on file downloads when the
+ * private file download method is selected. Modules can also provide headers
+ * to specify information like the file's name or MIME type.
  *
  * This hook asks your module to clear all of its static caches,
  * in order to ensure a clean environment for subsequently
diff --git a/core/modules/system/tests/modules/common_test/common_test.libraries.yml b/core/modules/system/tests/modules/common_test/common_test.libraries.yml
index e4fc824..06f87d7 100644
--- a/core/modules/system/tests/modules/common_test/common_test.libraries.yml
+++ b/core/modules/system/tests/modules/common_test/common_test.libraries.yml
@@ -1,10 +1,10 @@
-jquery.farbtastic:
+jquery.ui:
   version: 0.1
   js:
-    assets/vendor/farbtastic/farbtastic.js: {}
+    assets/vendor/jquery.ui/ui/jquery.ui.core.js: {}
   css:
     component:
-      assets/vendor/farbtastic/farbtastic.css: {}
+      assets/vendor/jquery.ui/themes/base/jquery.ui.core.css: {}
   dependencies:
     - core/jquery
 
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 2323cdd..0139347e 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -188,11 +188,11 @@ function common_test_library_info_build() {
  * Implements hook_library_info_alter().
  */
 function common_test_library_info_alter(&$libraries, $module) {
-  if ($module == 'core' && isset($libraries['jquery.farbtastic'])) {
-    // Change the version of Farbtastic to 0.0.
-    $libraries['jquery.farbtastic']['version'] = '0.0';
-    // Make Farbtastic depend on jQuery Form to test library dependencies.
-    $libraries['jquery.farbtastic']['dependencies'][] = 'core/jquery.form';
+  if ($module == 'core' && isset($libraries['jquery.ui'])) {
+    // Change the version of jQuery UI to 0.0.
+    $libraries['jquery.ui']['version'] = '0.0';
+    // Make jQuery UI depend on jQuery Form to test library dependencies.
+    $libraries['jquery.ui']['dependencies'][] = 'core/jquery.form';
   }
 
   // Alter the dynamically registered library definition.
diff --git a/core/themes/bartik/color/preview.js b/core/themes/bartik/color/preview.js
index b9fb4d1..97573ca 100644
--- a/core/themes/bartik/color/preview.js
+++ b/core/themes/bartik/color/preview.js
@@ -8,7 +8,8 @@
 
   Drupal.color = {
     logoChanged: false,
-    callback: function (context, settings, form, farb, height, width) {
+
+    callback: function(context, settings, form, height, width) {
       // Change the logo to be the real one.
       if (!this.logoChanged) {
         $('#preview #preview-logo img').attr('src', drupalSettings.color.logo);
