From 06d2ac74be4b2530fc640a46451ac85452e7c5bc Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 28 May 2018 17:22:17 +0200 Subject: [PATCH 001/205] added custom item page fields for Projects, OrgUnits and People --- resources/i18n/en.json | 32 ++++++++++++++ resources/images/orgunit-placeholder.jpg | Bin 0 -> 71990 bytes resources/images/person-placeholder.png | Bin 0 -> 141375 bytes resources/images/project-placeholder.png | Bin 0 -> 320715 bytes src/app/+item-page/item-page.module.ts | 24 +++++++++-- .../item-page-abstract-field.component.ts | 6 +-- .../item-page-author-field.component.ts | 6 +-- .../date/item-page-date-field.component.ts | 6 +-- .../generic-item-page-field.component.ts | 20 +++++++++ ...nt.html => item-page-field.component.html} | 2 +- ...ponent.ts => item-page-field.component.ts} | 4 +- .../title/item-page-title-field.component.ts | 4 +- .../uri/item-page-uri-field.component.html | 2 +- .../uri/item-page-uri-field.component.ts | 4 +- .../simple/item-page.component.html | 22 +--------- .../+item-page/simple/item-page.component.ts | 3 ++ .../item/item-page-fields.component.html | 21 +++++++++ .../item/item-page-fields.component.scss | 1 + .../item/item-page-fields.component.ts | 23 ++++++++++ .../orgunit-page-fields.component.html | 32 ++++++++++++++ .../orgunit-page-fields.component.scss | 1 + .../orgunit/orgunit-page-fields.component.ts | 18 ++++++++ .../person/person-page-fields.component.html | 40 ++++++++++++++++++ .../person/person-page-fields.component.scss | 1 + .../person/person-page-fields.component.ts | 18 ++++++++ .../project-page-fields.component.html | 32 ++++++++++++++ .../project-page-fields.component.scss | 1 + .../project/project-page-fields.component.ts | 18 ++++++++ .../relationship-type-switcher.component.html | 1 + .../relationship-type-switcher.component.scss | 1 + .../relationship-type-switcher.component.ts | 36 ++++++++++++++++ src/app/+search-page/search-options.model.ts | 12 ++---- .../search-results.component.ts | 5 ++- .../search-service/search.service.spec.ts | 24 +++++------ .../search-service/search.service.ts | 8 ++-- .../search-settings.component.ts | 5 ++- .../entities/relationship-type-decorator.ts | 22 ++++++++++ .../object-collection.component.spec.ts | 6 +-- .../object-collection.component.ts | 8 ++-- .../shared/dso-element-decorator.spec.ts | 6 +-- .../shared/dso-element-decorator.ts | 6 +-- .../collection-grid-element.component.ts | 4 +- .../community-grid-element.component.ts | 4 +- .../item-grid-element.component.ts | 4 +- ...on-search-result-grid-element.component.ts | 4 +- ...ty-search-result-grid-element.component.ts | 4 +- ...em-search-result-grid-element.component.ts | 4 +- .../wrapper-grid-element.component.ts | 4 +- .../collection-list-element.component.ts | 4 +- .../community-list-element.component.ts | 4 +- .../item-list-element.component.ts | 4 +- ...on-search-result-list-element.component.ts | 4 +- ...ty-search-result-list-element.component.ts | 4 +- ...em-search-result-list-element.component.ts | 4 +- .../wrapper-list-element.component.ts | 4 +- .../testing/hal-endpoint-service-stub.ts | 2 +- src/app/shared/testing/search-service-stub.ts | 14 +++--- .../view-mode-switch.component.spec.ts | 10 ++--- .../view-mode-switch.component.ts | 10 ++--- src/app/shared/view-mode.ts | 11 +++++ src/app/thumbnail/thumbnail.component.html | 2 +- src/app/thumbnail/thumbnail.component.scss | 3 ++ src/app/thumbnail/thumbnail.component.spec.ts | 2 +- src/app/thumbnail/thumbnail.component.ts | 4 +- 64 files changed, 463 insertions(+), 132 deletions(-) create mode 100644 resources/images/orgunit-placeholder.jpg create mode 100644 resources/images/person-placeholder.png create mode 100644 resources/images/project-placeholder.png create mode 100644 src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts rename src/app/+item-page/simple/field-components/specific-field/{item-page-specific-field.component.html => item-page-field.component.html} (77%) rename src/app/+item-page/simple/field-components/specific-field/{item-page-specific-field.component.ts => item-page-field.component.ts} (86%) create mode 100644 src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html create mode 100644 src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss create mode 100644 src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts create mode 100644 src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html create mode 100644 src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss create mode 100644 src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts create mode 100644 src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html create mode 100644 src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss create mode 100644 src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts create mode 100644 src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html create mode 100644 src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss create mode 100644 src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts create mode 100644 src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html create mode 100644 src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss create mode 100644 src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts create mode 100644 src/app/shared/entities/relationship-type-decorator.ts create mode 100644 src/app/shared/view-mode.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 53ae9015f6..06671e0809 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -45,6 +45,38 @@ } } }, + "person": { + "page": { + "jobtitle": "Job Title", + "lastname": "Last Name", + "firstname": "First Name", + "email": "Email Address", + "orcid": "ORCID", + "birthdate": "Birth Date", + "staffid": "Staff ID", + "link": { + "full": "Show all metadata" + } + } + }, + "project": { + "page": { + "status": "Status", + "id": "ID", + "expectedcompletion": "Expected Completion", + "description": "Description", + "keywords": "Keywords" + } + }, + "orgunit": { + "page": { + "dateestablished": "Date established", + "city": "City", + "country": "Country", + "id": "ID", + "description": "Description" + } + }, "nav": { "home": "Home" }, diff --git a/resources/images/orgunit-placeholder.jpg b/resources/images/orgunit-placeholder.jpg new file mode 100644 index 0000000000000000000000000000000000000000..11564bc6357bf93aa88ab107d923c742051f42f6 GIT binary patch literal 71990 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 literal 0 HcmV?d00001 diff --git a/resources/images/person-placeholder.png b/resources/images/person-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..eb6232e00f835b8efb3e72b65279e8580a513d51 GIT binary patch literal 141375 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

C-s>Px#k}Y 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≷T1&aud6Y+*F5d zRuxF^%iyM@qF@en-Cn4f+;}1}FlWr`wg1%Yb@FW9q0Vx$GEhpq26dr2*sP}lAKt61 z#F~PGcR+mayx~&T3e(2?!)OVVCc%(_Ih_X=w+D-X!_!koU~H}KJm&h>cq7etJAt@7 zNxi!^YWhSq7n*A1wwm3y`a|Coe4?$9tt>J0$edWK{QUjGQO-b7wua7&6B%8)UO22! zsb_t>FY76GK$6yDx$40zwo)y-#_58=M7_Cc6iv@ZQ1y(B>7rN$8ar~WBeJ~56?RgI z^1I{2F}tG#>aQf2Uu!8nc_S7q0na{mQc=BjOwXr@zP<53@9azBq7?mPLFxJ3g5rEK zAfyo);UDPHh7i9SGr9)sib;4^O3@YGcftqm{%2Qr*Wo2i=gaw&ZVPwds4$>w524UD zT&l^bZ{=uu@H@4FB=ww`aD^t|%FaYxsB++G4=?$&^_r465M}dQ@V_g24u7`1AqGrh z2iIfu?r^?+#p=t!Xm<&t=YBUQ(aqN(KvbjWb>YT*PUlNz38ARDa&Hq4P!mjz&BI=( zYx^zjLE=j3Kp(x_GQV#Ln_o#m%myvUqsbHn(j;tCTL@p*Qw3jEcFewD;lhOQFaLC^ zvTw>6o~=ZNd}Y~@l|$!vcSd(#19AL2HBdk7+~Y`gVHkM<&@otc0OtB`ZQt*%xS`&; zPVkIV3TL<84sXX?+sn84JX;>K@m3u;j<5cC9OJv)#8d1*G#@XSZ>%Kh$Ebf(&(?pd z^_*p2b*G%tGnjE=!|3#mc_9)C(BbtwLNJ}+qT zOgF+IrUMG*srH6Gx0U{$ru(!SehVko!lg;ZnAP>smT)+w^C$XtDvOJXg#OLsQ@MpO{okUT|T2tx@-d=6hua6|cv=A)D?N z@bc9lS6}gwQysjB8Uof`8?K1wbD#cw;<)Hg6$kX?p6z2<0kKvNp;s62^8o<2wmBnm zn?qHD&LrSs8PEayL!bW#vbB#OMO`Zg8VL{#olz&$o3Y4N{a0hYA{nTGd+vs+35GA2uQdT-w!3NDg zX?=sSkwCf3yY7h7GfMHE8mWd?5a#fkLTD>>SEAvAZ3Ak6(JaUQ@mNh`lEHU-=kaJ3 znuLNqU4Fa&EJikQS&I9)2n_0b1xiNFs{iWnS73OnoEgggM%*=`^s03wwf82k*u z(uYqt$NP%trcdII3JBhH{JIdFtDg(7P)YBAU{S{Hy9vo&0(_toI>5_&d?N;t7ZBZ} zXYuRO|E@xyUqR=0ys?Skc(gEzyx@VprGk24jyYe80bFH7V#*BmW(i&)81j`%Vabd) zQp&#mogSU)PWJ$5G0TUbt=PZ(1I3^MT?4l7Y#f$)uf``R){X+xD#a&*anp#(G$eEo zUP26$WO{`)nb^|g8>&cvFYm?0-lLbKSWx$5PG7ikqFmKug=3Lx`8`8(A7+ckow=C| z$o*=5a7iV>dhc$)c}rBgr0PXPuMSk3I58f_!`TGB>D;5HD%1(tt?eE*Tk5CO`m`Y& zbxM+GAKr1HV@VkHqNvOJIkkRGqNZqM+fhmnqacH5Lc|{EfT4EL`xQxtJtK#tBnKXO zGdsW3^y`sT(zD&&mJSIIzH*92;l5giu3Rp?AVC?&SuM5sezKz4TusmcN(Tl)_zDhq zB50(60hFp5a{4}F(`$$LTK_iA4`b;+HKzpZkW>&+}lv| z0=x%mjPIPkf3+LVb+Syij$`NrE;yq1_-5MhDv(K{%P_{{@C-t5P1*|t2@6%$zhka} zHVAV2g&}%YW*TMn>(-?22Hi56;`JLo>|Qt2XreN&bGn{&9Ds(sf|>u7{dw8x{6N7A z&}gv!IFDb({EN=x#~?RQ5r~$FV*dA=r$zo4FP&j9Dc^Z|S(58*$|zGi)M74+zIv zQ{8mIyLR~TF8ELG#mXF{c>?;Mo|ASldLKIjH>G-f z{CYcRNw;zWrzI2YW3n229B2NKcv-tAOrBw6328p?9<|HaJPHen*=eZ{Is==5)dRgn zBcMaLOcv>*$vbxE|8HvWjb~ zlTfg>)l^T|?u~i5=z|h&eUZtvr&*lx^u0c>)VvFE1W1D{Rn<}@Z}lC%jP2ZbQ;*~n z>bE1^pjC~&W5GRTdM9MFS=DVFo2=~OrIjc4SzbKPbxjl`Zs>o+-I7*1))tVL zrKT?(2)NFRu_XyMpIpebB**{QD$<>XNc^hW#g`A`3sDu+yK!4)nt@u8P#WSUF zwaVcmvyRm&)+~R{^CXFiD-2KRnz*kIQxk$}tTamtoo-4~>WBM`0;ac#jfEe6bdIW* zSnB^My1|P&|0{O0oKtoo%PX+FAiPsRhwa6Wz#rnUW_m#evI?7z@#gUrdn!7w8ybe1 za@19_e|qwt)4oj+x54Rwj#v}K_nN~vzWs$nvQIoVIVZ5ZCwKtzOnvjiWlNLd{dlMX zwxpWA%^7S0z;!w)Zm95z$a50^z~t>FR~L${&kM5xuA(zy4Sd*tan?U8^K{&Kc1O9ss3qu8WTQs5Xs)tL{OVGBk@ohlgdi*Ei#Ld z%o*{wE)#uzTTM)R4^ae1g;#Y$mI6r=2Ybz;)oReCh=2@NIcU*V_u=S4T(C?84o-?4 zHkjSonueyYXe$VXJ;1ndUL7q|xXbiL*Tz7Zrf+(}`)ZA&;w;j_h)Q)4x7M z%pnu}@LZG{%AL6uLoeJ0z(73=&gg*MXXW|o_bT11_Yu;7dOyO40LwOXsBu=fog`Ru z_U3TfaD>0f;3t_C`6%bXqL_%Shhw(8{n&8NPT8=x=8;k3iTQRRTM*{rd~bY7wiEwU z-J)7(Lk?X9zd~N}h>`P#W~sbtl+?6ZmtdlQS!G=vs&$alp#4$OM^v4QRkKH(YC`lj zeKOT5=KT-zs~*kDC4&L3`H30A<_}~_^A|D&1CS{=NCN)tIQmJFKu}K@Z(|#ch5VTX z14ot!EqqpYi_`&i+%^gUAIoAU+b+Wgj4YjxKVkI;yKAXIQ;FFd($wM~m79D!pL(=! zVlAsv3)&eo{tcR`o zd!XNaaLT?7OKoDWk+Cu$pF(LVAN%^~SK=JO5%lsdIJwCW?IP=k&Yw9cd%$kfm(t}1 zVIq2+VW#l9V#l-&rg#P*ssMw2h0_32hRTcLbcp3y0{fE~6sYK1+e$A+!}-vK*B2L$ zD2h1^x>mbUZO$?hY<``Tuj5WOC=c%?M>6RLuI_ai@Xqz=)?%By;fQ3xa*)jFjuuv{ zrP>@f`!I<=Sd<|M!s)A-6gp#MYmj=p!ceSg>oghkK3aM<@ua}gJfA)NNVn5#XZc|v z4r{>E-U-#PQ!90w;mV4ZH!YS(w2wC6os&*kFQF?5J4wbXP;!f+qZ7$RKiVR&4MV!E z;nHMYlt}}!zQf9CYZt1fKtSXJ5X@%a9xJcY)YGFdG`xwjFCYiJYIgSbzBNUvcl(#L z4cpf4N*sH13W${qd#PGb?qel4hv< zMUNPjz`hI-xaH*qeUQPO8M<&rf{X{*Weo#KHZ-4zg=8|}Rj)8neIcCGN(fM6DOqV$ z0>vyQ$}bWXUL#Df{^9iGL$Cjc(*5Jp)2ZAM)Z?yNN>i}aO$T@|T;7NEAyzi0Ii3_( zoF-Mmsb`kbeADcaLP{MJ5+lSY-mRUjtGf$}Ksn~l#*6_u1a!-XYx6_m2=5d_0rxf^ zE}kghKoOV?sLATB)_6AYnf4{lQ-uyTdK25%#e9s1~KEk~!i%l&&477Nj6mbugv0h>Rb!=|^ zdfG#<3S6f_ATq*Z_)o{C1g0N0L)lR!{mc_bF@MKJ#IvvK_IsHk{^-k%%`$+k7*zZQ zNnv7V2@ua9K|ni;$nU`YBc3(fi)YD0@@pbRVI+|e&gKMt%d%4{!8I?>eflmGzNOi3 zCYUTD1sP!`Y6<>0LvI`>3PWmP9f>U`L9cM9d)*RkN7m~V(APqPwUa|y&BVBgV1!^B zrsSK&oZrt+CMVDas4c+WVZkVM(x9*=bW3u_IG2BdJe{K`EfD{z)-$ABo=Bx{_l6A6cSsl`#`H80ufC@H-)3)3 zye@7oh&Je76$B1^f_X;9LiT-JRQbf?^8XLEwz%N!7F9$}g^$VfOYLIL4)6(S$ca?Z zyJp-8&7q>+H?Mn^3w#mbi8c>5aauGfcw%^z#Ed!~!&{p=8M?Z{_`^wC`x`qwbwthEFjnOb%3w%%o7(Y!n?W&42X zT>1*|x~Kt>Voa@k6f3UxUYFCrj`(JD=pq0x$d~tK&OMcG&G{`9@T7k|J!zktcq=e> zC#iUA(BBF8p4yk}UzRosTME?RqB0Lm72L1+N|-U(zs8ZSRcl-uE+voBc)lai!6Q-> zq=rV#O&*EH?-0~r&sO^+9>9X}*#S2zENhS5&~e3Skn0PUG#69(9w^B)W=cy|dNe7nAR_V{c4wO8YtBn7z&+(j5R zCH^~OQiv0i`Forb-&@thEM{+s45#6TqP(O?y=EGl2HezE9Y&)RxXNY^l%x$rj38{S z?GAOgUaLTuO6kB5k-=zN9~-&1y3u$e+%fm`%C&jkQ{dP+pw3xY{FpO&dmK`LuKQd5 z_NTL2RBixWYZ>|;qPP|#ov_Ao`DL?5a}d{bwFc*7=g?@1$r+@^JR`s06}1MX-R~}r zzZ3;v(XYQf8L&|{gFWjY+|SNoCK05>{T$qgsKw_Tz0nieeM<3|XBFm<%{{PhOr&8OA%K&~x5tDcX@} zXzoMc{-`dGm+gi(Ap=^IM@wTQpN^4GZ6rVMsR<#KxrrS9#{Dcr-kSis&vR`su3M~? z3{LuA*l2=Nl-BgGDU$>CUxb+rheP}BHY?!pC;VTUYeNpNDee!av%+oZo%yGjc!J`7 z17JMC6fy-8VM;G1u)R=Jnr8}%{7zr!_&pgtA^of41=276U*_w%nJ7Qwzj_nqG|+=y z;Zi)%yaHD9vK9J(2#69exBUFyu%eX39;CCMr8!FCy_4cRM-rPK0(Ou-qxdA65>S6} zoW;rtfBScILj~zfn5KQk3KBGvR$NWez>KrmbfsckwrX>7|8C8uQYC%Oep@~0_hY~x zi6AX_Vx(7PB*d(zTFZojjgKeaj8mjV?+@F`Z@^%}pbD0ATl`0XnB&qqHVMZh zOi<{5Ghxt3NtZEU_RY|8mBMCau773UcHB+h#xEKpN>{=_jyHtbOq#)Lmy6`P+&6E< zzLwPjg=F&@ZL>Zt2cc8LSYHgEvML7fAo6$CFB3H#!x9%F9YN1JTH~l0Mo{@Hrkl!x(t4{XITF%gt~bI|W&dJn;IdXJ5EiB#``1AL7Ey)rnLQ`tRgm~)`xG;B@>;`m zp=c?5yj|^7Vk=$*3*_M!qi{f%tph=gc{|egmH%31?ok|0CyLv*1nnKk>+7i`qptl9 zP8C6n-vTsRD$)Q)h#hhi^(Zk}<41tC-2X7jBiU-ewC|+qdf4@)a(~s<83kwCx5xg4 z#At<|ZP#eEInNnY=t`M8`?7aXBUy%&f2uxB`U=9CZeX)MltpM72HCtD}?md+Y6oaexaFC zU|$+3rs5ha`4xPLVnHqtBFa&fo5$=;y&@007R^Ap=da`5;O-WaJ>X>E!iZR3G#afY zYjo+lpELf(?XVZ`tY16&?lhJg9)h>3QV=;2v zSbW}CER_`wTD*vQ2)f)WT`i|fx0l4+7$I=}^QjGo^7W1RU1P&j^xV2BcW^uIFnbB3 zE}wnXxz>vMM;5^LYXrJ?9XzZN2#G6Tn}6zm^#Fcl*v_**8E{pK3@41bI?xduKR|NN zxNb6OSnfBnyMibR$%v0qoG*uaw6#`DonSSEcs0R2DTKLak^I|Q=UR{icYx8f3pe?1 z`uiyZ@BNga6@9Xz1}&`v3dvUbTOQ{i!bXvk3qTnmw}%Xi`)Br>vMy2pzN6cOmTT` z5d#(lGF(WP9w#o*QI{PnElBa9G|rQ8w@3$PV+8}jk86_8C_T6P{8ssfBwBaZ>=V(O z!QTC)LS0l^io0B8t|m0Yyt?e1{LcsOO|j#nhvFg(1?)!MtUmdD8RofLaQv$-9=raM z)4|XHElRI(qw~bt`;J*`haQ97TyYR8Xg97?PfDU(0X+ylYuVaO&?UHb``DT1Yij<_ zOt9xkPS8Rmk3dvyRtc;FbC2)=#%z%J6ye#Zr9Ll(*3PO4rJ@ulD7sV*bV?-aLw_bB5!$CLY(<^DV@$QN zp0h`g)3RnjxMil0Si$XD-2Gewc;}n=0b|_wgRPPo_0L?={9r|PBM`r8-6OaUW>y>2S` z?xOe3QIONM@u~V!cw=k<>bq@SRNb&1L0NJ4?oMri7he_GcB6A%^7RacPt*zWVw471 zn~Zse`+gwGV8@Emn^jr1xTJ*N;ZNHhMqw!vxQ}-aRMU{ru2b}-Cu{H`Qw~-qul~>A zZ{%-=jOpTR{;CX=o-4YMip9O-1!cTBM1v&rR=FD9~ZILiQJ&N5th{NKO;X3Xq1Y zBWbxUe_0DnNhJ)d>;91Z7;!gU*0$R6c$|OQb|Wjc@rznssU@da3;Qgw^sSTISTkZ8 zEiu#97#qA8I0Z?3{lm&PW=^h{ecZW*_pQy$U(b}IuK-WJh?Pp9vCsgyF2NL&;0o#2 z_(jZ^G7@@V1D9xnQJ*{W6JA{uVWCm@C*NM%ib6?tB|M=w>`fMD{PV)iewfU(<_<;R zxVP$iI*Um0_*GJyCUEKfd*fIv-=72`vZ3jFM#QT-C7HP;rK@)5njZ@BbaJuJf+nG~ z^kho6zBht7jIL8lZ%R@EIXv#vhPfj1C))x3QR2s&oeQWwr-coV;I~O(R9<25Yg6<+ zKXf2b;F7UF5{{GJupsDqJnf33yppA)^mxu4u<*s7(vv10{mf*D^$p~U;`uKy9{mnx zptvkwS^C0rd)0=qV~f6ezg=Iv!|T|P!^wap+ztVa#Ffa6~ZaOL{N?w@TEWGW0MGBq-#6E<~X+P|~K814ApS3*{1`Qy`vv-K{sl0tdxX&FR ziPQoMRt&sN?syD+6i7H zZ5eW$N3<*>lI#KsWucT3E(8VrHwsHV_$o+jojh2lF?k&S=ZOmvCGX!fueA~f>=8WR ze-XVLdDU@weV{kJ&f}E+)#mx_CGthXeBX$m->VHnKi#O8jT84Vu%Y%mhXyhbE(I^(4pNeT(WLTdJ=H@Y7GhSagdmS;73D^yqi7f zZyj^wbdg$UoOc)+KnxYZZ)wGtXqwUw_vY%)t(tNNdMS_;4<3_%(ulbMAt7!SO!)3o zNWDk*892S5n6z{qg&0pBGh?*{aF{`rutm&42ZnDCy7iqY0Hox1_CJvlLdK??tn~iq zz6dtklqWNnNyJ z3lLT~4#k{( zu5keqxul&RyACs4(k_nd5$22uMq_M=QSvE~S7Pl6K|khrSKC#_9+ru6Kgo(33e8UF zjTwqFH~f==$COL$KqCEGz#|zT^0&Mjp=YUECANUzCLC9db;&PZof2-aY|KW)@!mnp zwy2-CJ0PKu5*@E1j)5en;YS2*E?Wszs-JI+)z7tnZjPu_QsK96o<^brnFY(NdQ=F_ zI~ICVb)HerCp_BsZ8~`!?xp#pAg1ZYV%nthQ49-*+b1?t~{iGB4 zw+2V5Ue3K>%ykE7wwfXe=8~MbU!6}_stH~6a!=J(XXnP}{w8X#dwRAZ9Q-Y#NIu%t zy$-oU0^)x@9kL-Kv^H0^f1$g)hv&~O-2ZvoTX82U$}hk4+f&M~>M!8EVvD#3b@PW+ z1|o;kCE?|z{a?O{<`T;npiiDjlxIumO9M@iTw)QSfcQ7SJ{8uAkFJ)fK2(JW^tkEN~o!b(!VDjiVVDb z5@GzLx7Sox;asqyh0k23U0Q3Q?vA<{QrFC_DNEg#590w$X?0#f4*8hVf}m^cLw1Dv zV<;!-4|)eO8t>oqA+_G0>E$Z;V@)5lhEpS9H)4GbXdhtndyR|3u9TN5Z_&3*%jd7c zOHOwxYXJm7xg6^rL7@FNTE!;MyvPZm-fRbhar|G)8_IW2nL9&K>Z=u= zsT6v3@wNT~IJ9xga7RU+LPp&~L?CC7^ZE*yMY2Dc;r$ z-T?z|w6G_g!QmmM^g%9WlJiqa*184QB5q;&fvsaC6pj|ne6ekLtP*J2H7`kLel9S zNgJ_#O6D>^UFms&et0TNU@5dxLzu0yF$@Q*ejotA6Qnu$gjZGDPbu!`e*%Fht^Gr03JZ#l|#g7siY$xZoWY<$-h!wTlvJaMA)~k zVJ65`Yi{h>CuLHR^Nf9P73(Zq+6{$ML z%FQp$552)gJwUqfymLIUtIxeXYfqwKTkCqc%~4TAZG8G zIPX&A?GwMwY7Msuao+%o2_O`CZb@DR!a?bwT<+2r{^LUDcI_n-&;~jqh%M}mrcb0R zgtbc1ziUnWVE_1&{wvDwS(z*=*^fL`WKUPRX zkL`&X!oAt2RA&RdM|y)8n>I8kXc{#3Kn>HZ-*XeVYG}xu@EjW==KVaUvEm9zP>@-u z(F#$V@Z0GZy-Mq$Dt)`%DA_3Ws0^naHRrTDE9&o9>4>`wA`}fK&hD4$xmD(QN~Zg& zFl(JtBKmTkE1@O4i{Lq!0jn8vQJ*@eYU|B{{+p=>-lUlC#lR|kTvUoKUpfSPP8Gto z3tjaI`0{#)vv73Xl#0WuE9{*T+kVxdCJG^pF1R>U36HV*Awdf-=ZDt>xZ&f@9xqRUb-{R- z@3mBg%`|fZeG3~OLkZsxOIk*5A#M4>jh+=*BfpDU6fwucWg#oew_xm^jz6i7J{}Sa zCW+SMbS>LT>UCY_m~`=9Vl+-Up$c*gWm+JrFD9=y3!F%#ht4u4l#(q)J+B3C9aKbo z;GM+LE&8AI3&3vl_9PmJjQW0k6aqZGOiQ&7_3y21(PEL=bVQ4dMj59JSxPv*W;619 z&}9=V#^ap{I-%({SY+(Nm1qlQ@5@VcF??D0a;xU^fP}OnL&cN~HgU{Y73JToUzBHd zG$!dPXnUAx%>Y66ESwTH6H|C5>aVE>!KJ@{=SlciJaD)fXJ>0}oJWb{M zzHh5QJ1fMKZF`X$Gw>W$rKkJ<;Ym#9W2>~s*^OI1Hi9avR(bAdvq@TFhPKrdQ)i)Q zo9TbUo$Op_g@Hr#ua*Dvy$?28>H|w(i@(%Vi+_}9&WB~cz`*Khrsbx^8||O>!{go} z?+NsWu;N}F4(4G`A`;P#?4NakWnPS+S_rlq!O@7I*6C`&R*@K;Xrp+~3C;7ML-il&MB(ZZiF30WcMd~9QQw!(rK&@OwZCjqMGbHsjuYtP?1jb z%Y@FFHKt{C#0%9_ihS?rz$htx8!0RgU>DCtpy3^lo=+Lu{D}DZ>OJ1vqlD&cX~2b6 z6K%>ZBB72Ep=ZQJdiSv3X75Hn@P(}xi&>V0;PUg_Wv{T{2b)CU*VhlPpE6k&S5M-! z`P?j-eqXpGLfatC-r4bbELee$_5+hP}&;oGPY1F)${k`@#A`x`ql|!Yr2yYNRJe?*ykKjsM zbB$xmnE)q1I$lJOy_5WNK$U#N;UnTiG3iJa`iO8<{brsuId-<3&T&O2xu+r&)jcK+ zbq+lGg9{cLFooAaO3E2mlmrgf+{+JfE?pXdM#<>YTD{n9>SOQqRHywlh_!@YAMTZD4Dx!7 zC3OUR6(vw;vA6PmX4bOAe7C=Awl=qs&yp>Vc;_k=O# zz0aUlO5lKq^oU%m5laFDV$bFGqh!t2!z0{7lTw8#Q;7C+RxMA}Xbm)u{#dXR&%@Ut zw|?@3sM0ka>#GibWq7$2Nosm$XdX0%iW5?fQWaJ4V#5Kh@0T_ECKGfx30brw!Do;k zjOehPFkQA|9!et3V@v1=VK)h|oYM2lhiZTi-XEda+TCNXIF^DCo7>?0cJAp2(agG2 zeUf8l@?F2@jSmUqL(NSW-Mke{z~lkdNXt@Px16|=D6+SuJYor&O~Qps==@x>gr7|p z2f0+(zv=h^e>9q<8b$l(=@i??s@MUF>V#9H0@+#lE8A^GqwFuHw3nJ$su|t&ijP>Y zNuQln?)p%g)D%3;})Fd+QIpr0a&K@li z*p)iE_#ZMT;l{9q$fAgDSNS>svVV6rHlpj7(eAO_&(H~O{d~3)!sW%vODCm|Ck!{o zR*m{b&|hdp4LD-y8}#63Jx+LCI7a+;=;1{f=T#Z!OA^Gt!^kugDp*nxuqz5M7PO(0 zUU9%g3^$aFn^;kTUK|F<*j-8FDoYr;;ZSKaWjO1r~*; z>|}Y|Pn1Zc*3b#2Y|Y+1qF)RW%#f z9{LC(=4OYDJ3|Lr`UWblC@|0&YJoJs z?~G()X5~q~iWbDX7K}knaviki6riz2bayH>aplJIZD^#B2vrjc(f~Nw>a3{0wskm% zVle5PyvFvfB0C>i5o4VvD>Q38eZn(}b+=bEWuX+lR!PsZjy7nm)tKw4RK9$ zDW4L~h2DF@`2*W<_Cp!5q3f4I^tdTSX)?<2sLqF-h>Os$Mx8^!+;sV~gr!uaE*vmcW-rG4rek<>1_g(21T=E@v%X_Y(K!I zkaa?gYC};wK{*MVyjFC!G!$&W^D>GsHTfuQE_gaPGib#=ib8Q01X14GsT6|2? zdw^k?rft8>;yF&H?95AGQX;8Lsv-LQgsxOt$^Nk+zAR47+gN<(si)cZPBV#083 z?=6NY7JiFTu2rnTgw18zzB5XdB4~fpE~?8it}r2xAmUu|=cLLF=3xFPEM=Fg4YSB5 z++us7>HI=!2Wc69lFT$;N@2VMGPhxvKKtbt*|!lsZTh3lVVz%uhHAX%vJan-o7zen zvKek9CpnDjpZ3%m(w8QZoKZn&6GNN-#JI_Hpzy(uC|o6fmr#g?=*meaGzMkEKU+|!1Tl+lIxnS6l#!ijS}8M= zR2IVqI6GZ8e+h=!iEikLYn0hH%C5&26M`%7p>0B6(Y~I7X~Jwe@eOAffN#stLp9^S zr#@yF9*$-4YDOp(;jj~ySdFR|DXlR_icln^sV;YGSO$~KpoYm>*^9pWoFr&4vaUt5 znadKm2A*fujy|_yb7TGxIEh68U^XQ35WEd5@9UDgmXA4P<>GENZME$Fh@rTHRz-VP z6A-8>P3Ji8C;4G$t6ru7G>!>vlLyGHg-_DsU_O|Om^n$~bK_y^ou*<;Ud8Wx1ybOH zd~u~1Syb2%7D`EmU#r}cnsjT2pq0Y8Vit{AK|LZMbmnLl1|&CHs7A_` zezMt4pgR2z(6K+MQQE-O3g$m{oVh%LLBxq3XKD`!I9};yX`fl$yf_&7;10D0wIHESrp0Si zajHyzG&eVR)I5-?f4lSZrIQsm_|TVcLWOoiQbvz0Xn;M^D!R@qM0n3)-M-*Up+|;W zxz2}Ec*Zmp#=uDd{X^m{DW9Um)t$grANp#C&8@;R6=4vCFUCs9Y@!=&f(y+VfY(3V z1>DZJc)lCW{+8W`e*;eTe=;t9jOEUpMt z|HibnvAsv{p1=V4G_1M`112x+^LulpAeET0&G+I|Dmx$b^%wh$uPDdFvwV7aqJK&E zj=KlwEE#YPc6m4^5pkIem7wwQq4M;6 z+VF)pQ$k;=yrI=o`wKAB0LR&B<464*F;koO`SHw<{*f~YkSYBe>)Q{hi)U%xE0I*= zpH#wGj$u5$Wm>TtaCKa)AGglZ`bFSwt$HGs?kKr92NKa%PtXQWzScnzH`tDMICCqyuhX#8`^=6Yz49l?WDF4w@Xq)G2yY z2TuL$iHw_z=U*3#7%XyZ08k9j+gh!N^%O_nJNy_;RU$?8(R1;0|ky@qERs8_CVUh4S`1=kLxn=AkXG1P1FN;qy~`*JVY1;d4bX5$DB1>P)Lv!tybU;_<1pXO17XG zUg`PQ{F)hINtF%x=?m1QPa_YTz2*jBEQC1u9c+GJxb=DPe>ho7C1&dA9mU}ln{?CN zL0`r~ECLSbg5<_KOh18@47o@vZ;J+dG`g_}FYc*}@?o8lDCEzDj!s@nxVrvbrdROM z>4yOmUdd~BC%5qgi?u2JKz8b4Nrf|SFJV8HIB{@*jZxNpa^; zp!qxTUk2c!s%xA+(!tO0*`Vazz~4{*P^}cH&@}?Ui^)1c$>r0&MmW-rIN^&M)%$?v zck4x7pJsIQ@T9lu)n{aMHT}O|Xg59kH5;V9fPQ}!v2p#gspI^6FNb#-@26Ls2B%cc zTGFP6+OXya`CBjcM9ZL<3YvT9#& zir$Q$v<|4c4_Za%)IwSIpI!dat(E=p6}29JoTt2%TYH!tw@BCUGrsw?=PT9jLE|Z4 z)4DJK7*B78`&ASG|D~8ZI4=heYaV1y6a+yw@S`&49@zx+#mJdniT#^J>?ta>hT>&#%|jJGw#sXkxU*S`0w2*uoDk z2Ie)kJ%QXhEv*5%WK2V1+tp9Q*^5a$Na-GX{@|iPk6iUj4YEs6;bl{qiSUt1jN9!}t?!Q^)p`I9sT(LSX{V#l7Th2+_quajAoP?%wUqK61 z4Isj5t*TKB^4peP#Ve&qp$mjViDP5*znb#G!t>O->vhzw$xt-47gG1QYz6J~%-TDG zwXLq%q*Z5YTHRB( zlzgdWVezPbf2=NI>1Z4NH^TkgKU1}1(3llinn9UwvC=dJLOGazpxb9r5}hUJWkK#I zUZ%l?YIXKx!YBVWMZ-O~^<5e2!s)PYUH+T|H1LQbB}t9g6CJNcE%Ku2f4+^wt#KNS6C{#$g3hKF>8=uEu#A7*`P` z6kN4czyN%pw&=CU@_J34giq8tRWY?QzycD=`Z(#6U?qjEqX*^kdY!~9vn=^3a6Gb$ zEjd#mNBs6L=k{R7Fytm37NPIdsDTtTKhlM+in_%_V{#%GKQyI(LGxh@Z_@K~(%A&5 za2V#8zQhyUQ4|`Ztlg%+wzwYnb#!dpE2VHk1U3{%$mZ{D)qd2(_1-lz`pLrav{otf z@owu{ojb|Uk1H=6g;#5^cFVF#wN4Igd@K1}3*<(g;O=OJC0|=jG-h^DG>cJIiLiAF zbr8)7$)Gwf{YvMZ!ilZ(!{ekrv}+PGhHl5gEvW{drVAEeM2X9o=caUJd%8)2TX4Yf z&gP@K>y_iABP86(phE9tpl;i|DHD&p#hU3Qq*$UK?<^cew=14uPK?yx(Avy>+DFT2qGe9#?8rKM&Fp>V|QO+9~C3 z4PIaW?CtehxOV*h)SVZG=UxqX+Q8JCioM>3R^!tYG(g<;RLtGl(%Y~n%bTZveR(Y3 zecLY_ZwOs>f-$z3rOr&wiV*e1cJ2;jwB|=aiP)nD&<&|A9f})gB zrYxyEF+Nju8eWQR$L$v6PrEJuX(XNW7gIwbS?-prrrNtVOJWK1V`kEeu{Y<3`uX4{ zmZH)AsR94CB+26+7CJ^iu^za^H#eO`ASWopk)gu8|8r#*lf_BMC&eEDX-^PdVxO|R zrCFQpFF>rUrcv0fmifq-5)r3Yf!bU;yl0Aej^*E#Gh}&xc9>-4OiCOziqB|~SQT~B ztR_uJN_`qPG(^kZHlR(y53_JK5QQI*2ByyXZj*;g6QLx~9Tg1+n>3_`bMH_*v2;$3 zw9h>G^$uUqM1^kjZN8FVP^`9zDhzSrVP*WKON=u)=E^yZ<`hrQug(VdN&_S;6%U9& zq1>jfF2CL;JA&Xeg-epBykWwHx{SbNCdG)V-&HXIlTWw3=U4PIZIgD@UNVb1+ikK| zXuTQa{a#(Ir#CaSAGBjD&HubjMs<}Gc#HV{QmwPTq7d^;a{$`;ya+?5mZLe`cOWn| z-g!U=a()gXm&%&pVy(kuQ2|tMG*XDJ85llBryon@Mac05pcgfw)Lyi&31YwVU;rZB z3Vb#-K3rdn*|4+L5zg%E>x$ZRCBcWpDVdciGg1s*tgb#c%JQVhK{0c7?@wH~b-A|D zN&ud#Y6s2}d0YJ8*zB&*!W4jz5FSwH@yQ=z@f)q|JWiOv*)?-=o3JkgprmPk)s2-RsPhC@+OujD9ndT!bwl9wvC6Xq@np=o>fbm&dyDxo~CG z+&nFM_)cUv1rc7_*P<9oj|Xdh@(|Z>D|=o)KvxiCpj?wQO4~R*z=!!L%vMg`4%xP@Y1( z(y9=W8I8_wabRcUYjB7vEjk#z+=@@s2m6e!HbMU~)AZTKW3Z8e4q*i9o1k=vC-+j~axn$1oo`k2VE(HQfsN@Aw5B68 z98$_ekS7)W=L2NL`;zMeWUjLsvi~h>Gb^?nDjNdWdDl1i610VPfn1Ze``9n%gilnH zLtuidIG41p0@CCypFQNF#8qDn?TOCm)FyY8(3U4Dxi1Mwp(tB9w`ne68OnPb)8|2O zv22xxBnz^P^NaG(PoCMS@EwqI#)z*OmRFqr#R)d-6qzmd>rW`NC%4A3Dj3YRZ_h(h zW#l#^K4c^=?op`rA4DKh9hhh-HSucU8`joBj&9UX^8_uN(h&JvEaQ6-9{b3NaHVTB z@7hT$Y>R4_?iy3&Olw3#c$XbOKmb#B+KXEg8OR-0AssEwSz@ulsrv3Q0Z|F&Xhe*{ zR2Q)x$uUw_8E>;72@x0t#VNH^cPF|^Lg!;zNh%B9-ce!q7BA3Mu)>p1!89Fm0Xz5B z);$M^X7jz`3PGu7P#2JL7+NSQo(4I=q%GH@jO}3>Gx{Ff@Hv?_I$IFMzK5<)Z9JZR_eqLPuuswW!U4CbYF#57N=lBo4lamakUyN(XeFxv z2?uFy0?(RavHg%_n3erSG2mqCJP^|ioQ8}=N&^5ZCP4@R!k|oo?g{GPWQ;DjEHL3S zEB%gV@2i^D%v(x9BZsuTkDdzN9J&)h5~3*~O13Damj0zg3GRO!654~E_{H2ETJCIn zR}yuF+htP-Z*N1XNi+Pts0Q9%Yi!87o{H}S8Xf;%4#}P2hqezRBWGi#`#Hucb09=~ z*_PMl#n+?LVX^Ss?T^iZt~KMO?F7{P562OI_%9NQSll2_UMPm0<<1dvPYM+fX(E-1 zskmR6_iu|hQO|Nm)mJjCjGx)sF(>|vT-nA|r1;`egwf4iLiR>y7tOd(0UdWt*p6mI z=i1PuGpOPjj6BXFnIH5H#TdamXws#@Ywww+6kn?!yVN;xh}MqThq0Fu0~9mRrsC~g zKTVeKR2(>0UK2RJvn7z|eN?82_Eo59$ZmH>^v4H=xR{JRI=XH!qWM$pM~sq>81wTP z+Bg;Q&0LBB9~nNcQl)Lp7rlNOy3;3o1u%9$)pn4eulW7xpqF>w;QOyWXJqk{0wm7| z2B3*)!WJhEdD{lL&)9raO#inp?axO`hH z{&n?Wt*K*Q)qNOsF5YfP=fr03`RjE0kLTc_-S?umbwFbbcV`Q75D9;XII4kGIl%lh8^-+60Z$;fT^0EJn@*Duuiv4?No&31oy%OIM? zusnozS=T)wLEjz2*SA(R?ND6@Dj%U^LXiZl5iWFQVhSb?DqGm4 zXTv5*L0~An+B6tsP7ZIi!qgQEbN)m_<-APXl`tNTI$~`Ytpb8^&Xv_8X3xq~Cx>Zq zM%D=&dM%$)+=jgW4*|4z2var|EvpDGOmcT82`oUJ5=>)*Ob`a@EGk>PKo1=%O|zEW z5kh`GhDY1Fa?Gb%Pb0x5i-y%n+Q3mJv5A*~CBD#hE1O9}P@&znY-757oe?J+w23fn zj7XCRs~D<-1yaTyB7=`wU+4ZMnsHtm;zJ)0Ikrt(Dd{M4OVPES9)U}{vIi!9?U zKq@~J<~8YGHXff2x|lx0-=R(-H4CQ65p1Iv(#T8F+}6fGJo+GN^1*a@ZdSG`^;Y3dE|jln6iTzJ%z+Ru@kF9QFyK@Rnj zRQm*PGh8wR-Ot2(vFK3>?7`tDZiT%&`+fcKpwFMrT6{8aA|gI};8p7>_Yj0?AUr2) zT^TUZBiai^`DZG@#!*;>m}^#1S#l}FL08q{5Cn-vRiHc?8zomz3cyx zD@{1UZ2$F}Ww5rFRX7FULJ>3Wb2?a$aX`|q!eVxgLA8Ij&FdH+lRZOe&?4^3gG;l9 zzIo6MO;8>=U+#L61#W8uJ@;H!Y$qfxQCbES@dVD40vx6ppnO1o=#bnpS>;ax!!sEzG% z*1Z!19_|=4so{^Iv8M!g4h7(kccp3!zF&IMUkrT?x&0B*O-D7W{u?Q&-4->HgsVxqT{^)#E|u zo6iLHg)jn~3_+l@ zc&HwLor@tiT53XMT?M<$|&d#R=K@X#cQGAbpCz4jP1Rld#U;`#@!l<~x2Kj%+4SFG6 zau9$S7eT|97FVymnqSf}ecVOYmZZqFyJX1{eF(ohD8lrGfvj{;Ui?Dv8wXC)s2a*L zGC+~4C4dOAnyacPTxh4%Nx}Q7vh;>81W64SVpDP3A;$&ML-jt08|tw^aIWBj&(O2U z`q9%TfH(RMm5Lg^JjIvQQqp z)|54^<=EHOQJBt>X3aX-ZMRtPRcI1EIxcEJUBoJ$u+B#^*^r8D=L`P}#)6>*99r4| zfQ=YOH|GD6GM9&wO_@Q^tGV|z&6%3xVF0eM4QysqhwrsjL`^C|w#D|;F%KfS{ja5Y zl2EDSptP)l-qC@K8&i~##>}j4d)@z`kgOzp6=CpX zMLK)c@-&+3o7`up(nLZa*rdv6RE1#3EOtI+eHzmQT{E%v@Ocii&X*$emx}SLD4$(bDnD-oi9nG8sL4fR#M?4mSV4I zRVCRQ&@qp_JVk5Te~S;xDmE_hmY$cZMISFhASM#xt&dKJDkK6es>fG8+Xo-7PCC|S zq=U)Vs^$9r)gIKjZ7!M?GjA+xr-+dmO8ingZsT0U(yxE?2T8yBaEOCaiFm&@2!Y<8 z9o6@EPMJrOxp_2t7<%L%Nq5A1-FJd=WU$xN4^{1kA-~w+A)tCG*%Z_&I%k zL~1^Hl+GD3t*Q4;-nA6df@YrvJc?Ls}=*N8DA|*GbHV*V%OWX+?q%+{mG&6$fLJjM}G&zDIzqi$|U$+*?}>&ETA0i8=6f>>Q-WkDi$Y4qxbsqFWv4Vg z(YhK&rK%@t{ZExq<_ANZMTz}D6c0y69g$>s5Z>?qn-!|Aa_azrE(u_Wa6%1k_u)1|G zhC~}nQ1uKfiIHZ258!(M%i@^HN9ARTRTj4tb49WpDm&jfcuIqMe>S34wl6DQ;d-ET z?YzIO>30xGp`kXwkGTXv{R!p@%Pf6LIZW7cBoVvXny1>D>C9X~fj1HvaH;3e8@=f1JO#-91zf!_16ne9Et+atlvaV2)=$8Eh^+R~;5 zG65HZPxsYkub*$usT$#^wPM@~ouSM`U-gwg`0eDVdFL><`$E2+u+1E~8V8?8YWBSv z9aC#*!+%)McQQHlviC{j1_a5`FWet5ZNK(dPX6La<7UT3E;`F4Kx12bNep!9s6IGw zmoAbr!f6i!9^b8;E5E`JB-v zBi=+5n$f@2>-GGoRRGJG!xRcbgfes*V*=kTv^KrU)V&IQk>Rn| zsqB&K9STu2LJQSQ{2TD#$;^;kgceTzhuaObB*F?7JhtEzkc)KK?i8pK{(r|(PT+47 zz%RT7lf)9(0lv~$TiQ>b%~qSuN7X)cV)#=o(MdJ@k*)=Hzox5E$-RcPNE!=eSvMQK%eVHg0w7&#!SoBvqI@Vpx3NXQn|lqi+iWA=XIw&0_Z>?ZkQ`Tv;ICkc|u z4%_4;WzU0DQlC1RphjXst}CJbUNhMbcPg9&%{s34M&#j-4Z;%22^s^fj?^p83wu~m zp7fn>%feDkodfz zOYf~-So6L@$~rHTG?(jwQ`A@l2binh$mp~JuU!Sw{hDWJ4xE>#8>QE>(4V*eY;C|p z6?=p0Kfe=_N@jm}@3ecTtG}zLgzKCpTtGkaAB{{B(dZ)HOdh1ynk0_)R8(uDlCX)3trHw(A=3aCH7%=hVsA1XXOk^< z4(U=#!D1lKbenJrD&fm-cz0BME9s}>jP8OUtdsoQ{=_~VBPFUIX%e=e!4{YyTFlOG z!O3(2U?fux1U-c9o>5vqw#8vYc!3jQ1EDKgeBn@{c#Ky1+J&zqt<+;&7jo=D{~_V> zAt*Z+B~r(7)P3k+UbCior4us92pG(zD9`Fx{i}2ggwn%5o&I*`dA)SQ{PcPEXfc!E zv&3QR#(2x=A_6PqtN@;{r;Q=+$oBY?v*xKU8SjtyDYyt*nyjU5lq+d|%Z6uS$FRQ< zq8HwT_~BnTu9Kq`GxJDr7fxgT^%s0%NFg|Bf-Yi9ivbwq0F z!)SK6(!ukcuc^~C3S&_Pm8uv=&O`Lb@Fkwr4hnCA{^Pb}jsTUL|mq z)hYV#qQZk{uPgB}_>~p&y~WV4WP9ue)-}A(xTYlbZVS{sc`cq<@r-@+c?P#lu;(Q4 zyQ!u3wiF%{#7Fi^Ci9N<_dA>51AZ0#i0Ai0NMLtlWqd}HfekNYQVrqjxiA-(Mmvx%6)|f&5?EHnFTRcE?8bn&k%tnHI#qIS@ z%%uX)gnBnO`&iP^nIAwZ)ix2wq}oZ(YCWYS@0?#*Pv@FHr#<}q6{|r3-$X^L>(|x9 zIGl>9a-n?&SIH-7lOy$GbDxTogsfiMbLR*IE`20RK_i*qSH&WWkH}_Yji`)JPGM^R zt}`8C=TF&84~TBL=rM`AVQ*=-cX-lV8nex8#XGp?IVHVvy1-FSN%cknAMd|-aqk}& z*XI>V!`emV_;tRqfgUA%4^m`MfoSFNhpMs^4-;GaU@jL?=_Pj7>!4<)#DM=IMx}6R z^hBt8;0Mv{TN&SgjfpjVw2d#)9m40XHV&7r1AM%pqVy&=C)*qQ$Pi&x#J1NZlW9^L zVeIA<_yxw(^Xcl|hED+4$ADBWv!!F{=k|A zs6rl1jr{C3d@$CU;4{#76Il3?qn+&c@!I?)j%bAI(dd%ruUAd3Vt&TP0q3VeW2Fd3 zhCjjLosh;J zwf&k#1R+2S?=76dpm6488zjH$mLxPMM6t{992KOUZlRXI|8p{XlnC@S?f( z(;H~KR5i*_R~v~jO&FGQF7g(v^k{h0raPEQR_0ZRr$i{rBJV5h{(5>9-dKn`bT7Z67%G};{^hVUB319_Wo>Rx;$KLL(LTs2w}k~hgeQfADTqJo;{ zle7RA@O19CByxLiV3KZs~gFPT7->sy_T_5aiqt2Q%uGu7igd5qBykOf^+S z?kw-^=nhrpdw)Q-*v+qpzK%AZOLiJPxL+2#|I8LUGm`|1&d#ZC~bdAsPAp@boNV9WUw_Q_USN{Alsq5()Fl# z_SCLmHs~EH6>EPY5{~K|b=ZA(GZt`j_v38qUDxXEYyS$_=X{;W`^saj5niEcYhp2l zrCWa7;O`vqB#bi`mb0q7WKw>n$^rzsD48%%_}fn3DOd|%wdH-{zjP?Fu)%DbChv;B@*tVU(KtMf3vXYK_Z8C;!2IKr+Zea}zl z%yOiCQC<7Q=H^CMpq#BDB@aIfJeI5XWan4@?0G*B>HZ$cT$>L5RB>`L96{X3em=Z( zxaoPx`>VOhXGQ*UIP_;j$uEtupXFbG^ko<@hLz6V17n{Cr$f}dveTRnDZk@oO6h^% zAqnSltdzUNE7qol!r7qL?#ou5qN_dw0_>Y*fAf71Pi9^$lt{dc%H92MRAOto`kIQM z53EnazT=xVxsv91e(SrPJq5i>`qD=9IX`GN$XdJ;BM3HwZqnBLAz;j`6*ZBlq~lk> z$?pT4{g~xb;GgOD5?@tB2!~dM0K{Tyh&}}iYD{aL9|RDg;Ejr!%t}mQB17H3<{_MjtHDcZNptLM_ zUD$3T@fP3vWy2jF1ff*FUG=^aZmy@rW@!7(#B=eykWo7p|PZ^H&XIn0r z?*FWb@gBkMPZ@akJ2x1PyNq9qyyV+5;&=IbPI&9(g>eV%xH6e=R|JI4kS1*2s1*3> z;l~EO#2tc{lkR=-8`^~w)DpT1|7!OLK@|SJtH1D`Grjmr-h= z&7c$QYIQq0L@X{2a=e0A(tQD8?&`JKFE4Lc!la^N>?z-by+{O?b87bSd*jSrHmAThKK41b0hIuZ2W*E;-sj=DmqI&Rlal1mze0Mf&*jK*@JyK{`6MO_yig&2RZANi(j{8eDWf2W#dOM0Ku(&b!S zoTaJ0ZNRA34OmIlwsD6FeVekCPPdt!Bj1iX-1>`ER0b17sxw`*SK$i5{LhHmyut4c zqf}52UjT<1pjR5-xUF|g^1WR@9m$Z`yVGxd z@AsyK_5g``6Q%Eoc0}1|K=;c%;Ou%dhuG});3%F+#_NOn*eV-A@zbRPgB8DxYSWL_ zyKWdg2=Pt5ER>;cXfs-3I`v*u?XLP*#VE+#hh^WoH6S4J?IUSeaCK6LzhB9XYs6~* zaH{ax(^OO`s>N7rak^i9w*TIw<4cI6#QptH0f+jpV)eRAnT&foJJi0A?{?k-6))fH zJ6`!`{@$cN|B59eg*9f=aG^S&6rKmymc$fdDI$>*}NJw0{>!d^|Q_+*JD}b5Y4n+lkgy(-K;pAZ&F}?RQPg~l=I|EU>-^83DqJq1s0Dp# zSn+#uz0Y~Lls=>kk5L}WQSnsdc`uP-QgUrmsGc1! z4+@ga`FWT5&w~x87h;%t-gxWFveL}Rsu5S`agCiJej&@S@?2q0WIkY~G?~C;s`3n`>Q|g1u z9Sz-^@K|1$!Kh zobgf1BU1{!y|u;b=y2(yam)1|Wz#-)?YAb)a)dMwjFaE!V<(ZC;XR%^Kc3ft`B_s| zba*RXm*I)>4lH8??EFpfU0cx$p>CM%&2-A3h(IZPas@eTNce&r09{Pt*v=jR!b-9i z6+*#c&a9JNZVN=OQ&9;$z3ig}ltnHRsXs>n=9$KyQgnq`1AD9{`+N-Q6r=G#%Kk#Cp!#ZvFlZ|gtyp}sw^rHv?+5s zxd}vc3f{j*)3d%*R3_b|3=z^*tPlayqgR#@)s8F1Rvok2@8Z(05=J~~X@Ke36v=_J zb~V(r+;O=o&+!?(?yL~Fj;!Iz)B5!J74R5C>IfX$U~#)SRIqMakIBJWPd#fW?XSKM z`<^W2jV<*R1-=r-{(>(!2-+oIT)7?l+E5H^f~BO{$CjXuGucZzc65Hg5wSJ8w#H$M zIZr@;f_c&}hTbayCOEd#G;BUtd`l!V&+Eue=c8C0RIT)?M5BT046ZsAE>u z-Rw+tf4t0k_$w^AQM53~QNG%=OC%2l{H*x5f;Y?r#Zd9x?sY8mfQkYZ0#K8Q)pMl! zvL?pN8!+giZzFgbI4mm2!hh-Y{ru^T^Ai~1xu(6`vHtD-r~Cq*LxK#oEmwJ)k>bgq2?=>nyZFp|QN5!d4m2G1g2u(SNiX)sIR5#O`BdJ8JNwJggCS#M zDuc9Y*KCVdHe4^K#8Fgv#vRT0k<=uEr;}N`-_2=qU$y_;!9WhNX}ezoFv|7jU6lq_={v_gv+9FYJoLfw8>F(35 z96o;(rKz`lcT$^rapCTTQ?ylc0}|xaeBr81+Yc-Epwj@Ac#3wfh^0$(6^}Q>P`5tDmTQs~7Xp z!31i@#>F)b0^ji-dyF@>Vq_%#kM6!YD$1{IcPIgc0YL<55G4g9Bqdd(Q$kt^=^=-1 zK|n&JQzV84LAqNSX^@zqd+2634=TU+{bHRz&Uel~hi5HVYZmjwzW2WO9oKa|QsMe< zD&`S>8Z=;VUr}cGPkLw-26cU>9c8e6soFH0+u|XqdWuLcnaB~PIQ#!X+#8Ae zJdMakZD}Mb$v-uwIOb$amJv5-=H%xT{Et-i4kGy8gflp~bIhN5E=BifYh$`DL_4y= zLL%@k?=Wy@SEQ*Bzv+;D{O^2DsZZVf_Sn46PtG^BlS~E1ygruKE3a9&4BOHmJ=yVk zaSRa39cecMfqvIVqyMhg)rpAkED@q)ET`eo3kOQLFzCR}&^t@+)0)(@LBn_QFSiIH z{G%jDf?gdwZYWx>brXzvk%=o_+rpL28^x2Et4G|Y1ClY&S#1ANH1>8d#{cC8aQIM03uq14?Y;x+|;n#KMUUtJ0sAm z^$h(Ly(WB%VWfpbmeit|qaI@#9H?*N{!xHHN+HZpB})Ow@%uO9VU`2wN`QfT{I38% z%(79w@8z_B7gGMrZBW9H;@$O-KxS|_)Y&<*da$)or~B;SUq$J^<8&t|$K0%_vPdU#ZC&q`d;l;1#MYBR7&=BWuX{t%GVJ^=m6V&qn)zW&+gdN!1%h;4O=5HN)yx zR)g@8uSnL*iyZ=3g*5E4zQCSP%Rir<&UbpSmrgJ$vu4! z=4@?&J(cOyFo!Sc1|AodQMp6Ulk2oAj@YdWRa;|Sh5VkFYf)T1mwP+-_z)YW25}|5 z)T?zr!xrhcyKKuFL((-;$$qxnx-Rxwv{a;;|3PM-w)u_h`K zY+xS%%}CztP4=odnJ3V&m0m4s5cSyI??p5rCQnaD`b*gwZq$=F*4Fw($%$qKP1TzW z0M7Pe6D(wrXOa>jB2w_p%|~E!IT4CD`@A(ZSqHU2u8mktA&~woPZEoAF^$L8@ zD$vu89~wooh862#0PdZN53}0d6xsQC>{z(lFOXxl_RKpuY!Y9%Ifih;C*H=tOQPJ| z8e#$_UHZbb3H)&^+yQsA-W#PJ2&F2Shzicfit9|{Xp+SM%^Fju&|`nH1IACG^X@JYCdezbp$qq= zJ{Qib&YU|zvb1?JA4tvN#)n%MbbWDf3H%LnII5{yDZS>QnlV6nzB>5dQ^9c!Eiu0- zw^QG+f$wabtc#+YQOka@A?$1;?L%Wp84^>I_Bd?ynAY@i@vv?N{&!?{yqIx6qv#ab zb^t~*8Zqcz{1 z)omVYHT-LA{3@4G9ryV;y4e$7H~r}RY!!BaO)wZbm@4dIf4nolk1xDbQAh2lWAzym zo&ElQyKY>@fw(+;>9cduAp1<$UYz_@e3qKTU4ZL4j3-AHApQ7iCo&FFDfa&Pp8O|0}NxLM<-0jXJFRMejr?gmoHdbb*{t@DY=_nF*} z8e=F4>h;;Ca%E}ZeE{KQeHkU_m$EM1=R` zuZdcF)1$lW!{Zwq!^d&;Z#Eg6h`R;&vi{7SkacgHt-<%18tjPK{-W9G%e^+5qtv}b zL^TGrJ>NFm^(1D(*0JCLA>>}C`48g_Gi0w zLa`kBGpAf*X}!6BMvP+vG}E!KDddG(>eW4W+nK`;b2{Hy4$&y87K)iWb=`2vxO_n{ zi?c`d^Ww$e6aE~q8kE18V=2QtnLYKrye5;TlZWFWCLiuoVd2u6QrqQ2c9Esvr$@$* z5(x z#N^$tiepdZ1brw}k{Q3A*zf*Of1_gip_CC+IgntgSg2d)A6^9{mv!|s7kVWlOpg~Q zlj*5St8Z1RraHZ2lVwJi8&;y-raUdlWym1}sqkZicJ_aAg7E8ff|&Dy^Nb+gH$I-u zS2CE|k=RLo0*Tz~7zS$gi}(Lk^WFoR$BQA;yJorqcIJ<|Zd5P6}Q@ zwc6_Y=ZJhW=NOiN>~+~*ym>kU{a&T7g7}%(AX`X!3gOOm!w34Q&ocX(k4{>J@DwqQK>Zf^!+Icd18`} zL3cFbb0VC{&6)V*YuI8_R-ED7g_SO(^;(;Ubq=2gSYs>1?1*?4izZ+r5FmH@ePm*| z{0hRVXgkr}+lcOt-9|2_ikzRo1q!#F4>zcsI3SIZ+OPLkcU^BtS_im&rBJz{A{O`Y znA3sA5Iu81hP5fJHG=t>x3P1Nrg%=aH!POno;n(PH&%bHcfy_A8M9&2fo*9g+RgB{ z8n;RPso|A3!cbPgeE~g!m5t8fqJ0L&pS4%<8~|6U1H(TaTxCBEmz4TH0aa`fg%Y^_ zDKQ7tBj#C)hPoRbxx9Uk`j*mFSvuJHj;Vij;b&Q5gws)T_a}Y{fDK8N6RaG`7;%4{VvpoOGBLmg6Rgi+IXsgEe^Bv06jEmy%JvLL(?)RCD zTt`FZDo>k#yEca&?^=%A$wbS;r_HL)U~yT+>@FFfZo-2r*CrFskMS`OMl35y8}D2d z3L-Gub|U9Dhq@r=l7Ah~eBSKcNv?Sz0Q7$U9NSug#pO<@!$oQ z=WmU_qeJMrd$chuSWv{0qh7K#2S3QkKSK>g{?C+7>c69QlFxN2%^b)<;_nDPG1FG_t z6SkR`CwrB3_|{UlXVL5X^xPB506vq{25Zrc=UhP1RTn>rePVi|*3kl6!%V08`#m`i zQ9=K|^X{s^xB}uEp4uFr(!4~~T2ycEi79C5l>FGV+;p;8dIUX&6x?*P5R)~Hu) zuekWdFkW2P>O=x4n5wjwpwy3V&4ISl?rjb@ok8zC5c!Lsdva=;*lH-DuvJcS5zIYJxE zD_!Ms5Xm4(!$Nwf{}9~lUzg392MOqL3@j-m!tHX{p8Bzll*Id1*5KqwajDnf;zhHp zwbkH!dX7JX1rfsdQT!?J27#|k$QcHxlnyFI9cPN00ohpw`u!R6NUV~gQNer7Ow zl3baug$ZV6zL81p4}puhZPCMENYIU~;1E?t{y3lh%DJ;o9C{n|T-0l*qoD_H#z*pE zqCJ46lA4DxHSAevUZAkvxVkDfF0*Xt8GBjjxYOJ(I^Fmnz#P!lro%XpZ3!T8Rtf>yq+qCm> zoP?YgYD95tb2YYz73zhWKh3JoyKOSEoxOieK=o2;ZC9MZv)a+>f)dzVP7fTkJvU6Z ztKLU9_SwwPnYmHuuh@K-&->$QF@+kfP=|*PQ685UBOT!Y?P3f$OO+(#6@xA8th9g` z;>oKHAL&Y5I-ve(odTQt+s0d_Xh z;INGIe8I4SRJys^%lg#zm?E_{AYE4CW>oso%ZX5NfzWy#yC0KAs>yLe^0@rOObDVv z#P+@oT6*GiJR$1MK#4<8dwh5-yR|#?Xb@;o#U545icf$KzRO|=IlJGYvRHRs6KXv2 zR&5}+!U9Bp?6HpnDT2Y;UVR|1SiRPUR4>%8Tu@OIhvW3^j{!jPVM zD)wFez_--S>(WSheogC5d&_+dY(QdT1LOv?$FA>srQ%CD@(xQ(dy`?w%z-i0m2z5! z`XS*2hX}q$bMvtW2GZ;JgSMZwRpD%X*=Nz|>fV}n3v5TLGEmsgDJz}elTCV0Ic({T zCP`BU5|S@Ul4#syO;#a!y%KJH)%4N%9`-w) z3hxmf+-6LjBBkhOch1$YCB9QuSD8HEdr`-3@k#uSOp$`uZqEbmMYhoH58v2eh{c%r zAR$O!pYuU0oYJm4?IAN?kzSgrqN`YD;PK6}@;oSPwji=@nloI;-`@x_)MUFcbg0CD3dqCkCk9 zBK#gz+N`2bA~P0{S(VD2-k0T|i!-&;vb9g04h$l_k43i?0@LbdrGKGifO>JF#O(3W zPVFF#qCLbm9k;p;w9+diT$kb@TnFK8S?F3_MsElgJeUEEpCBI?^%j8#r<(e!EGDs+ zt%Tv7xR3^}S+f26V94gC)l7X^2tj4o*4M{ptUV~JKb7UH7^=7u!<}zVhhVK2&Q*lWYiO|R*San5mRSJn*@^E)8y01C1Y^)<v}1oyWr!s+>c1kJL`4QIvEg}ogd^ev`Y}V8-fe97n!(E*%BreneC2 zjyiudQ!%>ZGP$=0dwsY38w#Z<%NlLedTY3Z)L!y*1*c=K6)X5V=^ie%YWb&S6sE{b z@GS{jjG}Hg{)}&dc|1?fB*{;yIP95w88ttdO#F@H2Lw5F`Z}QxW~%oCX^NhD?4E{; zri!|6?*ed2cU2aJvTct7(b{kB>vL!G4kkGa_w@`W-?DT4F2EMw=hpC89Dk#Yl5wWk zy+yIyP*dZvd^i)$8tV{!!z;&1^zGtIh5z*vDUPDmv+hvcgs#JLV)#TGI^JJ()57|P zvSiUms};-gOMZPj_9)edsYt)56-)g#*-tmEn3KUu4gWk_OT19S-(PQZ$zZ^f8aQVi zQcI?5LN5s@lUkO#$n=+zWuawFDZ|xln^{wzc2;0t9NR)o=Yi5yVucfjl@)~g!oTzp zNArrhp@Dad04`r9UB8`9R;f*nxMpwPgb$CnSiO$=MZET8%#wiw-MyPpTDz$%w^iI< z0(=cx7Rj9ybhZ>R$*!yz?m1)1FFvJLRe=^tVGTe=C>y{#5(8Wm>q4!ZR6Ec%lN=fP zJ-mB%*tnQ#!#=>S&BPob*AgG^Zzi_-Q9~a$&%yCPGRUr(9Y`U3x|Tkz*d>H;5IV~ePt zEC3A&Pp?xrA`glrfD~iEUKgti&v>z@7u5vZr)mG`?}%x_zZUs+!2{P-hOL6g`6iU$ zJ6jvo=P>m}zn3&3a6jOUph%rsdu;uoiusDouXnco_W7>7F=SE*Zb}W2rZ3uVq#6Pm zEvTnnJ79go8C}f;g%f`aYD6!2?>3)4UJi6HKC-A>wZwclB;}qiPZ!k#YKd)b>5`nt z`Ro_0phe2L1c8-VOQ$2UM_XCH%<_vYY=TVVAS2SKewH(7uDM8`TD*8E zY#5LAOJ*`qO24X_SOV=z*Bwy_`Z+*{G#AK^(juhpy_u8vt(t-jFm!2iCFY)DX$t!U zM9DugZS4k9Bh!NpTj>A+=R%2BM9M&J^blZPOzV`Yh%SQQ%5j{STx47eU?QCCY>e=j zKe-Vg6*S~Mc(16QNPTr}Es$u@UzhM}TmYH8*}e9*M(zhI>aJZ)Xhuz;%9%CZM!f!P z8g|*zEbo`n{9m4g_(=XpCJ)9I6O;{!N)u!L94983$Kwkiq3#Zk~Qx3lR%dOoODGslTNBWws&fe4D1|KO;iVDPzA!36Xja0Av*_y@UpDH}BndWA@v`e^*0{W-s38Y{ z!Q?@c*HIb}rzHYa&Ay!rNCWC{zsWBaDN0#b0GkFoeDz8skpHP&0KyE*VZ5(LTlq+U za!^W&Kc*)o#?ebn{+7fuS09B&U3|T4TQiFb7)>GkZQ*eSSBv0XqK$vDYTyDCt74w4 z!fu;uPf&HvJBQ(-DHn$w+*7PnP_8yK0-m(-%>nHKF%^ra2`cYUfEyj>7s6Cxy(cziSs)qq%a z*ffZ?#J12?*Qbb-Yi&5WFR8od-ly!)3jLnvQ-6iSn$WE(Ihd}iOXPDpc&zcdLN|2& zd*$MvRwB@JGX_@UbNAr6<sgJz-isPue(3>Hq)AY#81 z!H1u>>4XOp7LmukaS1TrQe>bi82qzI7{n3e6_`&qkmG3{U(pt#NFF8?uJ+cD+sq*K zHMMK(EtUh3MRT4D);^O=N+Qlp#rh;VTt9YMJ%eOla1p#>{i>6b!}W4`>JG`5(g1Yh zwl7hQs+6(l&%_ma85Yc$IC*_aB3R?ui9x++Vzn@SnnC8o*L@!(({eF*;3Gf~aqqu4 z>D$gY%U0b*w6#^U{`6B#i8l>WXT3!%kA`n8aQ zmmA2ePP*yu$*2MJ6A&#J0O4RZ2`t1&%0~tno8i_ndHi78Xh@>+#|^B`B{2d*Qv8QW z49he`nE+&m%+afhLH$%hNj{$Lb|g$uECZvKYF;wA!UnN)yXKg)o8!`XcV&n5+_0fO zr9SCF!x(h;yuG(#Vrx2S`CYLbN7z36ByitsU{X@+ApF}vpwL~fUYOcokpR*h{yOy5 zx_wU zi$5bAP&t7s#oF0qQ{NAv!ryp+_*_VHNWjOQba~QYF*{vDDdOon@f!;Bx1hV;Xe-#r zr%L^;(iNzMY~P#I_jEajBf9-sB$Hg4eto0aT(Q_G(6L!T_i!&u{f$z^6XIHQ4@f6R z5&&)C%k7Kk8t73u$3dz?uN3dYu<c{seID5$+%A2$wtGxOtXucaCP5k9N{!i zU9oP)Mt#jsHbTs_H+zH`FTb8fMAU#bYs)3j(~JajeL_@c=7^a{M^OjMd9xnSS2g5O z<>};U##ZD1L}lAKLXFd54pH!p_^SePGQ>;7`3fuOdcJV5uPXz4$XiB0dvVqKd0By% zO$lzQzv1)=qW0DUKlXi=ZYTiD=yv9n|F>BH$o7h%(nV8^1VR-1fu=~PcGMh0_a3Q@ zZBcRA-ZFTou(`Mfa?VWU75J7Xu{O_k>EaB-9sRhb#!d7b;s}L#j2|2~WG!8nfPqbO zmuf&d#@>0X9*X5geR+PcGkdwS-; zu1J@cTIj}tlE!z*eye{4QWK!DE04l@Lv#%55SZ|47LS#xl1>;(x%X9CCH`% zTvjC@e2`Wjw#F``zBqwVTrPW^?JUz2QBu3RA6e6QAhd4vb4}G%%pWOxz#Rf5@@9^2 zjsA=VxCox@2t>Sv1_$bVsBC@ANt4XD+Wz@S5YH8^^!k8@Y&4cG+n^3B0)g<_i_fdcLv+${3 z@QfJX7xmrP?R{PD4!!CH*#Pw}-!EMsGQ>_nT@E+9*f{BB2}>Ao7J1l!f-bu%VYI=G z9!2S(T~Zu6um>P1z;55eG~N1Japl?;qxlRt#1KF&n@E|@HlM`~XO2bRk?@KR`wIU- zRzm=~p2m23qlg2|4yM}8ZhSGBOzdk0@&F9*#f%=fz&l9h>9jr#M>NKEC7=_sPV>_L zj+YBr1)|baS~1aFPKIP!;@Jd*ARAR-X}BdhX{NZbt2muLTz_>8qfa8egT#d;fp?3D z_`n;}i~!WC@$>q#w?V(WE|)L&r@c=4p*y{sHnUsvSyaN&h}9gc6Xj`BG0a+ty)nqP zl5~~S=M9&Six)M1y}busM;v2F4=7gb2a_WQwRE8$k^cV{SOTj=MsT3P>n#BWsqIvC zRp2}hfNcLc>*?a_cWa36rgp8!OQJ<9ocUyXLyPyTNP{;j!v*4(37en zp7s-gPuU{T*VKflfnozal~bF`v!l!IOLx=Cnj6hhuGPujxWSJWnPK=tx;7OQ208ca4F>hec2_4P6PtRBI~7bKu62wf-l;ob$_HLcgd5f?02Jcw%vY z4=4G@;iM554?hv%DWfhqKI}g5^?n`|fn!N8Mla)QrL?tiuP><}ceSUT+ZlSORqz^S zo$Qu(Hqkb$u5rZl_2!xDVZTN%8M_X@e!s8#_6&t%VCE92m+I~;U+0{R!`S}K4xiQw zH>Of4J^h)dXQPQ4HaZO%nPC_Y$qBtdn6w}?97`&n1nA1c_958pO8h6MYy2f zO|(u_b?kxcO%OnulYS69ri@EKdomgtroIY@XHOlr6b^lvRoX) zvv0Ek8hqDh7K4uy4p^oG(_*P-wPMD-Oz+NNrymE6=ZO`sVroj=rhR8L+guB7hfn_N z_UvV)6k!x%ZRULqRKL#MtfHEC#Gu^P`wl*i7zmxvyHTBZ;PS|V$aZH=7CGB+Ffm*2 zy1goD&b?Y?e`%ld*3`T??R{uF!7R9?xn2EI!q)cBE%_{Bnp8+1 z+SpFHzeRSP%oz3 zyVtS1_XQickE=oR*?b7srYwu!=WD2UIanK`5p`Bel&;APYa4!3->eMPPty5lUcPhO zX=ymGfvk7hThe>v*n>>79hM1I6ip_F(8PZX0Fn+WAJ<~f@~0sjS7%wj|0S8+F~75A zvAc%GbH8nD_I&-kiu)iul*S;w2r|4^$Xj>3wKD`sS_`#)2uN59W+OsJOi*=9L84d-=Pd^(6d z(yQ@Um(dM>;!PQQYhIWRR~jVnc$-K zCMyq0W?M`uAA>KTzBUu_X}Hg>_}SWOnw#l7)Z-Q`Lsc9aUCge%W+}*y$m$>u#@%Bf z>3XZ3&Z~1Hk(kU0-!EFz76v1>17Ui(4n#c~ad;~Yic1&}powzf8S zDcr@cmfP=X?sz*}An>lR$i-gz#OIPTeId_~mFj>r@Nd5Xn2<54af5E$1G5ySX5QRn zIq!cd0Xg~mU3tRoA=Xmdz!a5txEQ0@>? zV68p3h%1?@Us3)#B#U=IZ+N-Cp)6dwAs6mI!)!mhc=K+zu$IzNF#q^1H*$mzIpSuR zDTAX;wjRYpe!!3%MMe>;I54*og|JiP{vtwjJP6xe2p9zD}vQ-0%c+ zp#OZc7~QAs5lMl~@lJFXmB<_yp6i!YyIUg>R>&0(5iZr5>16 zy9VCIcKc~G5%D&23;&+=BLGR7OA8O zm-D`EF)kMHF&yL63?hNLnupKLvZ1)3}SSfFY-`_N(24cVSl1+d=!0u z=PjJ!uidm28x?bk+rJ%%TCfxT8UAf|oMDBq6MsJsqyA;a#_YdqiCE102$JMY!xj72 zp8V*TORSVrN&@Jjw-nI+nl2ghu42x%(uNbtzl!DWE^(W-*jeT|5?u%#*2;Xn?26xG zf-T7l%mPiR@4EbK`GP-GSAHN7q`>ibI6jWL?d)ZnZbx-EUX2=exJ-}30r)=n@M~uY zU5Csx1y|&x7C7eR)29wd%kx4TM3e)|N+Hpxv23z@$AhVTP07cGdYKX~mKNUwx()2n zTM7+}iCDMxhpF4@LS4vc)FDcMIjQe{*E*Q~{=>GQw8G&@Y1llG-RqjyxI)P@&j#I@ zl3RFr6HQWHn7Httx&3r|NI$Wta9~fD!bs}_{3`AP%AC&()_diH9M?{sVJr3Ff*s3kRqn2@i<0b^4A^0K3TpA<@j#g z?^X(=$JyCF&>{TYL?NL7%U#F5kNm{vnI*-ThC`KTa)>x)~^>TM<=ue6SX>QaRmx=3;dH6Z0OBIqWTaC3G&>E_e+J7F6vgE5`WsT zW5$|}`jK856T~I-^eH$?yva~wF-ujR)FJqOsJQKz1~VUHiH^YYwH|rhl=kxcc&$+n zSH~(}OKTaX=N&vXV{ICDWGs7##qkT?riiJE%l`_fySE{w_KZXaR$=^Uup6&XiCQG3 z-sik%RZFZho?n*0w1qZD)XF}QWOC4zoz$ir^Ur=1%ku7Jx5{ZCIp+*G64)s0@`vl_ z?&l;g8UzgwNgnmJBTw%tNW*-$SPXT`hbJ6#n&rcJ?+`D&Vu|V~e_>IH_k@vigZu{T zw>bsHP35Uyae+IASlAENvXO@3iLYkmLny}t-DFL<*ZJA$U}bsH#Y3y|484Ja0y?Ut zW=m#9F@dEGq;;nBk-F@eUlNI297eg6O<=H&CLSjKjoc}R+*Mx%+lCCZLX$iqy%dzR1C8Q8c(ujB1o9E^nd0e+ z^3%U{ZLBBH8!OZ*b4r+7hu{B+7m^_8Wub3H5C$GVY{BwRY|(o=+lsRG*uvg92k+Xw zCV4Yb=G$%yp+Igy-_+dW z*X{1%+u_wLFFu&S$mr_b)!Wa$rH$p!sO9L|;^*C<#>}~)jn&=LxS@%XzR2Usq2kD@ z=xe$!l2^4jkMI*mBGr2xW=lKfRDGs>)^=h z=G5c4hvVha1hPA{sFh0-O z&win?f3U!KpRwM&(bL51`It zpp1Or(7J$cX1dndjd^Xy)z0hQ!l=p3=**zF$HhN0KdH^qMc zzL~z#&w#VUc&WVCtB&dE+1am_!_UThq_nTX!f%(Vah$Q*vyT4&0&0?{!LOH&yvcK@ zz1q2(<>AoZvVpb1y?(5{j=aXO(A3tZirdApd9A?A)yd1sxR!!+Y@xZvzo~(#x9Zrs zlYDT-#j&)Zir~1La-+Jf(%8GFmEYXUhON42jG?8_*raw}ro+eD)WVp=&DqSjzRAC5 zO-t3Me_by@&z*hPyrcWhnj0NL{~&dwR3KJI!L~n zh0(;Wu!L!sab8F~L{VmXcA>Ogb%%AEt(3~ux_xzPmak!!u&Cq6PB1{zvYoAFXU4Rm zuDrJV3J$uEgIR8WcverGYgf{^rn`uH)31lnrk${&@Sg5qEN>yi! zvb@B*p1Z%ai$PG;;@roVb%v?5yRf3?*v4Ots>_6QqQ1W5<*`m(aAjIsO>Kw9nvcAY zag4>%uA!P+hojezd{1|hMp0v_hIxHqT5*)5#j1W)fSW^1TzYXxe!jYSyew?SzP*+KIvf(&AT8qIiX|v32aP79X@oQ#+ z%&&T~l=%+sF8~DLaY;l$RCwBqz0FIU>DD!V5F{U+aNA%c-0;v{T@y>{7b-+OBZ-Ow z=~B)lghJ#@k|6N|fD_UE^!=Y{+wMY{Q??jZr); zduIQ`)55pC@R@sf9r}#s&-m!LJ{#j3W8;>0 zdiW?jOY*G9)9G}26BG4%y(nt!X0y30&slhm^*Q9b^TXrA>{(siTwPt2_u->M`B*;K z+{7C^@7}#zTwL7QIX&Ip-hP%o+t0S~@Ll+fot>R`JMZ`$9UaNL@I&wUN&Z}3hF@@c zx+wn?`PZ7BK08|u|IY1pyH+b|MN!o43BMb8!p}6rXDsXQYw$S>zgPTrzxsqv;pc@f z(Tq=9ANio$#lJ!JL-w6^E&Oy>e!7Owso7JO@|`Z8@F`_mzeWw8RO)9n%Azj6FZ|BJ z|4tMC`Kc#Q_+>>SJViZx3$KOzDTRM@Dc|Hx*Pp5Inmo%>Q%!l_oT@%udBrc;m9G*0 z@Pr?f*B39| zym+y;7GC-M;~)Q6T9d!5;jgUx&6U5x_rDEK`1^bv&&_+oo~!yQFX6p>oIigf#9fIH zcP+j~nA?z-2yvr8xI1_u$j!>j-f(Q&lh-}`eE6*Jy0^#oRd@>k7vA8@wl*B?#({Hy zxDCa&yW_V6-4x-z|Ka;@0CCrEeslfyXCU0C`=1_uGK71y^oR)8nF%laYY{M^O(d8W zfD?#2Bf_2IC7{~5=4057}{;8qia1I7WU1kj%0JwA4v+u05fw`dS| z^ltMgyb*+ZyR-d1JODR*3on1Q~o*fqWIQyEW`V*Lt0k>55T1urwB;+BJwI8*6@iD4{Q9z z266IRD&=z%_QfN_0oDZG7N!>h+D$J!2FT4# z<7dLp&*8%bfw+Ki&lTbX*$x800pk7)uSW@-Jr_I^)O(&n+|lzV2MYn><{tkMAg(JI zC-__bPxv?N*Xx8Tc>$VrPJm$qIJ^j|t)HxS^aldGtqaDPB1Q@+($*XkKyG7G42fz?u8>9o~1`?kDfjP!a2wVg!>j4_ucjHZr;0j zZ&qJg;Z5tFyj&GvyQ<&h&Fs~ytpSLuusDEQWN{nUh;Rvu8{N1A3I}|OgZu(3BMEM)O1$aAlh@#7y`rr55jZdJZ=Eu1mXO37>^skH-9HQt_dzzfjEOTL#gnObBL37f@MUrnthyL z9DYPT5$_f8gwK^-er7l7xC^x|3$m8((qQc?=$brHs z$o*q2aJazYp318O+-Rzi#aQ$OGQeDnHe9aDwFB`gjY*O>_zhyPIo;V>#Sm(Rlg+eqRZae>BV*oKeaZwtl+2zxK1YM^mDuigRQ5*8;kZu^-sIC-~yDTC^w z4XS6#;sPWT7qttZK`)TFos*I^E%&NlV_OOnd9kPeJBcyPYC`S@E zDxk*cRM_0un36a`+_;UYT*L{9lQ|VEPDq>qoKC4+#F51nfH(jE{?5zT##P+8PO1iM zFAt5p39w;e1=g)%NR11465=ZgKE=9Rl~Y$W<87RxanbwIDlaI*0f!m0bA1{&Sp}9 zxDxb@uVin8xD?zB-pK9BfQ#gBZTX)75J~hCoGUUGSD)y`5w(-Ot4^%S`bcT}Iws?I*^hyRb|7bEbhPKgi2z!XIeuGQ)#cYc1JN)1ndYO9-^QB`pO;E}`4>zw*-@!gK;4coci0O0x{2=|_X!)d@c z@VM~20>bS?7N`3-S;cLiF3!&b;ueg=1pv7aFaW+4LgVV`oNA-$vJp7JxHCmJU&jTw z#P1u6$muw&%HS?Ej=xU6P8&?lXjhG+sr;i#d@G&ADUGc8C)6@3r$5P`@pYUa4nC!8 z2v;-!bA{rZ{3mG`k?R@4aUJK&xw`))g#KVO4u3$UjK$!QyY04cIF|m)9)xSFvz`MQ8OC5hO60pgmvh?}C=0EuCFI+<1Z%!%N@Q8xU4PzKcqaZiNB z1qyfYr{bMp+MlVm@anlioM7DBw>v?Dc*03_PQ-@)*-a^2-w2#++)fPORutVfb2^ROpS=^HB;+{V8 zRUAp&ccwN7$c=!-1&|YDYmKxxsK%{a>t4EuGl;w5@OCB6sX4&mT>^2f@Hz^7yW@(g z=?&Q~ZjbB^Yq;5#E#zkX*EpjajW7Dx7H)&H>X>T{cf%U)maXEjj0^G0kE5b0BP!K~V;@m<6Su01I3Sw}4Tn0WE(*R;ZaCc`z->Pp zWN~=j6VHO)uyaI3^=JpnI3aQPrI!kF*v0)@S=`xKqBh8w8nCUPj0hT+tm4{{!Bqu^ zWs4ig=rmCIjoY@Ezc3*Y*586F0wfLfXld}KU4Yi zPt_XU+5?$YcX0vY{@4{3*9X1{#07xD5K8D;k3;H8{LTtq)?I)wr9qZ={)g{!K{SCi&ZHqBRJ}Eegi%ES^G9HQAuT<%n|cw+-WOaW9d~)Drjb>{xTrP+EanO5&0w9Mx2lRQFU?g*k5aM@P8Xsx8iR)eX@aM$Ip>VQMOOac&vWF*Rrn z*M-BuG9rIO4|2mum{VmD7l?r(90pbXV)M$)T$ri4Wu6$K~l`yBOCnSidrZp@?Q=E>f{O~NYxaT&gx^V;~?s;Yqc@IK#NT|E@B*79DZU;*`S8W@@TfPr&2uZ~zuH z)o_jArdq1Jjz?D7081KIPQvDo*887(xkg>JpPx+*xKBv2EPyp?gJy$6*~u zQ{17=s6aT?8Ul$62=^5l_aV-zaZ<%5?&#f)Vw|zKOCxa!#DT@xoQiSI#)h5^s>b4) zIg4v9r;2J$=9W{uTgIrm3?dipB28BpfLSGXXJxJ@IM#`+m&^hNQwkSA4z`hCjDz#nNE{iQsHq*U*b;Hf5M2MTnjK3-g z^}yniZCtMhfSZ|#2Dt>~R9p4;QBozy{o|>yI5&+bi@Qx0CnPQms$mlsgoeQ4Mtt!m zc&4kkd-sTM`7-WrH?9o4RBdsoX@m%OM-)|=BL(9;+|bGgRAX{K&hlreFwE+k1mX6G za&Z2()Hkv*+8PfFKj7Oakxssi-4%rU!SvMcZ{0FMHCRS&>zw)tB<|6t9N|Q3kVpOp zx?m8XsTenX%w?STegm=@#I5Sf=AmO-fHmYC?!tu2? zoWyl16i(eEeRlzy^cVI~1SN@1)KpWIS+%=61(1{9Vt}@`j;UQR*Dl9Yp=50!T-S^z z261>R>H~jNzOW#i{6=)OH|4vyIY}IV$t@)K2L3j;obKWvH~1#*0Br8Sc5z2Zy5W^( z9Z2Fn*rfX8$;$;HaepAs5CE>;CxxR+|g z)$=HcvA9%i=*`HpwKlV+zLBj8#;Hq=@rD=aE>1?(r_@y4GxBZ5;%?r%KC6PN&^3WJ zDh&)ZsPi{n!{Nif4dB}qnntb-FgdU|^~G(Blg71@2v=npw^wCyt$Y{PN_KH~)H9+1 z2l<-g*`TTbM-mqR?p9pHsn|eEoN5htdGzo>7TyZ~OBuw~5N!y5mzQl^GY%JKR56ao zsEWK6R&a;ZP6>BFwnNOQs{(FMJiY~tJC#9IM1xCH4W8w)12xt698?$IDZ&xrFsky` z3Wo!W3*2zk2Gt6SbC9b@s@X1X&PZHSxS5$kmLqfXk<}oOGHi-(&34pB+ZlhukChxt zxusL_!11kJHID?euzei8!0m4QbYpRaW;axG$h{-{rd+Nb`9Q@(JG z^JJ<1JWV;YW#^^@PB>lo{er(W>G~_3#|cithvE=dQq73*t)O98Fl;V@oFZI0ry9;J zb4;c6*C7sE?m0|yPt+D?a>LtXANN)v4j>2Dh(KJ>OO(WUWGi34?MEWFvM^;U$u>7ERLh94y%a;*g_5Vvi9p6pHmg&p3)c>c5#ULft9B?($OS)a^x?Q&-bDwF!i~kY!v9H(W%B%QaO>qstap zP|ry7EOp2&C+HS_&H*k-H8^Q26|#!j0MNnVMmh%7l~y-J#C9^$cH2q3%R^lHXz;4e z5EnqJ=c+>_aGh$%!LmglbNyr?XLF_L4eip@fn5VK{Gm<33(ViQsSRD`FOJ8`TL^;Jb8h){XBaLH5 zH9iV)H2$9MFu{;);$9CX^3DDG&Md?4PRZhIi6@h)u{a81?oG|5ed<`Say`=P@SMSwx z8V7*G8v!|D+}04)AsXY}q%1CM;{uHnwc#g#90yg6sM7f>G;TyPs?2K5M)D>KYXH79 zyY*Kr?%Iw2z~V+JH?&COgvV79Tzl$@!zOM|GpcMFnKe1pR8=Beiy3^(sz`2OPTlaX zkumX$1jk5#Hyczrr+yziai67PuM5iS@;;QK99Z0L49f}dcVFgYV}wu)P2 z$;c-CB8R$xTlMJHJ2n5p_#4<5wrzus5mQxxai>P(4CIiC*cKKizg9+6n^R90-zkfm zwmFqOBND$){;MhoSL&FmJdWPo<>idb`999I2LCbki?|rec~|Yec^WIBZXj#)?tkRF9Ew3~QYDT@r*E%gN<*iU?c?e(Ar9|IWN{6J${4iL-Vv07 z>|zRqz#tI@=&Ja4Qo{)Re&ld9nNr0lV!>3yI=l!s3>X)F;qKH_W*Gs-NitP2PTtKi zn>v@UIL&Wm5O<(VPD@3OvR)Bi#C`ZcQB`yJ0pb3zIW;gikht|sWl$gki-U@a4@_{d zz{B>g&;7vdOD^Wjni3m%|r(e<$KA9ykC1!RaC>E`qX!E_v0?^J0hIK z8%Aas+8RL;^&a0`y=N>A@Fu2_)?c$Y0Nh`(xI4Ow8y_`^Kobi?Se!2CbWq*%0o9Fh zvnj@@EpEI8M_hPDH$0ho7mg8Sad8*-PoZ(*iTi}?mhiX);R3|%OFUIDF8npIO`QC{ zc{!nRXXj@SQ(aBPq`G>(%CJ2#Zj(JqKBGRVKpZeG02S=Jm{Pxx!@bU74$rA0+@_jF zPNOf5W2%S^iDx8W9IKQ#rz*NlxY_`4Xmivu8a9 zc?x@5q{!7SeeR6Yr^K}~Z9<@|taFaodx?2=)cQ3Mc&h<^iL$^u-$V`779 zDyf<+PCuSIo2H#)8A+})R~xDJ9r?#Rwuozhb{e;+1;Dwc6Kzq>giAysg=S#yeHE4YCnRes>>ySPf#NQK3L$f5BF z7{5MOt2%HTAEKWpKwy>v{)hog5)Ymg7j_iI`z232h0$l_A9;j^%~+n+6+sw^(N zK6>=Pw1$p4M$BE&b=$9M4QGsQNr6(RiW77Tiz*rfHU?SL zf~H@pRPIAH;jpOScq+g0J?B)e;}F7Ehzo;iC=6&WE5g+z+R!WLy_`r{9Iz#^#pSxH zBb?2qXED;{?h;Ei=$x9?A{n+7={~Nka5ROuS~}ml@fSf+Ybo(Se-~zwQnT^_PN+GE zToBOeV&dyVs`HCHtU8f6&kVz1&Xr=O zhShg4k#ETc`8^4~?RH~XoR=x7tU8_WxalnPU@Q&@cO=~HNEdRRQT5`ax9OM~p7-+d z_B}C`$fbR7e-Ub{as&EuCDQ#nJWKVijh9H?gLSk;4VM z$k^R6+r%XSd0WmlF;ri~QsBGYdBujRV4g z%qhfS8OIs*&^R0)RSh!Dz)9R?T*t`}`oWiR!9^#(a8st#cl?#7@1LpQ0PC-?xKnM5 zQxYc}skoq=3Qa51RNHAvRb>XBQxm8&7N?V{W>|l3{=V$U*hZyE#V`U?Gn;Wj<_g2N z8gg8vsH|meAfp<$aq6?v)I@20B>=cm6=RVu%}GBj!_^sL|4nn0{RRNe!V<9*1nMf} zfl?4|*XosMVVPn>6#iRLB8N+ali#C_x@;ODac&sMg-`ZHoCLwq2? zhin7HhS?D%aQ7Hxz_$^dQLm1SIKce{i;HvWjhw|9!~uTTTd4>)LW{2osyeb7w=*Z) ztO}}wm6)T@RPSidhy+weRZ9iH3Ao*rx3_#w{r-oziBk@DTRd^$<$+?{WHgLuPaIiX zJ0)@O{X$PYJCB3vp)feUD8Q|Hagu4NvG8~C-Dc))Qx5mq-&X9uh;X49^W7o|pqPf= zdo?h&aogS&2V2~UVGn7eTF{ z@Sz`+^plj4{ISi!_I%Sa6HKdrK6!GK1i0R2W9k>7a@n9NEUw-^ftZ>g+&YnM zpL~r&s;%JmR}x1sdVf#W;f%AYINb;sMRt74SzLV9XdPKg9l(xq)~57Ai!pN+Cjj@; z!?=t47Ay`;BZ_eJ!rfyP<~;>Dz6OvRY5hk<^~SX;71PKdizAiOVmB+rYz=h%8o)(i z)%n|xc~_jXIA9&eQ-tsDVoJr!U6UJbMX4d|;><9DDV5G&A#e{Am*N>&Hd`E;;$VtH-B2>B>f~2JxUB13Mb$)B)#>yMMrcPxLCV_i)xwZM zyDK@X7DZwoDV3ku2Y@)$Rb^oxJC)j58E{QW6VmZ0Rr1b}dL!w_oEToLbYc^feXCm& zFcM6n%=YVPdR}*Og^!r>Pu$grg%z*c$0e*Unru+k7T0m`+tE*4BI32n^w*SYsseG` zy3H|!KbKqMjK&e+vRxcLJdnb;KXr5+kQ+XHcm{+6#Jv@W3$4F4sG?kC;w0u$1>jap zL?xx;j|s4m#A%D%iPemB@C6{KoDe%&2}RyEs7HJ%k(X5#z-5%cvIhRG&;o61Aaq^)Fc5Fh$kMM#eTqS&1n; z?hXNNWH6;VtL-9a7-3IbR`xqvIobeG^$u*m8@h_a4vzT7QY|oe(f(f9A_kO=8Z#mOI;(Yhsic>)u~)+8d$!ic@WJREfrk!w zLRq!cngN*5>PhajlE?21ORCmA%oTd$`nrwl+mxzpP0785E*nH-S~W7+RAR}F;I>*u z8j9(u|yxdh?>ZkrX!ftu=( zyGHcoEjip5v;GFth}+^gr$!pLuOq73;nsiVj|5zkk955#Q|#g_qiP8*=`@!?HRuc@qBV^8 z#Rg?@OseV%Zsck!Q65@DS=}Y~wBg?$+JA6dP^<;<$;E7A4e~b4q2|Z=i4wL~Yn7#zAaAId1JjZ#FQvrGqLL zaRPJ6s0zu!0B# zM%~u(Oos61Mc;UNe5QjcXTuA|H|xIqm13oA88?;2R0(qFlsfJ8F2^w&%?3Q_&wU%V~6<9c{*4Q~xGmLA*g3O8fK*k{lxq5cc zA?uBkYc+N2cvCuunUf2&vbGBaM32|(Y7d@3R@(;bnx?dHLZ=Z8aq_32b1r-6M67TQ zC$p+F!lkonMQd1I_JHbwLflbqj0@!>fyGIA(x2I=O5g974+e2NrZxl?$0qZWbQK5h zn@Xrg;}rAOlY)_0X})f)<|ik5@xSPh}?vcImwWX<4mW(in-}>M9;;vn>5Lb$D9>X8gc!PF=(Cslh zq}beU(#$4C-+|j(dvO^jBQFUxK^ zUtT*fPn>q7K}_YSN<$ppsA=S|BC4(iypeBUap84--e?@E;?(*9!2wG+dHv$PkuR3u zQc3mH6xAJzZ&6Z3#|VwTaQ<#;2!A<3Tp`LykAI_kiEYGEs_n|0>U|?#>#Li%rtIN< zr#ntROZ_5kwSPppp-6{Pe9WdvDwM)^x|s>CUB(GE+rrf}@l{I-;~0oQ<%k}uA?eaM zJpoR5LnpV6M8Pi&Ip9qriBmR!bS%gumn)+Ycvt?BZLtE&yebeU;AZ6~ybMNTTrKLV zJif)Ba%r|Haj<4Q-N5S&F^z9`9Om*V6=DMvRmu&6Brg289dCFGfU{NH7t**yP@Ve& zEY5K0MDq;J;WDtpgu1VyYNul%F3zcfauX`7o{&QG|8s~5>6ltq7Do=3NUET4w2rJL zO~61o4dMR^PWOg`>KkWqkFq)STNYvl(+J>gWQ2mMT1M{CGXhw%@Rrn%#D(1dn#EnK zHjN;NZxJqKah22-uuYFQc>JCmF1-Gj(YR5oQfkSNy1^+mlqiYDfDsiePUX~FI;949 zBa6HK>9bC$9&6y3>LhNW2clE+!&}iY;(`N-oEm_I$BFSbK{(-YM7V%%;d_KPaJfaj zzQszUXWKfa3dX&DP08VP-Z%0-z!zy8_0+f8rlc$m9L{ZV+{L*oj%tH7f7_Ws&v2ze zLlYs@W@e4^*|e#V1|e`(poADcnP9h_Z{mt5H7KC6qe$h3c3I|`2OggBuV6o&_~I1f z%C_2FYW7*U#+9?U!GuF6^^L^h-~5t8(gIxYJeNfiY6fxbUEyg4ab0fbgw>Vy8>!Vw zE!HHX^Xr1O!Ww_uUEX>?`fH8=C(Xahif=qrGyT{&oc_p}t>Rp6$T99fG43ciP!l2U ziw>$mQQbL8JR{EHPEL4jX5G+j-_cFk+)5f-U9kv0Ue=Ar`O;JW2k6x~uyRy&4%bO} z+!noY&g8g|L&uT;+pmDRUo%lP*y1F>^_1y`pX>tHw_0mXL6z$`YlyRXbcAict*ck_ zbVKVeSlp0i^Nnk^i6g?b;EXdGHydwoWo?*yRy3lOzsW0~A06Z3$p($xtLazkFmFLm zm1M)Me}10?8@^Gs0gp(jpD?CQD#EQB#A!TLvRkS&Sc%e^+J28o;`G}gRfcol!_8Ay z6@Uw6>!2ENN~%TzRGpsgSgU0iP{Z>j(74~8r8G|6zw)U&zKTn#l(xa)gvI?^ zNZfq%j4b;q?!q*!iHW+0xU4Y_V`kS;PLAhkwjq@q@b-864v_7PD?25E+7g%gJqk*l zig1O}Ege{GAZ=&;N`MDS3{)H_NEpb~^zdrQ+p6+iMMBQPb-$^~zfXTv8&|Jqt&J1* zu8%L$P?Xb|wWh{hLpeg6jGHjA2$S2@>#O0VLCv9ov9#d^JIQoGtY82?oV*FbY|{?2 z&Uv@_oQ6_0&ZX>ac{*whE;opF8jFAbv;z)$NA)P;o3G-07Y9LANSvq*0HqUaF^3<{ zK#eG8qwD_qevWZTgzH4@aZGXXnyQlNiCzS_4iOGwL#G$#RE0S=kA$}K2y#47v!?D} z7gWLHDtA=BQx->v8yT@+!@Vdoq(-=O5%-rYj>eGzMRhDX<2Kab(Mr2UD!w>paSCaB z{JhwQnWY=(Bi_)nTRNWx9!Jx!d{e_nWO34H9vECu8+1;E=Qp0++7AG?!cAPyF1R$8 z_C*!ET2Hg2dc&Lg3^U@3g_jM)*sB;Xj-RRfciHMMX^G=nuG!SKrr-T4 zP0h|~)$$Wr{AU%$xCZm91UA}8tm2%ZuEam0wvmzzC2gxMDa&|&C+WtlXREkQKCM=w zT#?H4V^Wp!s+^IQ4&X-NX$NzUAnKrtH-R{P{ML1I#RZ6KX`y+-;__!Cg}I?Dj@Xv9#*JO$sc6660EF9UjgF=)ZgwEu zfZV-JsCfA?_l_hPRXf{Y1#xII7cbo1D5q-0$Su2``WgAu&(7i=KKKN?$b&%Q_ER|( z$p);Iy(SJMPOn4*yS zc?(2t@zmthw<?nt_O(?K=X{^kG&76%gdt3ysb(VKxv+w57g zVO1yAyd%>i`yD$qs313SLV)X0P0d}u!rlOJ9b3q4#X)tAGwK#CaO#B9PR!K(OBM%X z9Ou+;5#R#CY0t=rusB8>;x=w5&KbxJWpP&qa`-0_*5IL^E;_yy>Lmjn-<1333l0}8guu{@fyuCu5G0? zaS4MflG`zbTRLF+H%gcJgTc&@udjIvItNjpU+N$!73T2ojy_F;6!VP~)&e&$qjm;M zfMfaZM6!nKH~i=g|Mcuwpg`6xd&zf|lht-1Ky>Z90%ySQ!sbH@uL@ZIQ*HT1g}M_~3;h94v7% zs=gVp{+cn4f~x8bH*HQGQH?>fsjZQ(0GtBfAdB)ORkboy{mp5Qs;+)BKzQK z8?i5L)D~+4JtHlB=i9ja^49E+aTjM@aZ2LudNOqkIiBwmV^VO2VRpS?F<8~`rh z+;6&*%gznzpsF-ZjlUk^+O`lr!VP*R+E;OPf?G^3=`t>shC7X$(gauK=Ah*qxP&`f zj@KHR&4HRY!kEeo@VhqrV`FyJCg3)2Nvjb!X$v-$vYmIG*VICSQMRU+qZ=Gl3)?(a z!$b_5eDE-w74mkZi6LgOz3 z_*#D{GIWle)zYkIbZ zv3sWPg~{QAH|qQqj8hW#ld-tl(ua9d&eMqVmk>A78lhKYIEC{$HNeT=2GrCma%=F~ zwZU7e8`rMI1Q#qF)En*$PO5tr!Z)+X-mux!RR{C^-r*)rucl%+-5Ap?oEBlKWyC5* zV2b;cOsNkavFmqIhSYsKo;uMp64&(m(7uN|KRY)$Ref=X`YLY@GhZArZa#Sphn#u0 z!O{&rs0y;-5s(AM`K%f+5D2#;ByJHPx2=7_rxsEL&kG2razm;%XnH2Df02v6(6*5w zH)>*QT(g;p4!Vm=c6L@V(zHvdu}8h8ybUD)rPcEmZdZv_NVt$dJ+*7*;j*j^RH8ep zvs6C%=KP97*-(fp>QP>)ChIC(2Gs^Hw5XH-*4Cz%Ib#S!6-a`W$VMLuGj zT@?%sBVXd63W!tBi0|US!EFCFxnF%OwyM541-X?xt_t;mEKW9ZrlsblUlO>^fcrP@ z<7N!zh;c-@H;Q$Mpz0W>#~Wb${SFybOsNWS^uOJ^`W3z5Ke4!qE$+qui@T$8Lx8xe zcaqx;1KT*?#3_*j()~C%s1oMxSh8W12#4U--7#y6gJFa$?gs#zgi|Hi;Fb~5Qz11x zU`yNzisJCUOWjM9R8GVxgm_v|{UXPraG@#g z_vLty#`M%A+aN8R$u7Z2is^TcmDjgo|3MUT$!#)10lcD6mCapH0;B zh(jDxJNfPCG{)8E$r_?d3K7KqmJ>-Bx`2Ndjb;SYVs?NBC#T|LDK@cwS#Z_5c zU0IymqA{UiGQ0?bTf#fr$!RyHoT$-*T##8;*0a0=sX_-gY>93Yy@8^UR8Wlphlq1) z^p8YTV?gzVJAX}Yh`u;vRKK-KOt|8(i@OI7cQ4v~iDxbUuw~#k$l}tvxQg-j+D5jE z3w=tXt?IGp+0+@g=Ry3$Hqy$oTM3cVDOKb5V*t3jh;YI88=kuYaLjJ~Q;Xt|-TL&I zEbh?*o@}7h5KvC$RK zLc{KsRn?VTsZpt#SDg3M3DWzkPNjN`IdDkrPtY+EfK4FIlB%o$F02UHftd`A!>R2S z!RZRqiR1E)N4Ie8riM1C(4~n{wdvQUMO2ln>hu)V21mHZImStPE2JBqsPk9K;bOGm zKwWY`xFc0l*`uTg_l#9aPqO?L(_3085;kwyGS1F6{JidKIOTF0bjU|mWpOg566AWh zqB>)56x({SG7iY5-Z&dr!%Kz60quxzCO5=%!)=ut?vck)ZjePBFGCxR8%aR!FIgPk zT)Uz?t`fw*rjjamab}7et+Kd1{pJe85RD;weuVLtrV*UnvcQ(!+`0?fFZ;}IaZdfG z`hIVJHcwngZmB2k!P0|$4>mABTraR3QVti(MV!T*YsHA3rtxsYp)ol(|LTN_WGcjl zcZ=@~;ucSLcz-MCrXn&Z$O+1QQF~lU;~3)NtSVOE?bGTm?o_LkSQp1^gPw@))oo6t z=#UuW2Fga7Nx;F5(JX7ruhF~gW`m#4N&>0_AZPB8LL*M7#x)yom*^I|d|kFEO3%6a zuAXOWSWHs0CBH8GTtPN(Z^X8fYRZ8Ou5USqc#^B2JuZ+q-l8VFu?kpDKqHyeu;4;c zK#U32@@gr}X^RrON@^%l3PWNW;I`XL%0{AdBo3$x$-(GA;$Gr7O|XoZ-VnQ#L~U>y z_bOaV_3+j+!#KT86A;eB4fTl$Pp2BfEv2Rr$2lKV**#+SxMGpq3a%0=k?WY{cOolN z+8P#9HTE_GkkiwHYl?DlS`}>L+c#@Ir~dTtr=RZsoh%ON200b2g6cg>GJFNod=n>W z&=AA_-+4xy#clW{)p1U$nf14FN;Mu9bT8a&&f@kG9@nz7g5vsx!Z3ysO!$6<#*M@F zD_Vor{aOOwmT@Acir63*8(@jU4M_eUy})*;sTUijC4wJy)Z=4a!Epg6Z&y(^BCo67 za83!8D5oID>=xg?BN-DGcd3f%7v06#J9Wnq!{GL1HzsZq@&cD{fQ)=dZK6a5|Hb=X{2u@CF=g| zOC-O6s?CDkZ`4aUZ0lirrnPE%M}o3~VjJHzYKdV^;$LFtL*?%-eAlr|DPmlBf}?}2 zA{7?5z$?)&57ZZjtX3*FJWpuctDS_!efW?iRP{EEKpYY7#37CZZb_hS(ohaa_Va$y zb-u6Bd_T|yE~nCJhhwUsTc?6>c1G0zu4mDPtqkXk#3_&C2JVeYt6*^)RGq~U;iC0- zLiY#u=jyAM7sLoa+Td6(n4%cvD zO1*;Rm-shbA zbswssKX2Pz6=7iv;?ujP)r# zZ`rCVOQgPI{`QG^q@ZxRLyP`5NBJPNl+Ayc4E-`gz)3?(u|HR$g|KvHi=Y2dC{S?D zzBo5-M36_iTWY)drOxCI$$^!$l3gXGlWy2mB%KUT4yb1atE(EA{OnfW%pGMDX0%sv z>B8KEb2mt#iZ$zB%7v2hOl&BR0KY**a!Y+9?XtQ34ubX^u+^!Xz}x4afD-qbZA#zL zU+dlqhq%~xJpAc&ij5~ zZP$r22RK*s$_C||!lX2XBX{{o)nr-6fg5W2NRS8BUs}balE1>QYfjEY>zpBOuZofN zamCgHNtGV#{Q6NvA|h>i0&!{YyZ(j#dLFP)RwyBLE68?&bLaa>TwLnqDBRIR1Uh!NmU@} zy_e8Gj~#4TA9gb?KyR&$YF%1s);)y~`QOV+eIAU4D(Qnaa+K_k%gekO9B_Oh>eG<% zz|Sw&QzWw?(AudJ%cemUz{P)=xxq1?vN?+Y00~%-)}408m_m4Stna2n!&o&x*;BJ& z6@70NSy;YMoE8aK2YXSp@`Z`ymxl|8jnvHr2SM4qRs*Mj2nTPM`DWd1)H4`;-8XkS zaW0CIy#seujAU!CBgU$&*BCM?3p+ydO1aerpm4d3A3)oLEsYPmi60bw22%RB*(e^5 zk1LPL=u#g9nhks`!W>bNb8BKVaZBTSjiY)nAF#ub)0NI=!!?R&0A?I5DE_hnS&S53fz>*L%I*X|{u8YVTcuxx#7N`UGn;315h z{Fm&RWszCHriD^%{B;R_^oe5W`?qxbFG6gEC2I5srYDhU_G!3!krj_Pz$YG%zOG6< z)o2nZYW66{!q=#3417^M+BE$?N;47S~Nf-(N>(xXrvLj7c^@$$R|dvCoKg zXht^)2~!`dNwSuZ1ZBkV?KUU)jXN*r&1UQlEvFb16)~WrORyC5S=$)%gL>gDGP9Mg zeYdW_{8uhV3Ntb689p||(@g)(EY*L85EpGv{57s&>Lm-P_D5w07InDD#vR4!p)V=I zqU8f2$H9u?izU4=smpXBC&5QhyImkRthFR}5BrYd1?}L~$K1;8FN?WNyJppMRa(e~ z9DcC^{m%V)iVB(y$LMw~1m}CK+wC(Bj3_=zyxhWpj&BC!hrN++OaX!o1HX<#JhY}J zOA}L+I|jecamvCb03lvcY%W0U)64jE7cXJFNrgr1%085V#1gX$MA$3niuT}dt{8d% z(U0Ev%0>Na$w0f-0OHL4=B6}y6%Sq4#DLr?zz0y?d6dZJnl%{AIsUHLf9cdLBQ|AG z<#lsg{;-hER`1EB-3Q)xvsJRw4nJe37H1FBJ8tp9&D%lt<;2Y;H#!|0mdu8`8ZgMy zN58^v_1(nzese~da*xO#afGv*>6pMd^Thse+-W|qbLOkNJy0DYosAT1LKjjPKAkCP z2ihL>r(d6x?qe-fXh`A%PgtaI)L0sG>OV6ISbMhN^S88W$`Ef09pF+3_k!}jjQZ2W zCDnio{U(NEofRGu=l8~TUsE~Ydz-|cWt-y+>G@~3UVt?v+*ep6q6wq^IoI_DLGZ*% zm?P_e^UQbH|Hdk!kwDXecw`XcVg+X9{U9!K1^EydCC*Pln^6AEPH7yWfWd2zCSrEe z*S-CII?>|3>+gv3Q25p1N8%DvwFf+IvA@qzQF+9lo-6P*TkD!DZf0w~Y+#1`=96RD zDed(a#`|mux~PRuO2%tVk#9o(8vGpWWQY&c(mzi(bjF|yr$3lMJu|YH`&J)>qaK}Y z%}Z0{H=yEj=eGyL|9*AmF;(LVG_)u$Kul!gL6!ms6@PvaReRa2s_m1(HsXu7JLkum zJET9lGrTAO$7UQ*xD9pjzJi#{aV71J*8Qw~g9D}t4EGH{9o5b)EZ6Ut1P8c`T7G@D zaN&*3lzw5YTqpLEv?tZG8J_n|PnbM($s7aE>^C=$M}(r$r{wsXxo-gsj_`WaP*NwW zCub&_vq*^QV#i;yqK}>*=-|0iNxRndLERW$#+;7!P8IYH_Rt7*J8|i0mVCJ}H-A39 z6VMn>wIa{dVNKXV#HRMa(=n|)iqc;Z5Qvhd1ABf5g3VKre){-LXf_QoHKPPO#85f}bVCBMJ6KI)u3y`X~i0m_%q8$p5QL zt@Z;Qe}4ASgZmsF6hT!dyA_NAwgAp8_LV$A-G@7df9ObUQzOQB>o$v^BYNc*po-4-`X~2mbmB>EP|z8UITbl36bXl4uvsR_{Y+7r##R zN6Ng8aBU=5ata&Rd^~!j_j(d!;QAGBI=}na9y!_alieXIQ_!ifm4M}v`BA!F z;ea`YvZMVxd=&9koGTDjGJd1a0n-w;XPm_Aav`4%cniH=m^O>L`-32MTcsf=KivFx zH28Jn!)G}XZ!{v{6r{bN>zm~CJ<6UI)TCGc%rYTep8U=OpyIV0|T8QFoLA(gAG?&-Mg_-dl0rRD! zep!cWv72*VDZb7D`~hRTe69aZgm}Il?#R_#AE{to(}MVEG{9e~S4lKBx?4Lc;La}S z$mKcBS6AD~roxJk3uPZpQbC%}+s}x&VnH_pR_^dR89aB1E@Zw(>irI|Cnj@o z+s<=`Xb^;tVoRU>++wv8|BbRW?nyecOnFVpz$agk_>1Y^CFNw-wsJu#LL{AJVM}-0>K&f}Q zntzf+IVXj$)Qz~Zw5hI06hBN~!j%kPSa*l6J^1wAqRrLT>?5qzZWHu8SoYk5&==-< z91j^VBmTO~*Sc!OQvx) zJkcsvw-*zvf2H#Ycn|KStC}Pzp~OQ7yrnUbvM659yBj!)3y(QBdYJKG3lP*28TlOU zl2Hl~?M!l}5o+6Xwt!kpbAsof!1{;e*-hwP@i2|1Z9yDkytk2AEw9T(kwo+-8aAZC zT}GszW`_y2f&l5OMG_Uc5A6&-nuVf_*!tb;GzCv@<+IFkzx#To$q%WL^-+2`o#FH7 z@`8E5l`bgJsn%+?p(zWWMx_jp3QBr;U<~+igB4tb#nnPD1N4{ci+?MOQS!I~EnM5{ z#g8F9d}Ln(+{4x|4ukW?q8d;x|5Z#OA0@YIM&yl=%w{?mI!OOY2IrKotR83j+`7ec zx6tv-wsVywI_0%;vGC8jr{i9D2mb|N*%r5F)bQ6bZxp<0-9Dr2V#$5Hc7cmn4lyed zl4mUOT!4*w`ch9Kr7M#$*~>yDHaEP!d}o{=g#ZFFw2b?F9rvL`)v6bz^#kh49QSPF z&~B>4c6=3YRI&o;WLxx`0U>|KV6MYWRQ0z;_HiW34WCh!acm_ps>QGFzvH+~^wwwh zVLXbenkwR!n_Ood_h+;|gO%!X<~h#PrFNu%rbvG9dDq9~(Wg;bmFFT2&g8#8nm3ua zL)gbO-3aLWpQ_E8PO59bpBc;~&Ny$vT=)Ai``8&}Mt$F3(h9B?>$!2inIT?Q-Trte zB0rTga>>h-f%S(8D#w&}GmKqj%zE?}7I+7)^5tEM1aTYN#C51d7;0qZ@;Smiv*UxU zfn^q7!+~cLfs3E8E`kZ?&*67(mRgzUKN01EU#~gdf}=NH)ks#z#~1>l%rS4iN{mmD zFTVeehSIIcqupH>$1S3w<3@q(pi^cDvxRIb+dAOl5u{kO6lM}8{8`WYhx%t%bv|$} z5c6KIyVioimf${$7W@C~uJ3Z<6sCnwyWtjFzxFNu=5VsXO4-OrAA;5?#|PJ;PJzgJ zE8KKrwV!(Va%B(pma#=+{;+8AMPE*iLvtLrKVcLu{`J^%J#p=q%tvkk@Z!q22Amkg zuzyEiQz5gYVS?%5)5SfNRYOLdFf%71J#8$@Jg&8jPFt0zzH$&8J<>pqlM&p$)JGoi zJgRy(|%rb%@O7y9o#kIAg+WKtHG|Jw3A$+jI8kPforcCf&PJ!(B0L=qb? zTcqcx9=ZVDCr>C_kGJ|{H8?lK>A>qy%Mty89Xcs}WN}ZzF$Bi&ml&AeKacgEr?M=a zggrS|I2fcE^`c3_}3iA;~Cu8Um~Lc*dw&L(5W_EkB_+ zTZ)F>36_l_aC3qfUt;c1!w=IyXP2j)8_5;q%=qhrbnEG8CmqP;3gyNFpn1QwcBlVo zTQch9dNeeN?OG$Zl($>>Pj4h&;(nn;IIE^c(iCUn!&g zX*FEHF16(7XO!2~qA|u@bE8jf9Xt=q>e~>B?bhi(+V&a5Hs|ATi!{~mVw^Qb#wKuY z+=7iju^F;Dgl*wjS(~8$r;_Uy>!~A6={~EO#}^fn>r2yG%tHn&VSWAmxu3yH?n~)M ztc9${SQdZV*8S=b-nZ} zil2&krIbgkXQ}s8eZy84On)Jx0))F!MJs2~EMoDXFYY@E)DbV36L}$S9v)Ucr4A8U zWTiVD4T;QeJPey`Xx+HPumn*{Z4(mSNBZ5hZ5tnS^G&p1Vui4W&^H-_Xgf$Ol$#r1 zQ2l_#l+K1lNP>hIEPZFu>SGgv7W@ul7&7rz_=z5~{|Kv=ihk2-U3=a{qDPjzBwoE< zlNFym6Y^(HR-xVdR-OSSQ=%IGZ|FrPy#HJT4q%L@^?$NfCRmYM0kAMBjj*6Tje)z! zoMT>HMllV$4z8fCIDp?z<9DzK3#+;%Tu2WXXQ*>7S%=bk3Vd-ccWwe1ENw>-A^vmr ziCT93!`pAX?4FUkNTm`b7}flT{di;I-L zdjBdst97!Vs$>;B*^qJL?p53(MUp=+ryEt<>5rTl;BYt={pCjfd~deyUW8WRxg)pm zowB~8kaS{YkbB(22eI)>9BvYKa{^qar_6}hQ#I#^jxp}wB<8sM%x^VKR+-VzCJ5fZP zyyNh0rXIPJ@N57f#8Gg5lsqfey&Fb-@o0r=li8mLmdD7(c!r}Cucq_(G`pX4u5H5@r6-%m6f#>IbUWH*D4^q^&i)4>|~f^Tx__M z9xf@Y=mlU3k4~SR5ivCFohhq+78$VX6+V0m=-5h*MII-H+je31|8&v9FCLBl;?7O_ zi)w3A)wG?AHI*zpK!zAB`~*KsZPfC|&MVATUl zW9m{jTR?_7^*F#ltoc&%s6!mUj~C?)NoSVqCw%~}X4de5i6Meqwj=W{?A2aw-U+p+ z2u#V*p|}1*MhGCYhDheN=t|5Qjz4+(KFIsaBIaSRun{xy1F)RDBH+2|L88kfDGEc` z9Q$vD0k(?aiD?fmc&Dt1efwa;-X0|Zm7d)RAOB9nup+`T6iEK=d&vGgJ_RE~$){7E zY3M;aYSs^}B!E$PxM8bD#Emmk@V-1?C_4&<(O-=2?tE$67ZI~WyE=Ff>dN}P_U_B$ ze4g2F)LA$q{2i;J^Mqpdx%5~6l2(?D3;FUXiBxNhicf3d(SoBjMMHq7wZ|vXBN2Z~ znzP62i(N>Yzi}`2sV{O@;7OsikrrwAq@3CXGQ+&*-28;^!g10FcR2B1*vrLcXgV{8 z46W*_k6cwfURq>pvWDwn8!gxPmN^;Hr$5K>OwfDDB3dIpZ)$`5FU4&){_hYdqaj0f z4<#dS9*T8uDY3|DGX%`-7C#LQe?;N#Z*v5`ocTlwdkLhs<_il?sKQ_416!HrYDbVX z#O{?#+%USWeOZig-cfmqU7~^q;=|1he}?9vn($YLnUgBIzVZYe;Y>_4#Wm1jCR{O1-{p!#DAIO-4_8oegx`p;*{LXYe*yVzx+|EBDvNzE zNMhC)&ciHIAsL^7_D2s_qvF$aAg^$YotNmjKJZz4GY^lOy&bV)iRpPYLOTV}v;J;O zy-IpMV#mLzuwHqDZN`R=M>mG$EOHpD>)ZGfW6bhr>1ns^>%=r6ix9i1h^wIIW&;S! z5rqnO)Fabq#JD99>N6MQ$J3s;<6d`!%xQqL?pZWHaRBKlLr)MnuFSaW|Wm>Ie+JykKDrRj~)Q^vo|pY+ibh9l$}MD zf__EzOL;;7y7`GlMG|FQfpnR$+AnMZ1Dy&`9k2%EHL69v{KTujSBVG<(if+>IPKb{ zNP8fQ$9jpGDp{>Hi4o1(pCC5t0W*~sX9`|D*w zDykC|^AGmdMM{?f6W?O|*F^;e8Ai-OM>mP{3^cF@kKzLT3TO}3i_5KG_c+pNI)^ic zu+N)&z$xeIC(KkLjCTI(;K2{0hBUJoJv9HTH!=I`B1PX6?i7q*<#E9Kc#ZRu9B!5? zW~&2P4vc&4(@tfPtU@sz@pt5BNPU}dOUc;hFLN*eIoBdhz()@#hR@u(#PuyGKW#Q z`M&<`&RZ(f&G%ttV<=dAKLEuUmizFbkir@XAG>y0J8GoR1gz9@Yx_jjhL*zKa_2Dv z9!iu5l_VG1P-zw-^T^%q$280O=D(dg@A+ZhmdWx6mq|ESt!66=aAM9f2Oasc6S=Bw z*h!NGYpvKJan^erW)AnsTdjO>EKwYW-iNVXY)n}7vp&~Qj&E=8?OOe6rY*bqv)Vly z^X|@KYbI($NLL+`nu0o7Wop$Rg6ZGAvU#m&(`=n<(EkcEupR$!j1#8?OTy&~N&l8X zD8^G%D#-I5deocQY}dMwck@b(7a_B1pPuh2HqLN{BRMebD%H|2Jn5s9rJ5tAi;uip z?gI8}Bck8yx>9c6CHUV|lr`r$ZM%!A0mIB7=-IUX6AC|t<~55Io{?W9EJc@JEDf7) zvakJAIqgXwtOGt(=8&IP$$dYMGfv5oXiW=H{ng<+KruV>6Y(HK_z$1kz|hQ!<2`RU zVbOl3YiG;i9Lso_H#dfLioi+b3JepcSh&_?C@g;ev2ERJaTr5aW@o!Oza-1~J8XphEb0EG98YhtKsNHyPPcp9B_5S|jRP`J37!q=b0raCrG}|EmNx76J9CzMRbS*FszjTNDvPIn$}C)i1j>u0 zs}v`pjjER!_{aQpSH`p#i#)1SkAPf*`ZA{dy}FYrnflfDeh@v=PhO zSvHc#yY2csawdj9Fx|I~X}j}uSipItJ>Nr>)~Oqq@qk8SVc{;yI1y30U&=yPLd{pT zzSy1(^7VZZVx|in&+^d4{<%7*`j7L#pV45|Lk}F$;73S6QJ2usq%VDb*M$`m>-jCl z;YJX}oVuo1u79w>viX;ve3{Rt&sy+ru8o0tjI$6(p*HG~A<_y%l(OGvf>9%EVJMSZ z;B#~I5k>lREDN`&HOvTo z<6;_K45t;m4bEsc(?IycO$&z;1K+R^&yv3=5E5}y;A@HK|Hz$)nm8B6OOiMjRL7Y5 znVXuKnmh^F#ak0I?wHIs{X070p#VM}J9KCDu$mP*eY^oN0zMdaWnb+Uy8 zO>-PbjaA&0MWQxwuZcw;SQzZkS6jEVBmgNg?mSl7RqqljZxK*aQ3p(LXliiQUOu7jcII#mDEVxypYOcy zBS<|w5E`^6e;8c-U(G?Ekh)o)@2-5Ej1+yOe$!@_5ujBoeHzmB8Ivkbn4~=Vum;50 z6gN%o3cHO}5;GgnxmDUdOy2^rpTcqKLTF|rF4nMWwAaBC_x#nv`x;g&5`2u*JeYY_pT3EjyRdoC-LU%g?diao@Wh;PBM-2~*IIy~KGe^%m*hV&pWh&U`sY>&>4uSu?lY3z?03`t?sExgff)kauJ|Yn zcRr$m4$}q{>F6&&9J*9)L@-I)`vpBJOX0yxXpBw; zJfVwrX)doP4U(_$U}9+T+~2tcJ-csC^HUaWTNM*hsA2Pm148L?iy(SVx3gLo(n%kF zI_32_7!u;dDi2QDN}0U+MM2i)I|PZl&36-{;pcu|%h-~Df8dJ_K6JU^lt@!31q}%$ z{N~Y&&rfR#d6F!Mfc_#nbO6uYTmV^wc-ZAFETT)L3wo=Fw)?d;xK06ao6tC8r3F8d z6<^U~0?C!}aX5tUu=JA)NdFL%(NiM&7$}aQ{DCaz z?H5fo(Nx5l`b>dPO)p1j@19$@0S9kpFOve4sp<~deBDr^OaYq?CH7ooeGnL#b;BQA(A zXuJA*vO?uzjM!fHR1wen8aZ4mejCadXISR-b8<0>;I9SwKJ;Xm%ydgQdaCl?zQMQ67cWO3~Y64Ena zsr|S_bo`^szm!TSvQ%J>VJLJOStj53lkZ^f=EBe6DBG7=FJapSrSp;hsM+7~iF58| zy$M(jv%PIs@hWyQ5?FdSss_$~%e!{8VvPCA`?KIXGPL)WIf|M7Nwf>@O9Nw=XCg)9 zjx{g8r+l*}+xes+$1C11*#0>mD%E7x;8LYY!p&Hl4z0!Ao3~#Fe_9M$BUyDS+icgoLgE^59>_6Xa9NE^QiQ5AaM-@%U7Vq(Vj( z!0>yf#mSNd2WZ64OqMowe|~VtorM$peGzf!IF*K(nvD35}b;E!M-HCMLj|yui?IzgDum?Y zmrws5_5)ZB=(yd@dc6J;=do>1PcxSw)*)V}Ug9I)J6K75QiAr4@4?hQ{_^sZ^ zYqv%bsgu%mVjC+JMGa@2$1_6bX$HKWXt|8EAnic_pb@XZt)!P=BNE9XUGC9-lHsw1H`j5ChCC!rFhj7I5^GC*q}XdKq38wy3I#}_ zZ1xvHwDBA9*VJ`Tsc;cW|VX(XI{GbIeHKhKBmsqRdjK2{wsjZ%M zZKr!PUurh|gaLuFk7QYP#3)wtTZqix$De|6@&OHdkQz=>6@ePPh*>t0I5;~w13f|V z$%de*9lU_MuQ%L5pberLAA2I0r}{Iu^;|Kz1-6=>ZbK#+MBAAL!)4Rff;H;BXI72p zycu?4$TqQ@k%ojn)u*-nRRTjQaCwzY;bY&KjWrW}(3`F)g}$52V%A5Q*O}&Y^yO-; z0NQ|a@0LRJ7JRysQ7PtW1g?Ki)M-ZtIJl2vRu z6RN|>9kN@4x#nBvEKD?B1%5(&{)vUqTxcMs$K$)|YIJ4oDA%2%wQ zQgVVdw%>gau9jI{NOtpVY)|Nu_E6)Wr7nh@Y^mwE@8^iRRU8`CC5KmOlt&kck?okb;?c#^rH9?r_Pnrvye<|Sw?M<6QvK|ZLE{~{17^S}LW`y6H^4K5 z4(uB z><7Iu$;=S#{*B<946Tw*OYpyY#Iq=kc{pqX6Js)?$eeS$X ziRt8{D=p`9nQKh1JcgyyFZDJ-sw`!xcv)XjFUmx^OC`YgKqpYD1|1JYi_`Fwcs1M4 zLPyI%R=@dZe^p71bGn*os9r`hDs5A8ti*qV?;@kOb**RqR%E&9Gu$9UP`Vz-3sWz1 z``AIn#h;z20mbmz zy;VEP9pZx5tn88;7?*7r&Ml?_oCG*CyglE2OTF5k4}O_QR+nIenRJ$M7w=b5B86?~`xachtsT{jsplHNi7#xKjOGvC zj7RO)G@5PwX6 z@kH#e?}a?GIW8SSo)rrX;5RhNy9s=pX3D(hjpnKO)9?@l4Ivzyee_~;B09*HxMi_j zzC3nqqLf|i#_CD$%*zehDW6bS@TCUW1rWm+73LXv#2_G>Jka@sK8oWmWj_AKf6b12 zPCE4`_6*cmwMWn9Vvbue{C%h$NPD#38*~(Z^e?ns(Kxqi#{S@+-E795RD!GkuFHpW zv5#zsmxQ&-H69m>8pyfcj)jxYwo$XiW_l^dL*?RZ)X(h=T<_*_nr83;*5eJvO3=R4 z@xgDWK^f@RATO3o>KJ~3!35jw$j&nl z6W6d^CD1hZeoYXJSv6hza&18Q_?y^)I>{vK1JvjCtBW2Q$W%JQi1@`Oehu#M4E5U7 zuGHLBrDj;oazwYH$5(3&{c)NTVD~Mud@=ia#29|Vq+nOad4sR-oSn0N|3F-T zG+Zf7?wiWEsp70rb~;g}dqL)_cb^IZH;U((8KY(@0s+gdyN50n4LoKHue zdT)iDDVT=InmhUXG&0J_DDExdseEM=)##0QVv%v=-qnWnP{(%WtGtU|URb29ZWFtc ztZ->JS(Nbds>FvUM!xQlFfV!)*Oqf3WSG6#ILlGj+q{9QOxF#-Hj63n6ISC@*~6JP zedE-^r{~3C=V`sx(lUu>5}yNnS>Dl*CsawMSn?+b&k$GnviSV8Vh9qZHSncdPqt6~ z?^Wy5==*xmx@c}lYaC&e;chLJ9HdM`J~(NO$=}Xga}#C-ds}{nTkhTbA~-R_$Le}9 zM2TX4z}4A@AIZSNydz{Iz!|@I^K%@TCy^=9&?g$b(Xohb;h2BfMT4E8mETvK9ZyHuCagOrp!R{3h7`jUj~D>1i)>OT^=M1axKfbeSvu?-MNVrMxS&kBeDmYbGi$H0RCaGQ2K2ihMYr*sMYK56EQ5 z{-{gi@qO~Rr9KEP^X_M4^4ObdSS`)2dnB~1g4hysAF=ALR^6?Evfjr3xkS?=_6SJKlA=Q|T=RRupIhEI<>}$h83x&t zjAzK|%CGYuq=b zETvhOK>%3-*et2U5-S2qRJTAs2lw|ldd}G3Ydqn&|334SWvoAf5IYQAVU=(DOyX2%bVfH~b(Cf!ZFmvUcGx}M4hWScOk-XKwf~wDM z6M>Uog>V^^*Y9yE-ARj#A0KaKyIOuR%lRhS{xD$sHN_1H?a3CUsKn7d9ZNUD%Fd4l z$eXDTWtm@C?<=M5t& z$?4-8gWZGoX^d>$g*1(GQpF}%uq;br&cSwo$-r5FEuNjrzhh_g^Ue`zJQ$*8=OKU8 zEU_wDY#YLD-)cK9;>9f6NU{(?YB_I0`a(u%(!t1BiEo4@aD3F*(V`eX(L?#tCUqfG zyR>gB?Bj#qM81ZAmb{EXd@S3GzdNf$ytqpl6fD@!Oa1`k;DR28@*hu7!QF#cg=KLf z`gtRjbBD$|B=b>56$meOf0{Gbf^ziP(+Z?0(W-H7ytdv=s-4`I`-eJBk~Ztm}62R*)G$N z?d;JJTI_I@!7Z)A{X=$`;0J3r6GVxdmKguKZ&(AFRI;35~~Ow_Y7kzElczlc;d}D2yCa$m*_vDgwa`H4k&o6 z8#SCB^XYUx*)_3ZuF8>O?=It13NjFLYh>>J=JR{gwsqEjSe)cy#Pbv#vvV|{3Ko{} zrMcjgd(*Ep`z@XCBZ#OghShCk3*0QA!`ITr^Y_a(i zuL*ZuFg7XpT9PdMz zpDhL~*{-&wEB^?~)kS}iH-p{XRroU7n>8puPsj5BEY)vJ)W`PwJJJd!l$(ZI=n{1y z;NOh0caY|n2Fcd3o z(x>zVx7f6!wBA&-O&vXj;;uXJlxymoGllBA!W!@Hx_u9BGRjgA>1dN5(FH+Cf;TnR z7|R#RA>-}o&bphD)9NObAJ09MJ9G^fysM|Gpf}Fu`(T${wUv9b%!GmSJUDdDvE%ho z`-(JI0*~~wB*&lr>jbZ+*e<|PEOBa8@->D3fKtNiyR%^1iYuBrjLLHevucXitf4=_ zIQ~JHNd)f4%OqP~DD-4Fk{cSvxZ8$K?`SyzJ+qEo~xk?v*^h0vCvmIX1i10<>I z&Nh@oGjnbLT^*r}RBmbUMAG42;H#Sx-S0+HcLw^3>JkSWIMMgNlIzkbsiNv`-1&v( zfIurxWE{m;enVN$|B$KZYArpwc9eJL(}iuYUZN?X4vcywXx9B^e8^UB86aTDh!KPvH#og`vrMhB3Nx@8eE$gFrM(_+3rgp|0R}|i4GsNzu)qZXh*>V6NYmi@ZwUEiItZlo|Woh z(+B$C#rr9tH_CP_M%Fn8_WA5YdzULcrm|NMzpLyP1)8ksKR|*uUVdZ2u`p_Yp{H(v z^B@4lM})12ARg#qJc;WLe?fu2R1#m7_G*(0NnPSm3>E^|#@l=d7vZgN;D}%@U~k|# zKsv>i4ed%WVBzMN0+G04a~36x=##RUAv|Ag%WHLD5jA(JViBEm6}|=ZG+ni-rEqLh zKryFo6zCzX4g4^MFP}81gZGW75hjV?n!)eF?=!ce`Mrr|UBj#<-G&Px)E0L+6Em$! z;#{QIW>HGg9{SS$Frj#t*;u8FyEbBy7By);rnLJ4TSAwn7SJo;xinQDD~uPnuKO)D z4tbUynXr|`AvT%^oSr>`f<0B(JdDCmWStmjX?$JBApk#vUsJ@e<%Vkf;ODYpt=*5I zirO366YBXV7h7*gVJ}81cAkR;pXoM33%1Ab)iJz3x<8RajF)?ilMN~VyEFFM5?h+t z(?;I9IfXwj#C2-Pwy$*`d@^30OM^!_QY*r@1ngKw-=6iJ3Q4ma{U-)^k@c)JSe-28 z$xf$|;_=|76`X>P&`;3I$hCpW(Vqp6o83mAy&SKcLQtqBv658c7$LvqAok7a9fX~do(`)o%@2YD!N$&F%03cqFDn9*+zTKq_Co!p9d zqo$v)4&MLEbmEI*QI5serGs}_5{gA)+%^Es}f3pDZok)^qNxM=wd%a|H z*a3I80dh<|bpyTfz>`dXmUM^ojX)%$o19tSl!gX$zbgB*G+V2N;?d0l_I(*UCU*cjd(yJT4qBUsoCU{q5$K*`M1 z1&_Kg-59%*4;6py1}+rIHJul+F#D_x6bP*FcUch0nAJYyF=sv5Vc|`PU&;m!brR8zZ=q8R3*GcO zM??~P$BkJ&OC$vjwgHK8rb$Ky*_v5$FMT3U!V@9B_a16!eXu_+s-?XwR@un+LJ7w=gb~F|2}fLJ9J)2v5Gc51tRc>icy9Uqof0^U`X##YSn`;4w|~k zS-C7z5Cz9vlGzce<0|>c5Bih(ohiJ-fE!|qS=IzAMXGJuuKh)gWEMH7r_$srAi#zAe56=8x^Oh(zK^ShGNb#WI(B>= zOl^V@95#?gDk7tmqJ)E7f>Pz$N|) zmG&L`7XAK3xeo}xRW7NySCV4VEmz6y{kSQ3C;|3}x^_qzWpV9q62}~19wn55$|LaT zri*woQM>@89UM_Eh21>lyV3HkqkNUoP5BAEel2J;uQQ!_`U&>)j|!Muys3dRz`z?* zwY9LOmOvH61RcH3fyvExIg8QGjjbulKl)pP6f>^*P#5s71~}$Vl{Kchg8BqZA=)j; z&`hVcX*;V)*EsD@<)O~IY<65WvbAGGp$l7+o}2H2QD)aBel zIm{>n@Md>$Ir@|PL+363xA{{j;J+{Fo?`%sJk$%5Xy)mINSWAybS1iTE(e^{?Ttse z{e!jZh4KGUbQXS1y=@prkx+pjA-T~dEj>_58fh3ICEamIkq+sQk{Z&n0YgAwgwhQY zfq@`78YGpFeE0qd=kqz|ex7q(-}|~S-0|#3w%vPleW7`26xB)WWU(7&`W#O8Gc>2w z##GQQD&{nmo}}bjX^r@c2J!uYrW(E;D^gk4W~WpLd%%*&*tRsx<7k1@Pn~_T6T?Ox zI|-@vaLS|*^LsIZO-an`Ye1#8W__-QnwY<;HtlPQ3)mx8R|)R}biwhV^$ZM;LFfW$ zd-8*+$BsuZ7ZY#_+)GQK=G=^C@G%O?N8c*p{+DeQ_A62qO9;K;liLg4O+(o~O6a`+ zIx3_U*d=DnYv2z&$JT1_E(#4nst>n1GHq#8OVTW#EVkYXJO;?(&3uIrQos|ZJpC|^ zotEYG+;$G+@;vC#O!Rj;sCTfLX|}aYTB3{QZruhjfIeh{y@2FaEy;A}hav6^V58}O zJ~eKAsg(7cM2pTd3s6;_eBtylu}Dx!$@%_MKOYVZ#qWsMZVF(%Di~s`i*vgGDy@ia zB>uTqdCJfSmp`G_1l$%s$eFnLwNE?Cl5u}gAC*&u6Wd3Kpudg!HOGF;mev7pl&5i{ zPB7Vr0VPe0omb&cw4JLnoE)kygYAi9Qff(n#{6MO_luKC^l?s6T5;PK)%ZSs zC7ZA-c0;77Hf1cliPyYwKa!pg%xWH`@GpMlLq2u3`npWqdb-L@vAZ2 z)GvX~fJ5JpP-ySw@5OsmiXXkX<|XH)wx2V4AH+9}6RUXd24yAGeHp&)pR65D60Pz+ zmRV#xa^GG|I9{5<6r16PRbQ9(l1rU&UYc6tQHbjk3v2`lPB)5o)eNQbg|Oxn%l=Szx@9wELD1`MN7XMa@DEnydN zT+kG{KzSS{LnaA?I4V7h9G@tmQ=qI=-xMpmpK4Z~ARt?Sd`D||-nWAN^%wRIJn#>R zV0iNw7=@Hf!V=JgPm;~Q1@ENSU4M0El3VPcHyu_;VdQ2bWGvR!yE?vPQT`+zzL+A(^ zY>shLV5AkyVvpmS_%tq8V^bWdw($h}7KUk-Gwp>3KxcpxI3SA7hRMcq?t4Eo-A<>6 zNsg?FWvDg}sNAv#zvg@%dhaWVoljA8n)a};kl%D_HFU!}*y}SK$h-~8BO~+Gqo%AJ zBz!2*LafT~;ikcg*1Nq`@69w3)8~vc*r}tLgxOzxjZUU}{7tF`{nKvDwj?p10+(p9 ziS90bwK@Ip9_k_Ja8zOQWajg6<)o0mHo*y()9gm#ny%0!w-4e$GE6$5gu+=5qb}aW z*ylXV(uh*jO7OZ>z&s86ua&@@A%0FnJj$-j7=QNR@#+;96wdS)hA?j{Pe&O^Nq%`2 zb`@7l#>CvS7rP}lZPQJpJuvkCG1%dN&^o~~x-?XWEp|;DjJMDWg*$w{Z#7ukw3|{b zn7H(}$^WlTPV(gcVyt>X;-H@HIW?}9vuYp2Y=iPc44{><<< z2#aOoK<{~M|Nfm6ovdc1*vZXx+^`-!O26+puskr_pOJQ;t}Vs^{x7}h3bN1n#IGQ$ zH*kYI4$j%$_{RYKXCMM$bj{CQ;7HmR+u&5HN2^c3KZwUSy%gNU+~zk(WPiHa)6o6`^y@b~cdNB*1%|s2Io25X&-4 zPEa5gia+(5~nr^&8P*=32lIxxWkqF`pQdcbInx~%JZ}b z*3Cn(1nyXxBH$V?_<1TvO8T?<`g@ineE@s1%2Sz$4lx^mfU;kvOaRvGsu#8*?h*NI z(_@9uet*-uRXBSn`FpOpW+D(Xq>wF22il(ZqMyWm=v!=6N+IxAJ|Rc1fNWuO;40T6 zdV#fd#HFT>N?DkzHUjjhLZxZ^5=2u#11&IYBuMTPV#pp~qnfrTo>lnv#$II5AS z*!hUSU%N)Zb();nP3t3D$&Gt_{RKqFa#UPA_PzC0G&vkB?+NuDe)0#Tkq-6de#zg- zUM;-N@Hu&T3VS!Y<2}@5$~)))C0g|!V-RM}5C@!d6JAZ{Cp;Z=#Jp7hPF7xJQ`T-t zQTb+eA19ab>P7U{e1&_5^u7o>8g#4q_xtG-3O;L)PIrDKji23%)lB6LGSGW6%PpMa zc(w2fQDDrPV%I$Z5k>0rR%`&oUSfzyQ|}(dR_h+rm4xKO3}rF1TQ#7p5qT=XD4SX8RHDo=iF$C->FO+kF0Aekiam(PrwQUXY9 zwWV>n(!&p7l^^Tev)Eu-Xd|Ou8MP7VEO%^N%@9nfiZ|$~)Et`T^{1mM+=pvBd`w+=xp~O_qx9R3^;=nPFml{*p=(&dcQ6TXeqL6>RrkgO%z5Me(wy4^ zt8uE(`7JOJ59z{Ez__JuGG zRLFT}+-|=f#FBrbx~ z!)EHQ_uc)}GO&3~Yww->5^JYh{(&N`|6bzr$j(Ot1=-Iu&HGkyYq?Jf8LGVZ?whu> z5t6UYL*6|2W)t31@LTTRQE~R;K?1Sq4{eCYC|9oK?2ammXwa8mkQd@=gGGbbi=uhH z1Pa4jk-k+NJY2=_w%wXV`y=pr^(aY)tZzfM-A)1s$n4i@RL){WkBT!VAXL1K#3;uZ zc0Tt<5_Dz`jEfy|M0jvbYL%+9bKH)@AqVOOp;{{f4GDa6W(a+VJmfQy7FIFSLD1Sk z1K;!}t6h67lzEeDV}M{D%T|(V=(D`$%u43RjKj~EcSQubX9%^Vwme`kuH>Ob8YnQf z+7Jnt2*#la*#O>DpO0P`kGh-+gla6AjC9VMv8fy>?>w+MWmR7k?+Q2#RWIBPQE8p+ z!fsX~+Yl7oKVgfcly0&jxc#E9=OJ+bt?t=qDMAs8eiZ9-<9Gn$^7j zSQ`22O`=YDUa0d&-`+CndPwD6iFd2o+XVunTgxtmQ{$S1UMP6MV7v(7gP@I9S413o zl3#rs#L5%Uc2ucoT?jP8VuB2h7X#Z+-x)J{kDWgM6Y2|J-X02-OJ|;5=u+hUJBeE- z5Bs}Xs>**EvLb|Cq4M4&`3_gy{uW!;&L6@^zcwR2x{6x+#Da#=k;H?NG!g!Gj4-C? zRzH{yS*SCPe3-imIuOHM=3$c54b~4

qVQCeOc)*Fr$i}e!=@13K+DKV!1+%R&rw>3mbH$ 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
B5TbI1V|a54m}UTX${_A) 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 KMNUMnLSTXs5Sa)7 literal 0 HcmV?d00001 diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index bd801923e3..d903baf8f7 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from './../shared/shared.module'; +import { GenericItemPageFieldComponent } from './simple/field-components/specific-field/generic/generic-item-page-field.component'; import { ItemPageComponent } from './simple/item-page.component'; import { ItemPageRoutingModule } from './item-page-routing.module'; @@ -13,11 +14,16 @@ import { ItemPageDateFieldComponent } from './simple/field-components/specific-f import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component'; import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component'; import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component'; -import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component'; +import { ItemPageFieldComponent } from './simple/field-components/specific-field/item-page-field.component'; import { FileSectionComponent } from './simple/field-components/file-section/file-section.component'; import { CollectionsComponent } from './field-components/collections/collections.component'; import { FullItemPageComponent } from './full/full-item-page.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; +import { ItemPageFieldsComponent } from './simple/relationship-types/item/item-page-fields.component'; +import { OrgUnitPageFieldsComponent } from './simple/relationship-types/orgunit/orgunit-page-fields.component'; +import { PersonPageFieldsComponent } from './simple/relationship-types/person/person-page-fields.component'; +import { ProjectPageFieldsComponent } from './simple/relationship-types/project/project-page-fields.component'; +import { RelationshipTypeSwitcherComponent } from './simple/relationship-types/switcher/relationship-type-switcher.component'; @NgModule({ imports: [ @@ -36,10 +42,22 @@ import { FullFileSectionComponent } from './full/field-components/file-section/f ItemPageAbstractFieldComponent, ItemPageUriFieldComponent, ItemPageTitleFieldComponent, - ItemPageSpecificFieldComponent, + ItemPageFieldComponent, FileSectionComponent, CollectionsComponent, - FullFileSectionComponent + FullFileSectionComponent, + RelationshipTypeSwitcherComponent, + ItemPageFieldsComponent, + ProjectPageFieldsComponent, + OrgUnitPageFieldsComponent, + PersonPageFieldsComponent, + GenericItemPageFieldComponent + ], + entryComponents: [ + ItemPageFieldsComponent, + ProjectPageFieldsComponent, + OrgUnitPageFieldsComponent, + PersonPageFieldsComponent ] }) export class ItemPageModule { diff --git a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts index a8cc309ab6..d7d4c0af0e 100644 --- a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; -import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component'; +import { ItemPageFieldComponent } from '../item-page-field.component'; @Component({ selector: 'ds-item-page-abstract-field', - templateUrl: './../item-page-specific-field.component.html' + templateUrl: '../item-page-field.component.html' }) -export class ItemPageAbstractFieldComponent extends ItemPageSpecificFieldComponent { +export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts index e84a52d1b9..ac566c5e53 100644 --- a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; -import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component'; +import { ItemPageFieldComponent } from '../item-page-field.component'; @Component({ selector: 'ds-item-page-author-field', - templateUrl: './../item-page-specific-field.component.html' + templateUrl: '../item-page-field.component.html' }) -export class ItemPageAuthorFieldComponent extends ItemPageSpecificFieldComponent { +export class ItemPageAuthorFieldComponent extends ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts index 6950944f87..b0f02bb718 100644 --- a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; -import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component'; +import { ItemPageFieldComponent } from '../item-page-field.component'; @Component({ selector: 'ds-item-page-date-field', - templateUrl: './../item-page-specific-field.component.html' + templateUrl: '../item-page-field.component.html' }) -export class ItemPageDateFieldComponent extends ItemPageSpecificFieldComponent { +export class ItemPageDateFieldComponent extends ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts new file mode 100644 index 0000000000..fe1ecec57e --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from '@angular/core'; + +import { Item } from '../../../../../core/shared/item.model'; +import { ItemPageFieldComponent } from '../item-page-field.component'; + +@Component({ + selector: 'ds-generic-item-page-field', + templateUrl: '../item-page-field.component.html' +}) +export class GenericItemPageFieldComponent extends ItemPageFieldComponent { + + @Input() item: Item; + + @Input() separator: string; + + @Input() fields: string[]; + + @Input() label: string; + +} diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html similarity index 77% rename from src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html rename to src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html index 4a27848ec6..bf74aca347 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.html +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts similarity index 86% rename from src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts rename to src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts index f69671a5b5..525c898c0e 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-specific-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts @@ -9,9 +9,9 @@ import { Item } from '../../../../core/shared/item.model'; */ @Component({ - templateUrl: './item-page-specific-field.component.html' + templateUrl: './item-page-field.component.html' }) -export class ItemPageSpecificFieldComponent { +export class ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts index be8102359a..3ec11c8e3d 100644 --- a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; -import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component'; +import { ItemPageFieldComponent } from '../item-page-field.component'; @Component({ selector: 'ds-item-page-title-field', templateUrl: './item-page-title-field.component.html' }) -export class ItemPageTitleFieldComponent extends ItemPageSpecificFieldComponent { +export class ItemPageTitleFieldComponent extends ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html index fde79d6a04..fdd60d5f68 100644 --- a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html +++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.html @@ -1,3 +1,3 @@ -
+
diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts index 4f06337032..805fdc160f 100644 --- a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../../../core/shared/item.model'; -import { ItemPageSpecificFieldComponent } from '../item-page-specific-field.component'; +import { ItemPageFieldComponent } from '../item-page-field.component'; @Component({ selector: 'ds-item-page-uri-field', templateUrl: './item-page-uri-field.component.html' }) -export class ItemPageUriFieldComponent extends ItemPageSpecificFieldComponent { +export class ItemPageUriFieldComponent extends ItemPageFieldComponent { @Input() item: Item; diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 5f938364e2..a6dd4a47a1 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,27 +1,7 @@
- -
-
- - - - - - -
- -
+
diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 58a056a5dd..fee7753c82 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -12,6 +12,7 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; +import * as viewMode from '../../shared/view-mode'; /** * This component renders a simple item page. @@ -35,6 +36,8 @@ export class ItemPageComponent implements OnInit { thumbnailObs: Observable; + ElementViewMode = viewMode.ElementViewMode; + constructor( private route: ActivatedRoute, private items: ItemDataService, diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html new file mode 100644 index 0000000000..6301bc1bc9 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html @@ -0,0 +1,21 @@ + +
+
+ + + + + + +
+ +
diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts new file mode 100644 index 0000000000..39333503cb --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { + DEFAULT_RELATIONSHIP_TYPE, + rendersRelationshipType +} from '../../../../shared/entities/relationship-type-decorator'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { ITEM } from '../switcher/relationship-type-switcher.component'; + +@rendersRelationshipType('Item', ElementViewMode.Full) +@rendersRelationshipType(DEFAULT_RELATIONSHIP_TYPE, ElementViewMode.Full) +@Component({ + selector: 'ds-item-page-fields', + styleUrls: ['./item-page-fields.component.scss'], + templateUrl: './item-page-fields.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ItemPageFieldsComponent { + + constructor(@Inject(ITEM) public item: Item) { + } + +} diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html new file mode 100644 index 0000000000..6307bcc25f --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html @@ -0,0 +1,32 @@ +

+ +

+
+
+ + + + + + + + + + + +
+
+ + +
+
diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts new file mode 100644 index 0000000000..d65b51ad7e --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts @@ -0,0 +1,18 @@ +import { Component, Inject } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { ITEM } from '../switcher/relationship-type-switcher.component'; + +@rendersRelationshipType('OrgUnit', ElementViewMode.Full) +@Component({ + selector: 'ds-orgunit-page-fields', + styleUrls: ['./orgunit-page-fields.component.scss'], + templateUrl: './orgunit-page-fields.component.html' +}) +export class OrgUnitPageFieldsComponent { + + constructor(@Inject(ITEM) public item: Item) { + } + +} diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html new file mode 100644 index 0000000000..4763670b94 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html @@ -0,0 +1,40 @@ +

+ +

+
+
+ + + + + + + + + + + +
+
+ + + + + + +
+
diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts new file mode 100644 index 0000000000..fc89cd8644 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts @@ -0,0 +1,18 @@ +import { Component, Inject } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { ITEM } from '../switcher/relationship-type-switcher.component'; + +@rendersRelationshipType('Person', ElementViewMode.Full) +@Component({ + selector: 'ds-person-page-fields', + styleUrls: ['./person-page-fields.component.scss'], + templateUrl: './person-page-fields.component.html' +}) +export class PersonPageFieldsComponent { + + constructor(@Inject(ITEM) public item: Item) { + } + +} diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html new file mode 100644 index 0000000000..ccf342c867 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html @@ -0,0 +1,32 @@ +

+ +

+
+
+ + + + + + + + + +
+
+ + + + +
+
diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts new file mode 100644 index 0000000000..da9a4416b7 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts @@ -0,0 +1,18 @@ +import { Component, Inject } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { ITEM } from '../switcher/relationship-type-switcher.component'; + +@rendersRelationshipType('Project', ElementViewMode.Full) +@Component({ + selector: 'ds-project-page-fields', + styleUrls: ['./project-page-fields.component.scss'], + templateUrl: './project-page-fields.component.html' +}) +export class ProjectPageFieldsComponent { + + constructor(@Inject(ITEM) public item: Item) { + } + +} diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html new file mode 100644 index 0000000000..4965359495 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html @@ -0,0 +1 @@ + diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts new file mode 100644 index 0000000000..cb7293f514 --- /dev/null +++ b/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts @@ -0,0 +1,36 @@ +import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; +import { GenericConstructor } from '../../../../core/shared/generic-constructor'; +import { Item } from '../../../../core/shared/item.model'; +import { getComponentByRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { rendersDSOType } from '../../../../shared/object-collection/shared/dso-element-decorator'; +import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; +import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode'; + +export const ITEM: InjectionToken = new InjectionToken('item'); + +@Component({ + selector: 'ds-relationship-type-switcher', + styleUrls: ['./relationship-type-switcher.component.scss'], + templateUrl: './relationship-type-switcher.component.html' +}) +export class RelationshipTypeSwitcherComponent implements OnInit { + @Input() item: Item; + @Input() viewMode: ElementViewMode; + objectInjector: Injector; + + constructor(private injector: Injector) { + } + + ngOnInit(): void { + this.objectInjector = Injector.create({ + providers: [{ provide: ITEM, useFactory: () => this.item, deps:[] }], + parent: this.injector + }); + + } + + getComponent(): string { + const type = this.item.findMetadata('relationship.type'); + return getComponentByRelationshipType(type, this.viewMode); + } +} diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index df8d8e713a..770d123f92 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -1,14 +1,10 @@ -import { isNotEmpty } from '../shared/empty.util'; -import { URLCombiner } from '../core/url-combiner/url-combiner'; import 'core-js/fn/object/entries'; - -export enum ViewMode { - List = 'list', - Grid = 'grid' -} +import { URLCombiner } from '../core/url-combiner/url-combiner'; +import { isNotEmpty } from '../shared/empty.util'; +import { SetViewMode } from '../shared/view-mode'; export class SearchOptions { - view?: ViewMode = ViewMode.List; + view?: SetViewMode = SetViewMode.List; scope?: string; query?: string; filters?: any; diff --git a/src/app/+search-page/search-results/search-results.component.ts b/src/app/+search-page/search-results/search-results.component.ts index 14ccb5d541..976f422a38 100644 --- a/src/app/+search-page/search-results/search-results.component.ts +++ b/src/app/+search-page/search-results/search-results.component.ts @@ -2,7 +2,8 @@ import { Component, Input } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { fadeIn, fadeInOut } from '../../shared/animations/fade'; -import { SearchOptions, ViewMode } from '../search-options.model'; +import { SetViewMode } from '../../shared/view-mode'; +import { SearchOptions} from '../search-options.model'; import { SortOptions } from '../../core/cache/models/sort-options.model'; import { SearchResult } from '../search-result.model'; import { PaginatedList } from '../../core/data/paginated-list'; @@ -24,5 +25,5 @@ export class SearchResultsComponent { @Input() searchResults: RemoteData>>; @Input() searchConfig: SearchOptions; @Input() sortConfig: SortOptions; - @Input() viewMode: ViewMode; + @Input() viewMode: SetViewMode; } diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts index 4b558f8726..e8eddc6cdb 100644 --- a/src/app/+search-page/search-service/search.service.spec.ts +++ b/src/app/+search-page/search-service/search.service.spec.ts @@ -6,7 +6,7 @@ import { Component } from '@angular/core'; import { SearchService } from './search.service'; import { ItemDataService } from './../../core/data/item-data.service'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../../shared/view-mode'; import { RouteService } from '../../shared/route.service'; import { GLOBAL_CONFIG } from '../../../config'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; @@ -68,7 +68,7 @@ describe('SearchService', () => { it('should return list view mode', () => { searchService.getViewMode().subscribe((viewMode) => { - expect(viewMode).toBe(ViewMode.List); + expect(viewMode).toBe(SetViewMode.List); }); }); }); @@ -124,33 +124,33 @@ describe('SearchService', () => { }); it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => { - searchService.setViewMode(ViewMode.List); + searchService.setViewMode(SetViewMode.List); expect(router.navigate).toHaveBeenCalledWith(['/search'], { - queryParams: { view: ViewMode.List }, + queryParams: { view: SetViewMode.List }, queryParamsHandling: 'merge' }); }); it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => { - searchService.setViewMode(ViewMode.Grid); + searchService.setViewMode(SetViewMode.Grid); expect(router.navigate).toHaveBeenCalledWith(['/search'], { - queryParams: { view: ViewMode.Grid }, + queryParams: { view: SetViewMode.Grid }, queryParamsHandling: 'merge' }); }); it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => { - let viewMode = ViewMode.Grid; - route.testParams = { view: ViewMode.List }; + let viewMode = SetViewMode.Grid; + route.testParams = { view: SetViewMode.List }; searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toEqual(ViewMode.List); + expect(viewMode).toEqual(SetViewMode.List); }); it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => { - let viewMode = ViewMode.List; - route.testParams = { view: ViewMode.Grid }; + let viewMode = SetViewMode.List; + route.testParams = { view: SetViewMode.Grid }; searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toEqual(ViewMode.Grid); + expect(viewMode).toEqual(SetViewMode.Grid); }); describe('when search is called', () => { diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 0351a9a54c..f936c4b4ba 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -5,7 +5,7 @@ import { } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { flatMap, map, tap } from 'rxjs/operators'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../../shared/view-mode'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { @@ -217,17 +217,17 @@ export class SearchService implements OnDestroy { return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs); } - getViewMode(): Observable { + getViewMode(): Observable { return this.route.queryParams.map((params) => { if (isNotEmpty(params.view) && hasValue(params.view)) { return params.view; } else { - return ViewMode.List; + return SetViewMode.List; } }); } - setViewMode(viewMode: ViewMode) { + setViewMode(viewMode: SetViewMode) { const navigationExtras: NavigationExtras = { queryParams: { view: viewMode }, queryParamsHandling: 'merge' diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 145b58e27b..bd26663583 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { SetViewMode } from '../../shared/view-mode'; import { SearchService } from '../search-service/search.service'; -import { SearchOptions, ViewMode } from '../search-options.model'; +import { SearchOptions} from '../search-options.model'; import { SortDirection } from '../../core/cache/models/sort-options.model'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; @@ -48,7 +49,7 @@ export class SearchSettingsComponent implements OnInit { this.page = +params.page || this.searchOptions.pagination.currentPage; this.pageSize = +params.pageSize || this.searchOptions.pagination.pageSize; this.direction = params.sortDirection || this.searchOptions.sort.direction; - if (params.view === ViewMode.Grid) { + if (params.view === SetViewMode.Grid) { this.pageSizeOptions = this.pageSizeOptions; } else { this.pageSizeOptions = this.pageSizeOptions; diff --git a/src/app/shared/entities/relationship-type-decorator.ts b/src/app/shared/entities/relationship-type-decorator.ts new file mode 100644 index 0000000000..8d510e53b9 --- /dev/null +++ b/src/app/shared/entities/relationship-type-decorator.ts @@ -0,0 +1,22 @@ +import { hasNoValue } from '../empty.util'; +import { ElementViewMode } from '../view-mode'; + +export const DEFAULT_RELATIONSHIP_TYPE = 'Default'; + +const map = new Map(); +export function rendersRelationshipType(type: string, viewMode: ElementViewMode) { + return function decorator(component: any) { + if (hasNoValue(map.get(viewMode))) { + map.set(viewMode, new Map()); + } + map.get(viewMode).set(type, component); + }; +} + +export function getComponentByRelationshipType(type: string, viewMode: ElementViewMode) { + let component = map.get(viewMode).get(type); + if (hasNoValue(component)) { + component = map.get(viewMode).get(DEFAULT_RELATIONSHIP_TYPE); + } + return component; +} diff --git a/src/app/shared/object-collection/object-collection.component.spec.ts b/src/app/shared/object-collection/object-collection.component.spec.ts index b27a342eac..b033d1fe97 100644 --- a/src/app/shared/object-collection/object-collection.component.spec.ts +++ b/src/app/shared/object-collection/object-collection.component.spec.ts @@ -1,5 +1,5 @@ import { ObjectCollectionComponent } from './object-collection.component'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { element } from 'protractor'; import { By } from '@angular/platform-browser'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; @@ -38,14 +38,14 @@ describe('ObjectCollectionComponent', () => { })); it('should only show the grid component when the viewmode is set to grid', () => { - objectCollectionComponent.currentMode = ViewMode.Grid; + objectCollectionComponent.currentMode = SetViewMode.Grid; expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeDefined(); expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeNull(); }); it('should only show the list component when the viewmode is set to list', () => { - objectCollectionComponent.currentMode = ViewMode.List; + objectCollectionComponent.currentMode = SetViewMode.List; expect(fixture.debugElement.query(By.css('ds-object-list'))).toBeDefined(); expect(fixture.debugElement.query(By.css('ds-object-grid'))).toBeNull(); diff --git a/src/app/shared/object-collection/object-collection.component.ts b/src/app/shared/object-collection/object-collection.component.ts index 3af92443c1..05e22514b7 100644 --- a/src/app/shared/object-collection/object-collection.component.ts +++ b/src/app/shared/object-collection/object-collection.component.ts @@ -14,7 +14,7 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ListableObject } from './shared/listable-object.model'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { hasValue, isNotEmpty } from '../empty.util'; @Component({ @@ -56,8 +56,8 @@ export class ObjectCollectionComponent implements OnChanges, OnInit { */ @Output() sortFieldChange: EventEmitter = new EventEmitter(); data: any = {}; - currentMode: ViewMode = ViewMode.List; - viewModeEnum = ViewMode; + currentMode: SetViewMode = SetViewMode.List; + viewModeEnum = SetViewMode; ngOnChanges(changes: SimpleChanges) { if (changes.objects && !changes.objects.isFirstChange()) { @@ -87,7 +87,7 @@ export class ObjectCollectionComponent implements OnChanges, OnInit { private router: Router) { } - getViewMode(): ViewMode { + getViewMode(): SetViewMode { this.route.queryParams.map((params) => { if (isNotEmpty(params.view) && hasValue(params.view)) { this.currentMode = params.view; diff --git a/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts b/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts index 9f54bebed9..12da0f5b36 100644 --- a/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts +++ b/src/app/shared/object-collection/shared/dso-element-decorator.spec.ts @@ -1,10 +1,10 @@ -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; import { renderElementsFor } from './dso-element-decorator'; import { Item } from '../../../core/shared/item.model'; describe('ElementDecorator', () => { - const gridDecorator = renderElementsFor(Item, ViewMode.Grid); - const listDecorator = renderElementsFor(Item, ViewMode.List); + const gridDecorator = renderElementsFor(Item, SetViewMode.Grid); + const listDecorator = renderElementsFor(Item, SetViewMode.List); it('should have a decorator for both list and grid', () => { expect(listDecorator.length).not.toBeNull(); expect(gridDecorator.length).not.toBeNull(); diff --git a/src/app/shared/object-collection/shared/dso-element-decorator.ts b/src/app/shared/object-collection/shared/dso-element-decorator.ts index 51aa57bc50..4e4d37579a 100644 --- a/src/app/shared/object-collection/shared/dso-element-decorator.ts +++ b/src/app/shared/object-collection/shared/dso-element-decorator.ts @@ -1,9 +1,9 @@ import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { ListableObject } from './listable-object.model'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; const dsoElementMap = new Map(); -export function renderElementsFor(listable: GenericConstructor, viewMode: ViewMode) { +export function renderElementsFor(listable: GenericConstructor, viewMode: SetViewMode) { return function decorator(objectElement: any) { if (!objectElement) { return; @@ -15,6 +15,6 @@ export function renderElementsFor(listable: GenericConstructor, }; } -export function rendersDSOType(listable: GenericConstructor, viewMode: ViewMode) { +export function rendersDSOType(listable: GenericConstructor, viewMode: SetViewMode) { return dsoElementMap.get(viewMode).get(listable); } diff --git a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts index f383367e3f..af6027aa1a 100644 --- a/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts +++ b/src/app/shared/object-grid/collection-grid-element/collection-grid-element.component.ts @@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core'; import { Collection } from '../../../core/shared/collection.model'; import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; @Component({ @@ -11,5 +11,5 @@ import { AbstractListableElementComponent } from '../../object-collection/shared templateUrl: './collection-grid-element.component.html' }) -@renderElementsFor(Collection, ViewMode.Grid) +@renderElementsFor(Collection, SetViewMode.Grid) export class CollectionGridElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts index 21d73ff0fa..4f69e3e2c7 100644 --- a/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts +++ b/src/app/shared/object-grid/community-grid-element/community-grid-element.component.ts @@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core'; import { Community } from '../../../core/shared/community.model'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; @Component({ selector: 'ds-community-grid-element', @@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model'; templateUrl: './community-grid-element.component.html' }) -@renderElementsFor(Community, ViewMode.Grid) +@renderElementsFor(Community, SetViewMode.Grid) export class CommunityGridElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts b/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts index 5a8d46f1f2..a4137b3c26 100644 --- a/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts +++ b/src/app/shared/object-grid/item-grid-element/item-grid-element.component.ts @@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; import { renderElementsFor} from '../../object-collection/shared/dso-element-decorator'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; @Component({ selector: 'ds-item-grid-element', @@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model'; templateUrl: './item-grid-element.component.html' }) -@renderElementsFor(Item, ViewMode.Grid) +@renderElementsFor(Item, SetViewMode.Grid) export class ItemGridElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts index 7f8a3bb9fd..61cc5dca1c 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.ts @@ -3,7 +3,7 @@ import { Component } from '@angular/core'; import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator'; import { SearchResultGridElementComponent } from '../search-result-grid-element.component'; import { Collection } from '../../../../core/shared/collection.model'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model'; @Component({ @@ -12,5 +12,5 @@ import { CollectionSearchResult } from '../../../object-collection/shared/collec templateUrl: 'collection-search-result-grid-element.component.html' }) -@renderElementsFor(CollectionSearchResult, ViewMode.Grid) +@renderElementsFor(CollectionSearchResult, SetViewMode.Grid) export class CollectionSearchResultGridElementComponent extends SearchResultGridElementComponent {} diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts index 7c34207f5e..d445e27ed6 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; import { Community } from '../../../../core/shared/community.model'; import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator'; import { SearchResultGridElementComponent } from '../search-result-grid-element.component'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model'; @Component({ @@ -11,7 +11,7 @@ import { CommunitySearchResult } from '../../../object-collection/shared/communi templateUrl: 'community-search-result-grid-element.component.html' }) -@renderElementsFor(CommunitySearchResult, ViewMode.Grid) +@renderElementsFor(CommunitySearchResult, SetViewMode.Grid) export class CommunitySearchResultGridElementComponent extends SearchResultGridElementComponent { } diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts index 518fc23a44..30c36b3af9 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.ts @@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element import { SearchResultGridElementComponent } from '../search-result-grid-element.component'; import { Item } from '../../../../core/shared/item.model'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { focusShadow } from '../../../../shared/animations/focus'; @Component({ @@ -14,5 +14,5 @@ import { focusShadow } from '../../../../shared/animations/focus'; animations: [focusShadow], }) -@renderElementsFor(ItemSearchResult, ViewMode.Grid) +@renderElementsFor(ItemSearchResult, SetViewMode.Grid) export class ItemSearchResultGridElementComponent extends SearchResultGridElementComponent {} diff --git a/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts b/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts index f807542e76..84f9357b2d 100644 --- a/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts +++ b/src/app/shared/object-grid/wrapper-grid-element/wrapper-grid-element.component.ts @@ -1,5 +1,5 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator'; import { ListableObject } from '../../object-collection/shared/listable-object.model'; @@ -26,6 +26,6 @@ export class WrapperGridElementComponent implements OnInit { getGridElement(): string { const f: GenericConstructor = this.object.constructor as GenericConstructor; - return rendersDSOType(f, ViewMode.Grid); + return rendersDSOType(f, SetViewMode.Grid); } } diff --git a/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts b/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts index f95c087d5c..3fe64f5bb9 100644 --- a/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts +++ b/src/app/shared/object-list/collection-list-element/collection-list-element.component.ts @@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core'; import { Collection } from '../../../core/shared/collection.model'; import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; @Component({ @@ -11,5 +11,5 @@ import { AbstractListableElementComponent } from '../../object-collection/shared templateUrl: './collection-list-element.component.html' }) -@renderElementsFor(Collection, ViewMode.List) +@renderElementsFor(Collection, SetViewMode.List) export class CollectionListElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-list/community-list-element/community-list-element.component.ts b/src/app/shared/object-list/community-list-element/community-list-element.component.ts index e92c080a31..5e254cb1ac 100644 --- a/src/app/shared/object-list/community-list-element/community-list-element.component.ts +++ b/src/app/shared/object-list/community-list-element/community-list-element.component.ts @@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core'; import { Community } from '../../../core/shared/community.model'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; @Component({ selector: 'ds-community-list-element', @@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model'; templateUrl: './community-list-element.component.html' }) -@renderElementsFor(Community, ViewMode.List) +@renderElementsFor(Community, SetViewMode.List) export class CommunityListElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.ts index e4efbd6b08..8dd09f71b9 100644 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.ts @@ -3,7 +3,7 @@ import { Component, Input, Inject } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; @Component({ selector: 'ds-item-list-element', @@ -11,5 +11,5 @@ import { ViewMode } from '../../../+search-page/search-options.model'; templateUrl: './item-list-element.component.html' }) -@renderElementsFor(Item, ViewMode.List) +@renderElementsFor(Item, SetViewMode.List) export class ItemListElementComponent extends AbstractListableElementComponent {} diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts index 746e700f05..2205155bbd 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.ts @@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element import { SearchResultListElementComponent } from '../search-result-list-element.component'; import { Collection } from '../../../../core/shared/collection.model'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model'; @Component({ @@ -13,5 +13,5 @@ import { CollectionSearchResult } from '../../../object-collection/shared/collec templateUrl: 'collection-search-result-list-element.component.html' }) -@renderElementsFor(CollectionSearchResult, ViewMode.List) +@renderElementsFor(CollectionSearchResult, SetViewMode.List) export class CollectionSearchResultListElementComponent extends SearchResultListElementComponent {} diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts index 2ca89fc9c5..3f7e08d11c 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.ts @@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element import { SearchResultListElementComponent } from '../search-result-list-element.component'; import { Community } from '../../../../core/shared/community.model'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model'; @Component({ @@ -13,7 +13,7 @@ import { CommunitySearchResult } from '../../../object-collection/shared/communi templateUrl: 'community-search-result-list-element.component.html' }) -@renderElementsFor(CommunitySearchResult, ViewMode.List) +@renderElementsFor(CommunitySearchResult, SetViewMode.List) export class CommunitySearchResultListElementComponent extends SearchResultListElementComponent { } diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts index b776abc214..37dcb907ef 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts @@ -4,7 +4,7 @@ import { renderElementsFor } from '../../../object-collection/shared/dso-element import { SearchResultListElementComponent } from '../search-result-list-element.component'; import { Item } from '../../../../core/shared/item.model'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; -import { ViewMode } from '../../../../+search-page/search-options.model'; +import { SetViewMode } from '../../../view-mode'; import { ListableObject } from '../../../object-collection/shared/listable-object.model'; import { focusBackground } from '../../../animations/focus'; @@ -16,6 +16,6 @@ import { focusBackground } from '../../../animations/focus'; }) -@renderElementsFor(ItemSearchResult, ViewMode.List) +@renderElementsFor(ItemSearchResult, SetViewMode.List) export class ItemSearchResultListElementComponent extends SearchResultListElementComponent { } diff --git a/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts b/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts index 217c1776c5..c8d3316983 100644 --- a/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts +++ b/src/app/shared/object-list/wrapper-list-element/wrapper-list-element.component.ts @@ -1,5 +1,5 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; -import { ViewMode } from '../../../+search-page/search-options.model'; +import { SetViewMode } from '../../view-mode'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator' import { ListableObject } from '../../object-collection/shared/listable-object.model'; @@ -24,6 +24,6 @@ export class WrapperListElementComponent implements OnInit { getListElement(): string { const f: GenericConstructor = this.object.constructor as GenericConstructor; - return rendersDSOType(f, ViewMode.List); + return rendersDSOType(f, SetViewMode.List); } } diff --git a/src/app/shared/testing/hal-endpoint-service-stub.ts b/src/app/shared/testing/hal-endpoint-service-stub.ts index e7dbe8bea1..3ebe2c1d25 100644 --- a/src/app/shared/testing/hal-endpoint-service-stub.ts +++ b/src/app/shared/testing/hal-endpoint-service-stub.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs/Observable'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; export class HALEndpointServiceStub { diff --git a/src/app/shared/testing/search-service-stub.ts b/src/app/shared/testing/search-service-stub.ts index 7ad0d871ce..999ab3794b 100644 --- a/src/app/shared/testing/search-service-stub.ts +++ b/src/app/shared/testing/search-service-stub.ts @@ -1,23 +1,23 @@ import { Observable } from 'rxjs/Observable'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; export class SearchServiceStub { - private _viewMode: ViewMode; + private _viewMode: SetViewMode; private subject?: BehaviorSubject = new BehaviorSubject(this.testViewMode); viewMode = this.subject.asObservable(); constructor(private searchLink: string = '/search') { - this.setViewMode(ViewMode.List); + this.setViewMode(SetViewMode.List); } - getViewMode(): Observable { + getViewMode(): Observable { return this.viewMode; } - setViewMode(viewMode: ViewMode) { + setViewMode(viewMode: SetViewMode) { this.testViewMode = viewMode; } @@ -25,11 +25,11 @@ export class SearchServiceStub { return null; } - get testViewMode(): ViewMode { + get testViewMode(): SetViewMode { return this._viewMode; } - set testViewMode(viewMode: ViewMode) { + set testViewMode(viewMode: SetViewMode) { this._viewMode = viewMode; this.subject.next(viewMode); } diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts index 1b4bf6da46..2fe405de3f 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.spec.ts @@ -8,7 +8,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { SearchService } from '../../+search-page/search-service/search.service'; import { ViewModeSwitchComponent } from './view-mode-switch.component'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { SearchServiceStub } from '../testing/search-service-stub'; @Component({ template: '' }) @@ -55,19 +55,19 @@ describe('ViewModeSwitchComponent', () => { }); it('should set list button as active when on list mode', fakeAsync(() => { - searchService.setViewMode(ViewMode.List); + searchService.setViewMode(SetViewMode.List); tick(); fixture.detectChanges(); - expect(comp.currentMode).toBe(ViewMode.List); + expect(comp.currentMode).toBe(SetViewMode.List); expect(listButton.classList).toContain('active'); expect(gridButton.classList).not.toContain('active'); })); it('should set grid button as active when on grid mode', fakeAsync(() => { - searchService.setViewMode(ViewMode.Grid); + searchService.setViewMode(SetViewMode.Grid); tick(); fixture.detectChanges(); - expect(comp.currentMode).toBe(ViewMode.Grid); + expect(comp.currentMode).toBe(SetViewMode.Grid); expect(listButton.classList).not.toContain('active'); expect(gridButton.classList).toContain('active'); })); diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.ts index f6e04816fb..11db82b3e9 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.ts @@ -1,6 +1,6 @@ import { Subscription } from 'rxjs/Subscription'; import { Component, OnInit, OnDestroy } from '@angular/core'; -import { ViewMode } from '../../+search-page/search-options.model'; +import { SetViewMode } from '../view-mode'; import { SearchService } from './../../+search-page/search-service/search.service'; /** @@ -12,20 +12,20 @@ import { SearchService } from './../../+search-page/search-service/search.servic templateUrl: './view-mode-switch.component.html' }) export class ViewModeSwitchComponent implements OnInit, OnDestroy { - currentMode: ViewMode = ViewMode.List; - viewModeEnum = ViewMode; + currentMode: SetViewMode = SetViewMode.List; + viewModeEnum = SetViewMode; private sub: Subscription; constructor(private searchService: SearchService) { } ngOnInit(): void { - this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => { + this.sub = this.searchService.getViewMode().subscribe((viewMode: SetViewMode) => { this.currentMode = viewMode; }); } - switchViewTo(viewMode: ViewMode) { + switchViewTo(viewMode: SetViewMode) { this.searchService.setViewMode(viewMode); } diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts new file mode 100644 index 0000000000..c12d051943 --- /dev/null +++ b/src/app/shared/view-mode.ts @@ -0,0 +1,11 @@ +export enum SetViewMode { + List, + Grid +} + +export enum ElementViewMode { + Full, + setElement +} + +export type ViewMode = SetViewMode | ElementViewMode; diff --git a/src/app/thumbnail/thumbnail.component.html b/src/app/thumbnail/thumbnail.component.html index 6221cbaba1..dd0d79d094 100644 --- a/src/app/thumbnail/thumbnail.component.html +++ b/src/app/thumbnail/thumbnail.component.html @@ -1,4 +1,4 @@
- +
diff --git a/src/app/thumbnail/thumbnail.component.scss b/src/app/thumbnail/thumbnail.component.scss index da97dd7a62..9388327700 100644 --- a/src/app/thumbnail/thumbnail.component.scss +++ b/src/app/thumbnail/thumbnail.component.scss @@ -1 +1,4 @@ @import '../../styles/variables.scss'; +img { + max-width: 100%; +} diff --git a/src/app/thumbnail/thumbnail.component.spec.ts b/src/app/thumbnail/thumbnail.component.spec.ts index fc145d2397..f2be55d52c 100644 --- a/src/app/thumbnail/thumbnail.component.spec.ts +++ b/src/app/thumbnail/thumbnail.component.spec.ts @@ -36,7 +36,7 @@ describe('ThumbnailComponent', () => { it('should display placeholder', () => { fixture.detectChanges(); const image: HTMLElement = de.query(By.css('img')).nativeElement; - expect(image.getAttribute('src')).toBe(comp.holderSource); + expect(image.getAttribute('src')).toBe(comp.defaultImage); }); }); diff --git a/src/app/thumbnail/thumbnail.component.ts b/src/app/thumbnail/thumbnail.component.ts index cac1909b2b..5e2b713b31 100644 --- a/src/app/thumbnail/thumbnail.component.ts +++ b/src/app/thumbnail/thumbnail.component.ts @@ -21,10 +21,10 @@ export class ThumbnailComponent { /** * The default 'holder.js' image */ - holderSource = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'; + @Input() defaultImage? = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2293%22%20height%3D%22120%22%20viewBox%3D%220%200%2093%20120%22%20preserveAspectRatio%3D%22none%22%3E%3C!--%0ASource%20URL%3A%20holder.js%2F93x120%3Ftext%3DNo%20Thumbnail%0ACreated%20with%20Holder.js%202.8.2.%0ALearn%20more%20at%20http%3A%2F%2Fholderjs.com%0A(c)%202012-2015%20Ivan%20Malopinsky%20-%20http%3A%2F%2Fimsky.co%0A--%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%3C!%5BCDATA%5B%23holder_1543e460b05%20text%20%7B%20fill%3A%23AAAAAA%3Bfont-weight%3Abold%3Bfont-family%3AArial%2C%20Helvetica%2C%20Open%20Sans%2C%20sans-serif%2C%20monospace%3Bfont-size%3A10pt%20%7D%20%5D%5D%3E%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_1543e460b05%22%3E%3Crect%20width%3D%2293%22%20height%3D%22120%22%20fill%3D%22%23EEEEEE%22%2F%3E%3Cg%3E%3Ctext%20x%3D%2235.6171875%22%20y%3D%2257%22%3ENo%3C%2Ftext%3E%3Ctext%20x%3D%2210.8125%22%20y%3D%2272%22%3EThumbnail%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E'; errorHandler(event) { - event.currentTarget.src = this.holderSource; + event.currentTarget.src = this.defaultImage; } } From 26098c6bd07bf816b5f6ad74809c269ea5afd003 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 29 May 2018 10:45:30 +0200 Subject: [PATCH 002/205] Throw an error when there are multiple components to render the same type/viewmode combination --- src/app/shared/entities/relationship-type-decorator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/shared/entities/relationship-type-decorator.ts b/src/app/shared/entities/relationship-type-decorator.ts index 8d510e53b9..ee91ad4970 100644 --- a/src/app/shared/entities/relationship-type-decorator.ts +++ b/src/app/shared/entities/relationship-type-decorator.ts @@ -1,4 +1,4 @@ -import { hasNoValue } from '../empty.util'; +import { hasNoValue, hasValue } from '../empty.util'; import { ElementViewMode } from '../view-mode'; export const DEFAULT_RELATIONSHIP_TYPE = 'Default'; @@ -9,6 +9,9 @@ export function rendersRelationshipType(type: string, viewMode: ElementViewMode) if (hasNoValue(map.get(viewMode))) { map.set(viewMode, new Map()); } + if (hasValue(map.get(viewMode).get(type))) { + throw new Error(`There can't be more than one component to render Items of type "${type}" in view mode "${viewMode}"`); + } map.get(viewMode).set(type, component); }; } From 7db32b2e243c64bc1fb28dd6458eb743bfbbd098 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 29 May 2018 11:55:56 +0200 Subject: [PATCH 003/205] refactoring --- resources/i18n/en.json | 2 +- src/app/+item-page/item-page.module.ts | 12 ++++++------ .../item/item-page-fields.component.html | 0 .../item/item-page-fields.component.scss | 0 .../item/item-page-fields.component.ts | 12 ++++++------ .../orgunit/orgunit-page-fields.component.html | 0 .../orgunit/orgunit-page-fields.component.scss | 0 .../orgunit/orgunit-page-fields.component.ts | 6 +++--- .../person/person-page-fields.component.html | 0 .../person/person-page-fields.component.scss | 0 .../person/person-page-fields.component.ts | 6 +++--- .../project/project-page-fields.component.html | 4 ++-- .../project/project-page-fields.component.scss | 0 .../project/project-page-fields.component.ts | 6 +++--- .../switcher/entity-type-switcher.component.html} | 0 .../switcher/entity-type-switcher.component.scss} | 0 .../switcher/entity-type-switcher.component.ts} | 12 ++++++------ src/app/+item-page/simple/item-page.component.html | 2 +- ...ip-type-decorator.ts => entity-type-decorator.ts} | 8 ++++---- 19 files changed, 35 insertions(+), 35 deletions(-) rename src/app/+item-page/simple/{relationship-types => entity-types}/item/item-page-fields.component.html (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/item/item-page-fields.component.scss (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/item/item-page-fields.component.ts (61%) rename src/app/+item-page/simple/{relationship-types => entity-types}/orgunit/orgunit-page-fields.component.html (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/orgunit/orgunit-page-fields.component.scss (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/orgunit/orgunit-page-fields.component.ts (65%) rename src/app/+item-page/simple/{relationship-types => entity-types}/person/person-page-fields.component.html (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/person/person-page-fields.component.scss (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/person/person-page-fields.component.ts (65%) rename src/app/+item-page/simple/{relationship-types => entity-types}/project/project-page-fields.component.html (93%) rename src/app/+item-page/simple/{relationship-types => entity-types}/project/project-page-fields.component.scss (100%) rename src/app/+item-page/simple/{relationship-types => entity-types}/project/project-page-fields.component.ts (65%) rename src/app/+item-page/simple/{relationship-types/switcher/relationship-type-switcher.component.html => entity-types/switcher/entity-type-switcher.component.html} (100%) rename src/app/+item-page/simple/{relationship-types/switcher/relationship-type-switcher.component.scss => entity-types/switcher/entity-type-switcher.component.scss} (100%) rename src/app/+item-page/simple/{relationship-types/switcher/relationship-type-switcher.component.ts => entity-types/switcher/entity-type-switcher.component.ts} (71%) rename src/app/shared/entities/{relationship-type-decorator.ts => entity-type-decorator.ts} (67%) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 06671e0809..78a2b1a408 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -65,7 +65,7 @@ "id": "ID", "expectedcompletion": "Expected Completion", "description": "Description", - "keywords": "Keywords" + "keyword": "Keywords" } }, "orgunit": { diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index d903baf8f7..fdf9d19674 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -19,11 +19,11 @@ import { FileSectionComponent } from './simple/field-components/file-section/fil import { CollectionsComponent } from './field-components/collections/collections.component'; import { FullItemPageComponent } from './full/full-item-page.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; -import { ItemPageFieldsComponent } from './simple/relationship-types/item/item-page-fields.component'; -import { OrgUnitPageFieldsComponent } from './simple/relationship-types/orgunit/orgunit-page-fields.component'; -import { PersonPageFieldsComponent } from './simple/relationship-types/person/person-page-fields.component'; -import { ProjectPageFieldsComponent } from './simple/relationship-types/project/project-page-fields.component'; -import { RelationshipTypeSwitcherComponent } from './simple/relationship-types/switcher/relationship-type-switcher.component'; +import { ItemPageFieldsComponent } from './simple/entity-types/item/item-page-fields.component'; +import { OrgUnitPageFieldsComponent } from './simple/entity-types/orgunit/orgunit-page-fields.component'; +import { PersonPageFieldsComponent } from './simple/entity-types/person/person-page-fields.component'; +import { ProjectPageFieldsComponent } from './simple/entity-types/project/project-page-fields.component'; +import { EntityTypeSwitcherComponent } from './simple/entity-types/switcher/entity-type-switcher.component'; @NgModule({ imports: [ @@ -46,7 +46,7 @@ import { RelationshipTypeSwitcherComponent } from './simple/relationship-types/s FileSectionComponent, CollectionsComponent, FullFileSectionComponent, - RelationshipTypeSwitcherComponent, + EntityTypeSwitcherComponent, ItemPageFieldsComponent, ProjectPageFieldsComponent, OrgUnitPageFieldsComponent, diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.html similarity index 100% rename from src/app/+item-page/simple/relationship-types/item/item-page-fields.component.html rename to src/app/+item-page/simple/entity-types/item/item-page-fields.component.html diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.scss similarity index 100% rename from src/app/+item-page/simple/relationship-types/item/item-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/item/item-page-fields.component.scss diff --git a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts similarity index 61% rename from src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts index 39333503cb..90504812d7 100644 --- a/src/app/+item-page/simple/relationship-types/item/item-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { - DEFAULT_RELATIONSHIP_TYPE, - rendersRelationshipType -} from '../../../../shared/entities/relationship-type-decorator'; + DEFAULT_ENTITY_TYPE, + rendersEntityType +} from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/relationship-type-switcher.component'; +import { ITEM } from '../switcher/entity-type-switcher.component'; -@rendersRelationshipType('Item', ElementViewMode.Full) -@rendersRelationshipType(DEFAULT_RELATIONSHIP_TYPE, ElementViewMode.Full) +@rendersEntityType('Item', ElementViewMode.Full) +@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full) @Component({ selector: 'ds-item-page-fields', styleUrls: ['./item-page-fields.component.scss'], diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html similarity index 100% rename from src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.html rename to src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.scss similarity index 100% rename from src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.scss diff --git a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts similarity index 65% rename from src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index d65b51ad7e..97a0d4f477 100644 --- a/src/app/+item-page/simple/relationship-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/relationship-type-switcher.component'; +import { ITEM } from '../switcher/entity-type-switcher.component'; -@rendersRelationshipType('OrgUnit', ElementViewMode.Full) +@rendersEntityType('OrgUnit', ElementViewMode.Full) @Component({ selector: 'ds-orgunit-page-fields', styleUrls: ['./orgunit-page-fields.component.scss'], diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html similarity index 100% rename from src/app/+item-page/simple/relationship-types/person/person-page-fields.component.html rename to src/app/+item-page/simple/entity-types/person/person-page-fields.component.html diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.scss similarity index 100% rename from src/app/+item-page/simple/relationship-types/person/person-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/person/person-page-fields.component.scss diff --git a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts similarity index 65% rename from src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index fc89cd8644..ec0577426c 100644 --- a/src/app/+item-page/simple/relationship-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/relationship-type-switcher.component'; +import { ITEM } from '../switcher/entity-type-switcher.component'; -@rendersRelationshipType('Person', ElementViewMode.Full) +@rendersEntityType('Person', ElementViewMode.Full) @Component({ selector: 'ds-person-page-fields', styleUrls: ['./person-page-fields.component.scss'], diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html similarity index 93% rename from src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html rename to src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index ccf342c867..65a7c7f270 100644 --- a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -25,8 +25,8 @@ [label]="'project.page.description'"> + [fields]="['project.identifier.keyword']" + [label]="'project.page.keyword'">
diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.scss similarity index 100% rename from src/app/+item-page/simple/relationship-types/project/project-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/project/project-page-fields.component.scss diff --git a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts similarity index 65% rename from src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index da9a4416b7..f6e93dcfa6 100644 --- a/src/app/+item-page/simple/relationship-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -1,10 +1,10 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { rendersRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/relationship-type-switcher.component'; +import { ITEM } from '../switcher/entity-type-switcher.component'; -@rendersRelationshipType('Project', ElementViewMode.Full) +@rendersEntityType('Project', ElementViewMode.Full) @Component({ selector: 'ds-project-page-fields', styleUrls: ['./project-page-fields.component.scss'], diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.html similarity index 100% rename from src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.html rename to src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.html diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss similarity index 100% rename from src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.scss rename to src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss diff --git a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts similarity index 71% rename from src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts rename to src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts index cb7293f514..d2e2374e8e 100644 --- a/src/app/+item-page/simple/relationship-types/switcher/relationship-type-switcher.component.ts +++ b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts @@ -1,7 +1,7 @@ import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; import { GenericConstructor } from '../../../../core/shared/generic-constructor'; import { Item } from '../../../../core/shared/item.model'; -import { getComponentByRelationshipType } from '../../../../shared/entities/relationship-type-decorator'; +import { getComponentByEntityType } from '../../../../shared/entities/entity-type-decorator'; import { rendersDSOType } from '../../../../shared/object-collection/shared/dso-element-decorator'; import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode'; @@ -9,11 +9,11 @@ import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode'; export const ITEM: InjectionToken = new InjectionToken('item'); @Component({ - selector: 'ds-relationship-type-switcher', - styleUrls: ['./relationship-type-switcher.component.scss'], - templateUrl: './relationship-type-switcher.component.html' + selector: 'ds-entity-type-switcher', + styleUrls: ['./entity-type-switcher.component.scss'], + templateUrl: './entity-type-switcher.component.html' }) -export class RelationshipTypeSwitcherComponent implements OnInit { +export class EntityTypeSwitcherComponent implements OnInit { @Input() item: Item; @Input() viewMode: ElementViewMode; objectInjector: Injector; @@ -31,6 +31,6 @@ export class RelationshipTypeSwitcherComponent implements OnInit { getComponent(): string { const type = this.item.findMetadata('relationship.type'); - return getComponentByRelationshipType(type, this.viewMode); + return getComponentByEntityType(type, this.viewMode); } } diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index a6dd4a47a1..7c75887f38 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/shared/entities/relationship-type-decorator.ts b/src/app/shared/entities/entity-type-decorator.ts similarity index 67% rename from src/app/shared/entities/relationship-type-decorator.ts rename to src/app/shared/entities/entity-type-decorator.ts index ee91ad4970..60bdab7412 100644 --- a/src/app/shared/entities/relationship-type-decorator.ts +++ b/src/app/shared/entities/entity-type-decorator.ts @@ -1,10 +1,10 @@ import { hasNoValue, hasValue } from '../empty.util'; import { ElementViewMode } from '../view-mode'; -export const DEFAULT_RELATIONSHIP_TYPE = 'Default'; +export const DEFAULT_ENTITY_TYPE = 'Default'; const map = new Map(); -export function rendersRelationshipType(type: string, viewMode: ElementViewMode) { +export function rendersEntityType(type: string, viewMode: ElementViewMode) { return function decorator(component: any) { if (hasNoValue(map.get(viewMode))) { map.set(viewMode, new Map()); @@ -16,10 +16,10 @@ export function rendersRelationshipType(type: string, viewMode: ElementViewMode) }; } -export function getComponentByRelationshipType(type: string, viewMode: ElementViewMode) { +export function getComponentByEntityType(type: string, viewMode: ElementViewMode) { let component = map.get(viewMode).get(type); if (hasNoValue(component)) { - component = map.get(viewMode).get(DEFAULT_RELATIONSHIP_TYPE); + component = map.get(viewMode).get(DEFAULT_ENTITY_TYPE); } return component; } From 5149c48eddae065978d8ff391dfb65e0e59137e3 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 29 May 2018 19:36:32 +0200 Subject: [PATCH 004/205] first version of relationships - too resource intensive --- resources/i18n/en.json | 1 + .../person/person-page-fields.component.html | 7 ++ .../person/person-page-fields.component.ts | 86 ++++++++++++++++++- .../entities/normalized-entity-type.model.ts | 20 +++++ .../normalized-relationship-type.model.ts | 43 ++++++++++ .../entities/normalized-relationship.model.ts | 30 +++++++ .../cache/models/normalized-item.model.ts | 4 + .../cache/models/normalized-object-factory.ts | 12 +++ .../core/shared/entities/entity-type.model.ts | 9 ++ .../entities/relationship-type.model.ts | 33 +++++++ .../shared/entities/relationship.model.ts | 23 +++++ src/app/core/shared/item.model.ts | 3 + src/app/core/shared/operators.ts | 2 +- src/app/core/shared/resource-type.ts | 5 +- 14 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 src/app/core/cache/models/entities/normalized-entity-type.model.ts create mode 100644 src/app/core/cache/models/entities/normalized-relationship-type.model.ts create mode 100644 src/app/core/cache/models/entities/normalized-relationship.model.ts create mode 100644 src/app/core/shared/entities/entity-type.model.ts create mode 100644 src/app/core/shared/entities/relationship-type.model.ts create mode 100644 src/app/core/shared/entities/relationship.model.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 78a2b1a408..a06f77463f 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -54,6 +54,7 @@ "orcid": "ORCID", "birthdate": "Birth Date", "staffid": "Staff ID", + "publications": "Publications", "link": { "full": "Show all metadata" } diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index 4763670b94..a19170b77d 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -24,6 +24,13 @@
+ + + + {{publ.name}} + + + diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index ec0577426c..6599ad580e 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -1,5 +1,17 @@ import { Component, Inject } from '@angular/core'; +import ensureArray from 'rollup/dist/typings/utils/ensureArray'; +import { Observable } from 'rxjs/Observable'; +import { filter, flatMap, map } from 'rxjs/operators'; +import { relationship } from '../../../../core/cache/builders/build-decorators'; +import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; +import { Relationship } from '../../../../core/shared/entities/relationship.model'; import { Item } from '../../../../core/shared/item.model'; +import { getRemoteDataPayload } from '../../../../core/shared/operators'; +import { ensureArrayHasValue, hasValue } from '../../../../shared/empty.util'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../switcher/entity-type-switcher.component'; @@ -11,8 +23,80 @@ import { ITEM } from '../switcher/entity-type-switcher.component'; templateUrl: './person-page-fields.component.html' }) export class PersonPageFieldsComponent { + publications$: Observable; + isProjectOfPersonRels$: Observable; + isOrgUnitOfPersonRels$: Observable; + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + const relsCurrentPage$ = item.relationships.pipe( + filter((rd: RemoteData>) => rd.hasSucceeded), + getRemoteDataPayload(), + map((pl: PaginatedList) => pl.page), + ensureArrayHasValue(), + ); + + const relTypesCurrentPage$ = relsCurrentPage$.pipe( + flatMap((rels: Relationship[]) => + Observable.combineLatest( + ...rels.map((rel: Relationship) => rel.relationshipType), + (...arr: Array>) => + arr.map((d: RemoteData) => d.payload) + ) + ) + ); + + const resolvedRelsAndTypes$ = Observable.combineLatest( + relsCurrentPage$, + relTypesCurrentPage$ + ); + + this.publications$ = resolvedRelsAndTypes$.pipe( + map(([relsCurrentPage, relTypesCurrentPage]) => + relsCurrentPage.filter((rel: Relationship, idx: number) => + hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isPublicationOfAuthor' || + relTypesCurrentPage[idx].rightLabel === 'isPublicationOfAuthor') + ) + ), + flatMap((rels: Relationship[]) => + Observable.combineLatest( + ...rels.map((rel: Relationship) => { + let queryId = rel.leftId; + if (rel.leftId === this.item.id) { + queryId = rel.rightId; + } + return this.ids.findById(queryId); + }), + (...arr: Array>) => + arr + .filter((d: RemoteData) => d.hasSucceeded) + .map((d: RemoteData) => d.payload) + ) + ) + ); + + //TODO status het lijkt te werken maar duurt minuten om te laden: too much recursion? + + this.isProjectOfPersonRels$ = resolvedRelsAndTypes$.pipe( + map(([relsCurrentPage, relTypesCurrentPage]) => + relsCurrentPage.filter((rel: Relationship, idx: number) => + hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isProjectOfPerson' || + relTypesCurrentPage[idx].rightLabel === 'isProjectOfPerson') + ) + ) + ); + + this.isOrgUnitOfPersonRels$ = resolvedRelsAndTypes$.pipe( + map(([relsCurrentPage, relTypesCurrentPage]) => + relsCurrentPage.filter((rel: Relationship, idx: number) => + hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isOrgUnitOfPerson' || + relTypesCurrentPage[idx].rightLabel === 'isOrgUnitOfPerson') + ) + ) + ); - constructor(@Inject(ITEM) public item: Item) { } } diff --git a/src/app/core/cache/models/entities/normalized-entity-type.model.ts b/src/app/core/cache/models/entities/normalized-entity-type.model.ts new file mode 100644 index 0000000000..d9939cef71 --- /dev/null +++ b/src/app/core/cache/models/entities/normalized-entity-type.model.ts @@ -0,0 +1,20 @@ +import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; +import { EntityType } from '../../../shared/entities/entity-type.model'; +import { ResourceType } from '../../../shared/resource-type'; +import { mapsTo } from '../../builders/build-decorators'; +import { IDToUUIDSerializer } from '../../it-to-uuid-serializer'; +import { NormalizedObject } from '../normalized-object.model'; + +@mapsTo(EntityType) +@inheritSerialization(NormalizedObject) +export class NormalizedEntityType extends NormalizedObject { + + @autoserialize + label: string; + + @autoserialize + id: string; + + @autoserializeAs(new IDToUUIDSerializer(ResourceType.EntityType), 'id') + uuid: string; +} diff --git a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts new file mode 100644 index 0000000000..262359cab9 --- /dev/null +++ b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts @@ -0,0 +1,43 @@ +import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; +import { RelationshipType } from '../../../shared/entities/relationship-type.model'; +import { ResourceType } from '../../../shared/resource-type'; +import { mapsTo, relationship } from '../../builders/build-decorators'; +import { IDToUUIDSerializer } from '../../it-to-uuid-serializer'; +import { NormalizedDSpaceObject } from '../normalized-dspace-object.model'; +import { NormalizedObject } from '../normalized-object.model'; + +@mapsTo(RelationshipType) +@inheritSerialization(NormalizedDSpaceObject) +export class NormalizedRelationshipType extends NormalizedObject { + @autoserialize + id: string; + + @autoserialize + leftLabel: string; + + @autoserialize + leftMaxCardinality: number; + + @autoserialize + leftMinCardinality: number; + + @autoserialize + rightLabel: string; + + @autoserialize + rightMaxCardinality: number; + + @autoserialize + rightMinCardinality: number; + + @autoserialize + @relationship(ResourceType.EntityType, false) + leftType: string; + + @autoserialize + @relationship(ResourceType.EntityType, false) + rightType: string; + + @autoserializeAs(new IDToUUIDSerializer(ResourceType.RelationshipType), 'id') + uuid: string; +} diff --git a/src/app/core/cache/models/entities/normalized-relationship.model.ts b/src/app/core/cache/models/entities/normalized-relationship.model.ts new file mode 100644 index 0000000000..48d33e5f01 --- /dev/null +++ b/src/app/core/cache/models/entities/normalized-relationship.model.ts @@ -0,0 +1,30 @@ +import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; +import { Relationship } from '../../../shared/entities/relationship.model'; +import { ResourceType } from '../../../shared/resource-type'; +import { mapsTo, relationship } from '../../builders/build-decorators'; +import { IDToUUIDSerializer } from '../../it-to-uuid-serializer'; +import { NormalizedObject } from '../normalized-object.model'; + +@mapsTo(Relationship) +@inheritSerialization(NormalizedObject) +export class NormalizedRelationship extends NormalizedObject { + + @autoserialize + id: string; + + @autoserialize + leftId: string; + + @autoserialize + place: number; + + @autoserialize + rightId: string; + + @autoserialize + @relationship(ResourceType.RelationshipType, false) + relationshipType: string; + + @autoserializeAs(new IDToUUIDSerializer(ResourceType.Relationship), 'id') + uuid: string; +} diff --git a/src/app/core/cache/models/normalized-item.model.ts b/src/app/core/cache/models/normalized-item.model.ts index d6e9165471..252e866be9 100644 --- a/src/app/core/cache/models/normalized-item.model.ts +++ b/src/app/core/cache/models/normalized-item.model.ts @@ -57,4 +57,8 @@ export class NormalizedItem extends NormalizedDSpaceObject { @relationship(ResourceType.Bitstream, true) bitstreams: string[]; + @autoserialize + @relationship(ResourceType.Relationship, true) + relationships: string[]; + } diff --git a/src/app/core/cache/models/normalized-object-factory.ts b/src/app/core/cache/models/normalized-object-factory.ts index df67a1f2ce..ae07e6c0dc 100644 --- a/src/app/core/cache/models/normalized-object-factory.ts +++ b/src/app/core/cache/models/normalized-object-factory.ts @@ -1,3 +1,6 @@ +import { NormalizedEntityType } from './entities/normalized-entity-type.model'; +import { NormalizedRelationshipType } from './entities/normalized-relationship-type.model'; +import { NormalizedRelationship } from './entities/normalized-relationship.model'; import { NormalizedBitstream } from './normalized-bitstream.model'; import { NormalizedBundle } from './normalized-bundle.model'; import { NormalizedItem } from './normalized-item.model'; @@ -33,6 +36,15 @@ export class NormalizedObjectFactory { case ResourceType.ResourcePolicy: { return NormalizedResourcePolicy } + case ResourceType.Relationship: { + return NormalizedRelationship + } + case ResourceType.RelationshipType: { + return NormalizedRelationshipType + } + case ResourceType.EntityType: { + return NormalizedEntityType + } default: { return undefined; } diff --git a/src/app/core/shared/entities/entity-type.model.ts b/src/app/core/shared/entities/entity-type.model.ts new file mode 100644 index 0000000000..93e60f3efc --- /dev/null +++ b/src/app/core/shared/entities/entity-type.model.ts @@ -0,0 +1,9 @@ +import { CacheableObject } from '../../cache/object-cache.reducer'; +import { ResourceType } from '../resource-type'; + +export class EntityType implements CacheableObject { + id: string; + self: string; + type: ResourceType; + uuid: string; +} diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/entities/relationship-type.model.ts new file mode 100644 index 0000000000..ff3abd2d1b --- /dev/null +++ b/src/app/core/shared/entities/relationship-type.model.ts @@ -0,0 +1,33 @@ +import { Observable } from 'rxjs/Observable'; +import { CacheableObject } from '../../cache/object-cache.reducer'; +import { RemoteData } from '../../data/remote-data'; +import { ResourceType } from '../resource-type'; +import { EntityType } from './entity-type.model'; + +export class RelationshipType implements CacheableObject { + self: string; + + type: ResourceType; + + label: string; + + id: string; + + uuid: string; + + leftLabel: string; + + leftMaxCardinality: number; + + leftMinCardinality: number; + + rightLabel: string; + + rightMaxCardinality: number; + + rightMinCardinality: number; + + leftType: Observable>; + + rightType: Observable>; +} diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts new file mode 100644 index 0000000000..a26d6f5179 --- /dev/null +++ b/src/app/core/shared/entities/relationship.model.ts @@ -0,0 +1,23 @@ +import { Observable } from 'rxjs/Observable'; +import { CacheableObject } from '../../cache/object-cache.reducer'; +import { RemoteData } from '../../data/remote-data'; +import { ResourceType } from '../resource-type'; +import { RelationshipType } from './relationship-type.model'; + +export class Relationship implements CacheableObject { + self: string; + + type: ResourceType; + + uuid: string; + + id: string; + + leftId: string; + + place: string; + + rightId: string; + + relationshipType: Observable>; +} diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index cc84694e84..e8695c9e09 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -6,6 +6,7 @@ import { RemoteData } from '../data/remote-data'; import { Bitstream } from './bitstream.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { PaginatedList } from '../data/paginated-list'; +import { Relationship } from './entities/relationship.model'; export class Item extends DSpaceObject { @@ -50,6 +51,8 @@ export class Item extends DSpaceObject { bitstreams: Observable>>; + relationships: Observable>>; + /** * Retrieves the thumbnail of this item * @returns {Observable} the primaryBitstream of the 'THUMBNAIL' bundle diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index c0b9be3fbf..17d5887443 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -1,6 +1,6 @@ import { Observable } from 'rxjs/Observable'; import { filter, flatMap, map, tap } from 'rxjs/operators'; -import { hasValueOperator } from '../../shared/empty.util'; +import { hasValue, hasValueOperator } from '../../shared/empty.util'; import { DSOSuccessResponse } from '../cache/response-cache.models'; import { ResponseCacheEntry } from '../cache/response-cache.reducer'; import { ResponseCacheService } from '../cache/response-cache.service'; diff --git a/src/app/core/shared/resource-type.ts b/src/app/core/shared/resource-type.ts index c7e41c5744..242e277187 100644 --- a/src/app/core/shared/resource-type.ts +++ b/src/app/core/shared/resource-type.ts @@ -6,5 +6,8 @@ export enum ResourceType { Item = 'item', Collection = 'collection', Community = 'community', - ResourcePolicy = 'resourcePolicy' + ResourcePolicy = 'resourcePolicy', + Relationship = 'relationship', + RelationshipType = 'relationshiptype', + EntityType = 'entitytype', } From 82977ce5cf9d71695309c45775c83057d287c5ef Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 30 May 2018 10:27:59 +0200 Subject: [PATCH 005/205] optimized and refactored --- .../person/person-page-fields.component.ts | 122 ++++++++++-------- 1 file changed, 70 insertions(+), 52 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index 6599ad580e..7e93f7a007 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -1,9 +1,6 @@ -import { Component, Inject } from '@angular/core'; -import ensureArray from 'rollup/dist/typings/utils/ensureArray'; +import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { filter, flatMap, map } from 'rxjs/operators'; -import { relationship } from '../../../../core/cache/builders/build-decorators'; -import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; +import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; @@ -11,41 +8,92 @@ import { RelationshipType } from '../../../../core/shared/entities/relationship- import { Relationship } from '../../../../core/shared/entities/relationship.model'; import { Item } from '../../../../core/shared/item.model'; import { getRemoteDataPayload } from '../../../../core/shared/operators'; -import { ensureArrayHasValue, hasValue } from '../../../../shared/empty.util'; +import { hasValue } from '../../../../shared/empty.util'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../switcher/entity-type-switcher.component'; +const compareArraysUsing = (mapFn: (t: T) => any) => + (a: T[], b: T[]): boolean => { + if (!Array.isArray(a) || ! Array.isArray(b)) { + return false + } + + const aIds = a.map(mapFn); + const bIds = b.map(mapFn); + + return aIds.length === bIds.length && + aIds.every((e) => bIds.includes(e)) && + bIds.every((e) => aIds.includes(e)); + }; + +const filterRelationsByTypeLabel = (label: string) => + (source: Observable<[Relationship[], RelationshipType[]]>): Observable => + source.pipe( + map(([relsCurrentPage, relTypesCurrentPage]) => + relsCurrentPage.filter((rel: Relationship, idx: number) => + hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === label || + relTypesCurrentPage[idx].rightLabel === label) + ) + ), + distinctUntilChanged(compareArraysUsing((e: Relationship) => hasValue(e) ? e.id : undefined)) + ); + +const relationsToItems = (thisId: string, ids: ItemDataService) => + (source: Observable): Observable => + source.pipe( + flatMap((rels: Relationship[]) => + Observable.zip( + ...rels.map((rel: Relationship) => { + let queryId = rel.leftId; + if (rel.leftId === thisId) { + queryId = rel.rightId; + } + return ids.findById(queryId); + }) + ) + ), + map((arr: Array>) => + arr + .filter((d: RemoteData) => d.hasSucceeded) + .map((d: RemoteData) => d.payload)), + distinctUntilChanged(compareArraysUsing((rdi: Item) => hasValue(rdi) ? rdi.uuid : undefined)), + ); + + @rendersEntityType('Person', ElementViewMode.Full) @Component({ selector: 'ds-person-page-fields', styleUrls: ['./person-page-fields.component.scss'], templateUrl: './person-page-fields.component.html' }) -export class PersonPageFieldsComponent { +export class PersonPageFieldsComponent implements OnInit { publications$: Observable; - isProjectOfPersonRels$: Observable; - isOrgUnitOfPersonRels$: Observable; + projects$: Observable; + orgUnits$: Observable; constructor( @Inject(ITEM) public item: Item, private ids: ItemDataService - ) { - const relsCurrentPage$ = item.relationships.pipe( + ) {} + + ngOnInit(): void { + const relsCurrentPage$ = this.item.relationships.pipe( filter((rd: RemoteData>) => rd.hasSucceeded), getRemoteDataPayload(), map((pl: PaginatedList) => pl.page), - ensureArrayHasValue(), + distinctUntilChanged(compareArraysUsing((e: Relationship) => hasValue(e) ? e.id : undefined)) ); const relTypesCurrentPage$ = relsCurrentPage$.pipe( flatMap((rels: Relationship[]) => - Observable.combineLatest( + Observable.zip( ...rels.map((rel: Relationship) => rel.relationshipType), (...arr: Array>) => arr.map((d: RemoteData) => d.payload) ) - ) + ), + distinctUntilChanged(compareArraysUsing((e: RelationshipType) => hasValue(e) ? e.id : undefined)) ); const resolvedRelsAndTypes$ = Observable.combineLatest( @@ -54,49 +102,19 @@ export class PersonPageFieldsComponent { ); this.publications$ = resolvedRelsAndTypes$.pipe( - map(([relsCurrentPage, relTypesCurrentPage]) => - relsCurrentPage.filter((rel: Relationship, idx: number) => - hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isPublicationOfAuthor' || - relTypesCurrentPage[idx].rightLabel === 'isPublicationOfAuthor') - ) - ), - flatMap((rels: Relationship[]) => - Observable.combineLatest( - ...rels.map((rel: Relationship) => { - let queryId = rel.leftId; - if (rel.leftId === this.item.id) { - queryId = rel.rightId; - } - return this.ids.findById(queryId); - }), - (...arr: Array>) => - arr - .filter((d: RemoteData) => d.hasSucceeded) - .map((d: RemoteData) => d.payload) - ) - ) + filterRelationsByTypeLabel('isPublicationOfAuthor'), + relationsToItems(this.item.id, this.ids) ); - //TODO status het lijkt te werken maar duurt minuten om te laden: too much recursion? - - this.isProjectOfPersonRels$ = resolvedRelsAndTypes$.pipe( - map(([relsCurrentPage, relTypesCurrentPage]) => - relsCurrentPage.filter((rel: Relationship, idx: number) => - hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isProjectOfPerson' || - relTypesCurrentPage[idx].rightLabel === 'isProjectOfPerson') - ) - ) + this.projects$ = resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isProjectOfPerson'), + relationsToItems(this.item.id, this.ids) ); - this.isOrgUnitOfPersonRels$ = resolvedRelsAndTypes$.pipe( - map(([relsCurrentPage, relTypesCurrentPage]) => - relsCurrentPage.filter((rel: Relationship, idx: number) => - hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === 'isOrgUnitOfPerson' || - relTypesCurrentPage[idx].rightLabel === 'isOrgUnitOfPerson') - ) - ) + this.orgUnits$ = resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isOrgUnitOfPerson'), + relationsToItems(this.item.id, this.ids) ); - } } From f1b35577b2da9deeaa885d9536a5dac01f34f5f8 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 30 May 2018 10:57:33 +0200 Subject: [PATCH 006/205] refactor --- .../person/person-page-fields.component.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index 7e93f7a007..058ca8a7cc 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -27,6 +27,9 @@ const compareArraysUsing = (mapFn: (t: T) => any) => bIds.every((e) => aIds.includes(e)); }; +const compareArraysUsingIds = () => + compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); + const filterRelationsByTypeLabel = (label: string) => (source: Observable<[Relationship[], RelationshipType[]]>): Observable => source.pipe( @@ -36,7 +39,7 @@ const filterRelationsByTypeLabel = (label: string) => relTypesCurrentPage[idx].rightLabel === label) ) ), - distinctUntilChanged(compareArraysUsing((e: Relationship) => hasValue(e) ? e.id : undefined)) + distinctUntilChanged(compareArraysUsingIds()) ); const relationsToItems = (thisId: string, ids: ItemDataService) => @@ -57,7 +60,7 @@ const relationsToItems = (thisId: string, ids: ItemDataService) => arr .filter((d: RemoteData) => d.hasSucceeded) .map((d: RemoteData) => d.payload)), - distinctUntilChanged(compareArraysUsing((rdi: Item) => hasValue(rdi) ? rdi.uuid : undefined)), + distinctUntilChanged(compareArraysUsingIds()), ); @@ -82,7 +85,7 @@ export class PersonPageFieldsComponent implements OnInit { filter((rd: RemoteData>) => rd.hasSucceeded), getRemoteDataPayload(), map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsing((e: Relationship) => hasValue(e) ? e.id : undefined)) + distinctUntilChanged(compareArraysUsingIds()) ); const relTypesCurrentPage$ = relsCurrentPage$.pipe( @@ -93,7 +96,7 @@ export class PersonPageFieldsComponent implements OnInit { arr.map((d: RemoteData) => d.payload) ) ), - distinctUntilChanged(compareArraysUsing((e: RelationshipType) => hasValue(e) ? e.id : undefined)) + distinctUntilChanged(compareArraysUsingIds()) ); const resolvedRelsAndTypes$ = Observable.combineLatest( From e5fc17da76689d2690493dfd6baab6e98ba19c91 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 30 May 2018 15:22:43 +0200 Subject: [PATCH 007/205] w2p 51849 - display entities as list results --- src/app/+item-page/item-page.module.ts | 2 - .../item/item-page-fields.component.ts | 2 +- .../orgunit/orgunit-page-fields.component.ts | 2 +- .../person/person-page-fields.component.ts | 3 +- .../project/project-page-fields.component.ts | 2 +- .../entity-type-switcher.component.scss | 1 - .../entity-type-switcher.component.ts | 36 --------- .../simple/item-page.component.html | 2 +- src/app/+search-page/search-result.model.ts | 1 + .../entity-type-switcher.component.html | 0 .../entity-type-switcher.component.scss} | 0 .../entity-type-switcher.component.ts | 44 +++++++++++ .../entity-list-element.component.html | 1 + .../entity-list-element.component.scss | 1 + ... => entity-list-element.component.spec.ts} | 14 ++-- ...nt.ts => entity-list-element.component.ts} | 15 ++-- .../entity-search-result-component.ts | 74 +++++++++++++++++++ .../item/item-list-element.component.html | 24 ++++++ .../item/item-list-element.component.scss | 1 + .../item/item-list-element.component.ts | 15 ++++ .../orgunit-list-element.component.html | 16 ++++ .../orgunit-list-element.component.scss | 1 + .../orgunit/orgunit-list-element.component.ts | 14 ++++ .../person/person-list-element.component.html | 16 ++++ .../person/person-list-element.component.scss | 1 + .../person/person-list-element.component.ts | 14 ++++ .../project-list-element.component.html | 16 ++++ .../project-list-element.component.scss | 1 + .../project/project-list-element.component.ts | 14 ++++ .../item-list-element.component.html | 18 ----- ...-search-result-list-element.component.html | 25 +------ ...em-search-result-list-element.component.ts | 11 +-- src/app/shared/shared.module.ts | 16 +++- src/app/shared/view-mode.ts | 2 +- 34 files changed, 297 insertions(+), 108 deletions(-) delete mode 100644 src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss delete mode 100644 src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts rename src/app/{+item-page/simple/entity-types => shared/entities}/switcher/entity-type-switcher.component.html (100%) rename src/app/shared/{object-list/item-list-element/item-list-element.component.scss => entities/switcher/entity-type-switcher.component.scss} (100%) create mode 100644 src/app/shared/entities/switcher/entity-type-switcher.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-list-element.component.scss rename src/app/shared/object-list/item-list-element/{item-list-element.component.spec.ts => entity-list-element.component.spec.ts} (87%) rename src/app/shared/object-list/item-list-element/{item-list-element.component.ts => entity-list-element.component.ts} (51%) create mode 100644 src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/item-list-element.component.html diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index fdf9d19674..62253a3183 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -23,7 +23,6 @@ import { ItemPageFieldsComponent } from './simple/entity-types/item/item-page-fi import { OrgUnitPageFieldsComponent } from './simple/entity-types/orgunit/orgunit-page-fields.component'; import { PersonPageFieldsComponent } from './simple/entity-types/person/person-page-fields.component'; import { ProjectPageFieldsComponent } from './simple/entity-types/project/project-page-fields.component'; -import { EntityTypeSwitcherComponent } from './simple/entity-types/switcher/entity-type-switcher.component'; @NgModule({ imports: [ @@ -46,7 +45,6 @@ import { EntityTypeSwitcherComponent } from './simple/entity-types/switcher/enti FileSectionComponent, CollectionsComponent, FullFileSectionComponent, - EntityTypeSwitcherComponent, ItemPageFieldsComponent, ProjectPageFieldsComponent, OrgUnitPageFieldsComponent, diff --git a/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts index 90504812d7..4a5469c3c2 100644 --- a/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts @@ -5,7 +5,7 @@ import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; @rendersEntityType('Item', ElementViewMode.Full) @rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full) diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index 97a0d4f477..74e75e1dc9 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; @rendersEntityType('OrgUnit', ElementViewMode.Full) @Component({ diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index 058ca8a7cc..dce8fe5dbe 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -11,7 +11,7 @@ import { getRemoteDataPayload } from '../../../../core/shared/operators'; import { hasValue } from '../../../../shared/empty.util'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; const compareArraysUsing = (mapFn: (t: T) => any) => (a: T[], b: T[]): boolean => { @@ -63,7 +63,6 @@ const relationsToItems = (thisId: string, ids: ItemDataService) => distinctUntilChanged(compareArraysUsingIds()), ); - @rendersEntityType('Person', ElementViewMode.Full) @Component({ selector: 'ds-person-page-fields', diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index f6e93dcfa6..d648247a58 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -2,7 +2,7 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; @rendersEntityType('Project', ElementViewMode.Full) @Component({ diff --git a/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss deleted file mode 100644 index 3575cae797..0000000000 --- a/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.scss +++ /dev/null @@ -1 +0,0 @@ -@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts b/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts deleted file mode 100644 index d2e2374e8e..0000000000 --- a/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; -import { GenericConstructor } from '../../../../core/shared/generic-constructor'; -import { Item } from '../../../../core/shared/item.model'; -import { getComponentByEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { rendersDSOType } from '../../../../shared/object-collection/shared/dso-element-decorator'; -import { ListableObject } from '../../../../shared/object-collection/shared/listable-object.model'; -import { ElementViewMode, SetViewMode } from '../../../../shared/view-mode'; - -export const ITEM: InjectionToken = new InjectionToken('item'); - -@Component({ - selector: 'ds-entity-type-switcher', - styleUrls: ['./entity-type-switcher.component.scss'], - templateUrl: './entity-type-switcher.component.html' -}) -export class EntityTypeSwitcherComponent implements OnInit { - @Input() item: Item; - @Input() viewMode: ElementViewMode; - objectInjector: Injector; - - constructor(private injector: Injector) { - } - - ngOnInit(): void { - this.objectInjector = Injector.create({ - providers: [{ provide: ITEM, useFactory: () => this.item, deps:[] }], - parent: this.injector - }); - - } - - getComponent(): string { - const type = this.item.findMetadata('relationship.type'); - return getComponentByEntityType(type, this.viewMode); - } -} diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 7c75887f38..17d271f368 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/+search-page/search-result.model.ts b/src/app/+search-page/search-result.model.ts index cc2bd8cd58..2298f453e1 100644 --- a/src/app/+search-page/search-result.model.ts +++ b/src/app/+search-page/search-result.model.ts @@ -1,5 +1,6 @@ import { DSpaceObject } from '../core/shared/dspace-object.model'; import { Metadatum } from '../core/shared/metadatum.model'; +import { hasNoValue, isEmpty } from '../shared/empty.util'; import { ListableObject } from '../shared/object-collection/shared/listable-object.model'; export class SearchResult implements ListableObject { diff --git a/src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.html b/src/app/shared/entities/switcher/entity-type-switcher.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/switcher/entity-type-switcher.component.html rename to src/app/shared/entities/switcher/entity-type-switcher.component.html diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.scss b/src/app/shared/entities/switcher/entity-type-switcher.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/item-list-element.component.scss rename to src/app/shared/entities/switcher/entity-type-switcher.component.scss diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.ts b/src/app/shared/entities/switcher/entity-type-switcher.component.ts new file mode 100644 index 0000000000..a981f2665c --- /dev/null +++ b/src/app/shared/entities/switcher/entity-type-switcher.component.ts @@ -0,0 +1,44 @@ +import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; +import { SearchResult } from '../../../+search-page/search-result.model'; +import { Item } from '../../../core/shared/item.model'; +import { hasValue } from '../../empty.util'; +import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model'; +import { getComponentByEntityType } from '../entity-type-decorator'; +import { ElementViewMode } from '../../view-mode'; + +export const ITEM: InjectionToken = new InjectionToken('item'); + +@Component({ + selector: 'ds-entity-type-switcher', + styleUrls: ['./entity-type-switcher.component.scss'], + templateUrl: './entity-type-switcher.component.html' +}) +export class EntityTypeSwitcherComponent implements OnInit { + @Input() object: Item | SearchResult; + @Input() viewMode: ElementViewMode; + objectInjector: Injector; + + constructor(private injector: Injector) { + } + + ngOnInit(): void { + this.objectInjector = Injector.create({ + providers: [{ provide: ITEM, useFactory: () => this.object, deps:[] }], + parent: this.injector + }); + + } + + getComponent(): string { + let item: Item; + if (hasValue((this.object as any).dspaceObject)) { + const searchResult = this.object as ItemSearchResult; + item = searchResult.dspaceObject; + } else { + item = this.object as Item; + } + + const type = item.findMetadata('relationship.type'); + return getComponentByEntityType(type, this.viewMode); + } +} diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-list-element.component.html new file mode 100644 index 0000000000..777f131a23 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-list-element.component.scss new file mode 100644 index 0000000000..45a533cd01 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.scss @@ -0,0 +1 @@ +@import '../../../../styles/variables'; diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts similarity index 87% rename from src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts index fc40527693..8ff375c1ea 100644 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts @@ -1,4 +1,4 @@ -import { ItemListElementComponent } from './item-list-element.component'; +import { EntityListElementComponent } from './entity-list-element.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; @@ -6,8 +6,8 @@ import { TruncatePipe } from '../../utils/truncate.pipe'; import { Item } from '../../../core/shared/item.model'; import { Observable } from 'rxjs/Observable'; -let itemListElementComponent: ItemListElementComponent; -let fixture: ComponentFixture; +let itemListElementComponent: EntityListElementComponent; +let fixture: ComponentFixture; const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), { bitstreams: Observable.of({}), @@ -38,22 +38,22 @@ const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), { }] }); -describe('ItemListElementComponent', () => { +describe('EntityListElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ ItemListElementComponent , TruncatePipe], + declarations: [ EntityListElementComponent , TruncatePipe], providers: [ { provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}} ], schemas: [ NO_ERRORS_SCHEMA ] - }).overrideComponent(ItemListElementComponent, { + }).overrideComponent(EntityListElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(ItemListElementComponent); + fixture = TestBed.createComponent(EntityListElementComponent); itemListElementComponent = fixture.componentInstance; })); diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.ts similarity index 51% rename from src/app/shared/object-list/item-list-element/item-list-element.component.ts rename to src/app/shared/object-list/item-list-element/entity-list-element.component.ts index 8dd09f71b9..0a284708a9 100644 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.ts @@ -1,15 +1,18 @@ -import { Component, Input, Inject } from '@angular/core'; +import { Component } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; -import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; +import * as viewMode from '../../../shared/view-mode'; import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator'; +import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { SetViewMode } from '../../view-mode'; @Component({ - selector: 'ds-item-list-element', - styleUrls: ['./item-list-element.component.scss'], - templateUrl: './item-list-element.component.html' + selector: 'ds-entity-list-element', + styleUrls: ['./entity-list-element.component.scss'], + templateUrl: './entity-list-element.component.html' }) @renderElementsFor(Item, SetViewMode.List) -export class ItemListElementComponent extends AbstractListableElementComponent {} +export class EntityListElementComponent extends AbstractListableElementComponent { + ElementViewMode = viewMode.ElementViewMode; +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts new file mode 100644 index 0000000000..bcf2e45a8c --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts @@ -0,0 +1,74 @@ +import { Component, Inject } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { Metadatum } from '../../../../core/shared/metadatum.model'; +import { hasNoValue, hasValue, isEmpty } from '../../../empty.util'; +import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; +import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; + +// TODO lot of overlap with SearchResultListElementComponent => refactor! +@Component({ + selector: 'ds-entity-search-result', + template: '' +}) +export class EntitySearchResultComponent { + item: Item; + searchResult: ItemSearchResult; + + constructor( + private truncatableService: TruncatableService, + @Inject(ITEM) public object: Item | ItemSearchResult, + ) { + + if (hasValue((this.object as any).dspaceObject)) { + this.searchResult = this.object as ItemSearchResult; + this.item = this.searchResult.dspaceObject; + } else { + this.searchResult = { + dspaceObject: this.object as Item, + hitHighlights: [] + }; + this.item = this.object as Item; + } + } + + getValues(keys: string[]): string[] { + const results: string[] = new Array(); + this.searchResult.hitHighlights.forEach( + (md: Metadatum) => { + if (keys.indexOf(md.key) > -1) { + results.push(md.value); + } + } + ); + if (isEmpty(results)) { + this.item.filterMetadata(keys).forEach( + (md: Metadatum) => { + results.push(md.value); + } + ); + } + return results; + } + + getFirstValue(key: string): string { + let result: string; + this.searchResult.hitHighlights.some( + (md: Metadatum) => { + if (key === md.key) { + result = md.value; + return true; + } + } + ); + if (hasNoValue(result)) { + result = this.item.findMetadata(key); + } + return result; + } + + isCollapsed(): Observable { + return this.truncatableService.isCollapsed(this.item.id); + } +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.html new file mode 100644 index 0000000000..b5c125afaf --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.html @@ -0,0 +1,24 @@ + + + + + (, ) + + + + + + + +
+ + +
+
diff --git a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss new file mode 100644 index 0000000000..5ab410dcb0 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss @@ -0,0 +1 @@ +@import '../../../../../../styles/variables'; diff --git a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts new file mode 100644 index 0000000000..db68ecec7b --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { DEFAULT_ENTITY_TYPE, rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('Item', ElementViewMode.SetElement) +@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.SetElement) +@Component({ + selector: 'ds-item-list-element', + styleUrls: ['./item-list-element.component.scss'], + templateUrl: './item-list-element.component.html' +}) + +export class ItemListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html new file mode 100644 index 0000000000..901d521033 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss new file mode 100644 index 0000000000..5ab410dcb0 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss @@ -0,0 +1 @@ +@import '../../../../../../styles/variables'; diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts new file mode 100644 index 0000000000..9ae7489e97 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('OrgUnit', ElementViewMode.SetElement) +@Component({ + selector: 'ds-orgunit-list-element', + styleUrls: ['./orgunit-list-element.component.scss'], + templateUrl: './orgunit-list-element.component.html' +}) + +export class OrgUnitListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html new file mode 100644 index 0000000000..81e9300bb3 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss new file mode 100644 index 0000000000..5ab410dcb0 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss @@ -0,0 +1 @@ +@import '../../../../../../styles/variables'; diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts new file mode 100644 index 0000000000..b1156b1372 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('Person', ElementViewMode.SetElement) +@Component({ + selector: 'ds-person-list-element', + styleUrls: ['./person-list-element.component.scss'], + templateUrl: './person-list-element.component.html' +}) + +export class PersonListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html new file mode 100644 index 0000000000..7eafd91317 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss new file mode 100644 index 0000000000..5ab410dcb0 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss @@ -0,0 +1 @@ +@import '../../../../../../styles/variables'; diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts new file mode 100644 index 0000000000..f5275c47e8 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('Project', ElementViewMode.SetElement) +@Component({ + selector: 'ds-project-list-element', + styleUrls: ['./project-list-element.component.scss'], + templateUrl: './project-list-element.component.html' +}) + +export class ProjectListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.html b/src/app/shared/object-list/item-list-element/item-list-element.component.html deleted file mode 100644 index b4259c25c2..0000000000 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.html +++ /dev/null @@ -1,18 +0,0 @@ - - {{object.findMetadata("dc.title")}} - -
- - - {{authorMd.value}} - ; - - - ({{object.findMetadata("dc.publisher")}}, {{object.findMetadata("dc.date.issued")}}) - -
- {{object.findMetadata("dc.description.abstract") | dsTruncate:[200] }} -
-
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html index b8f3197a7c..777f131a23 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html @@ -1,24 +1 @@ - - - - - (, ) - - - - - - - -
- - -
-
\ No newline at end of file + diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts index 37dcb907ef..b9ac52ca40 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts @@ -1,12 +1,12 @@ -import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; +import { Item } from '../../../../core/shared/item.model'; +import { focusBackground } from '../../../animations/focus'; import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator'; -import { SearchResultListElementComponent } from '../search-result-list-element.component'; -import { Item } from '../../../../core/shared/item.model'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import * as viewMode from '../../../../shared/view-mode'; import { SetViewMode } from '../../../view-mode'; -import { ListableObject } from '../../../object-collection/shared/listable-object.model'; -import { focusBackground } from '../../../animations/focus'; +import { SearchResultListElementComponent } from '../search-result-list-element.component'; @Component({ selector: 'ds-item-search-result-list-element', @@ -18,4 +18,5 @@ import { focusBackground } from '../../../animations/focus'; @renderElementsFor(ItemSearchResult, SetViewMode.List) export class ItemSearchResultListElementComponent extends SearchResultListElementComponent { + ElementViewMode = viewMode.ElementViewMode; } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 79ddd8680f..90d498eedd 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -8,6 +8,12 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; import { NgxPaginationModule } from 'ngx-pagination'; +import { EntityTypeSwitcherComponent } from './entities/switcher/entity-type-switcher.component'; +import { EntitySearchResultComponent } from './object-list/item-list-element/entity-types/entity-search-result-component'; +import { ItemListElementComponent } from './object-list/item-list-element/entity-types/item/item-list-element.component'; +import { OrgUnitListElementComponent } from './object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component'; +import { PersonListElementComponent } from './object-list/item-list-element/entity-types/person/person-list-element.component'; +import { ProjectListElementComponent } from './object-list/item-list-element/entity-types/project/project-list-element.component'; import { EnumKeysPipe } from './utils/enum-keys-pipe'; import { FileSizePipe } from './utils/file-size-pipe'; @@ -15,7 +21,7 @@ import { SafeUrlPipe } from './utils/safe-url-pipe'; import { CollectionListElementComponent } from './object-list/collection-list-element/collection-list-element.component'; import { CommunityListElementComponent } from './object-list/community-list-element/community-list-element.component'; -import { ItemListElementComponent } from './object-list/item-list-element/item-list-element.component'; +import { EntityListElementComponent } from './object-list/item-list-element/entity-list-element.component'; import { SearchResultListElementComponent } from './object-list/search-result-list-element/search-result-list-element.component'; import { WrapperListElementComponent } from './object-list/wrapper-list-element/wrapper-list-element.component'; import { ObjectListComponent } from './object-list/object-list.component'; @@ -89,11 +95,13 @@ const COMPONENTS = [ ViewModeSwitchComponent, TruncatableComponent, TruncatablePartComponent, + EntitySearchResultComponent, + EntityTypeSwitcherComponent ]; const ENTRY_COMPONENTS = [ // put shared entry components (components that are created dynamically) here - ItemListElementComponent, + EntityListElementComponent, CollectionListElementComponent, CommunityListElementComponent, SearchResultListElementComponent, @@ -101,6 +109,10 @@ const ENTRY_COMPONENTS = [ CollectionGridElementComponent, CommunityGridElementComponent, SearchResultGridElementComponent, + ItemListElementComponent, + PersonListElementComponent, + OrgUnitListElementComponent, + ProjectListElementComponent ]; const PROVIDERS = [ diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts index c12d051943..e78da4fa83 100644 --- a/src/app/shared/view-mode.ts +++ b/src/app/shared/view-mode.ts @@ -5,7 +5,7 @@ export enum SetViewMode { export enum ElementViewMode { Full, - setElement + SetElement } export type ViewMode = SetViewMode | ElementViewMode; From 8423261cc7a98a4fdb2004d29263d4ca4f045b88 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 30 May 2018 17:43:24 +0200 Subject: [PATCH 008/205] w2p 51851 - finished relation display on entity pages --- resources/i18n/en.json | 10 +- .../full/full-item-page.component.html | 2 +- src/app/+item-page/item-page.module.ts | 10 +- .../item/item-page-fields.component.ts | 23 ---- .../orgunit-page-fields.component.html | 12 +++ .../orgunit/orgunit-page-fields.component.ts | 39 ++++++- .../person/person-page-fields.component.html | 19 ++-- .../person/person-page-fields.component.ts | 102 +++--------------- .../project-page-fields.component.html | 12 +++ .../project/project-page-fields.component.ts | 37 ++++++- .../publication-page-fields.component.html} | 14 ++- .../publication-page-fields.component.scss} | 0 .../publication-page-fields.component.ts | 54 ++++++++++ .../shared/entity-page-fields.component.ts | 100 +++++++++++++++++ .../related-entities-component.ts | 14 +++ .../related-entities.component.html | 5 + .../related-entities.component.scss | 0 .../orgunit-list-element.component.html | 2 +- .../publication-list-element.component.html} | 4 +- .../publication-list-element.component.scss} | 0 .../publication-list-element.component.ts} | 10 +- src/app/shared/shared.module.ts | 4 +- 22 files changed, 331 insertions(+), 142 deletions(-) delete mode 100644 src/app/+item-page/simple/entity-types/item/item-page-fields.component.ts rename src/app/+item-page/simple/entity-types/{item/item-page-fields.component.html => publication/publication-page-fields.component.html} (61%) rename src/app/+item-page/simple/entity-types/{item/item-page-fields.component.scss => publication/publication-page-fields.component.scss} (100%) create mode 100644 src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts create mode 100644 src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts create mode 100644 src/app/+item-page/simple/related-entities/related-entities-component.ts create mode 100644 src/app/+item-page/simple/related-entities/related-entities.component.html create mode 100644 src/app/+item-page/simple/related-entities/related-entities.component.scss rename src/app/shared/object-list/item-list-element/entity-types/{item/item-list-element.component.html => publication/publication-list-element.component.html} (84%) rename src/app/shared/object-list/item-list-element/entity-types/{item/item-list-element.component.scss => publication/publication-list-element.component.scss} (100%) rename src/app/shared/object-list/item-list-element/entity-types/{item/item-list-element.component.ts => publication/publication-list-element.component.ts} (54%) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a06f77463f..5057410ff8 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -26,7 +26,7 @@ }, "item": { "page": { - "author": "Author", + "author": "Authors", "abstract": "Abstract", "date": "Date", "uri": "URI", @@ -45,6 +45,13 @@ } } }, + "relationships": { + "isPublicationOf": "Publications", + "isProjectOf": "Projects", + "isOrgUnitOf": "Org Units", + "isAuthorOf": "Authors", + "isPersonOf": "Authors" + }, "person": { "page": { "jobtitle": "Job Title", @@ -54,7 +61,6 @@ "orcid": "ORCID", "birthdate": "Birth Date", "staffid": "Staff ID", - "publications": "Publications", "link": { "full": "Show all metadata" } diff --git a/src/app/+item-page/full/full-item-page.component.html b/src/app/+item-page/full/full-item-page.component.html index 4c44b72602..d79c4cdc34 100644 --- a/src/app/+item-page/full/full-item-page.component.html +++ b/src/app/+item-page/full/full-item-page.component.html @@ -1,6 +1,6 @@
-
+
+ + + + + + diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index 74e75e1dc9..9f3437c227 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -1,8 +1,14 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; @rendersEntityType('OrgUnit', ElementViewMode.Full) @Component({ @@ -10,9 +16,34 @@ import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher. styleUrls: ['./orgunit-page-fields.component.scss'], templateUrl: './orgunit-page-fields.component.html' }) -export class OrgUnitPageFieldsComponent { +export class OrgUnitPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { - constructor(@Inject(ITEM) public item: Item) { + people$: Observable; + projects$: Observable; + publications$: Observable; + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); } -} + ngOnInit(): void { + super.ngOnInit(); + + this.people$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPersonOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); + + this.projects$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isProjectOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); + + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); + }} diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index a19170b77d..16a33a65de 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -24,13 +24,18 @@
- - - - {{publ.name}} - - - + + + + + + diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index dce8fe5dbe..61b2d87389 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -1,67 +1,14 @@ -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; import { ItemDataService } from '../../../../core/data/item-data.service'; -import { PaginatedList } from '../../../../core/data/paginated-list'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; -import { Relationship } from '../../../../core/shared/entities/relationship.model'; import { Item } from '../../../../core/shared/item.model'; -import { getRemoteDataPayload } from '../../../../core/shared/operators'; -import { hasValue } from '../../../../shared/empty.util'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; - -const compareArraysUsing = (mapFn: (t: T) => any) => - (a: T[], b: T[]): boolean => { - if (!Array.isArray(a) || ! Array.isArray(b)) { - return false - } - - const aIds = a.map(mapFn); - const bIds = b.map(mapFn); - - return aIds.length === bIds.length && - aIds.every((e) => bIds.includes(e)) && - bIds.every((e) => aIds.includes(e)); - }; - -const compareArraysUsingIds = () => - compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); - -const filterRelationsByTypeLabel = (label: string) => - (source: Observable<[Relationship[], RelationshipType[]]>): Observable => - source.pipe( - map(([relsCurrentPage, relTypesCurrentPage]) => - relsCurrentPage.filter((rel: Relationship, idx: number) => - hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === label || - relTypesCurrentPage[idx].rightLabel === label) - ) - ), - distinctUntilChanged(compareArraysUsingIds()) - ); - -const relationsToItems = (thisId: string, ids: ItemDataService) => - (source: Observable): Observable => - source.pipe( - flatMap((rels: Relationship[]) => - Observable.zip( - ...rels.map((rel: Relationship) => { - let queryId = rel.leftId; - if (rel.leftId === thisId) { - queryId = rel.rightId; - } - return ids.findById(queryId); - }) - ) - ), - map((arr: Array>) => - arr - .filter((d: RemoteData) => d.hasSucceeded) - .map((d: RemoteData) => d.payload)), - distinctUntilChanged(compareArraysUsingIds()), - ); +import { ElementViewMode } from '../../../../shared/view-mode'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; @rendersEntityType('Person', ElementViewMode.Full) @Component({ @@ -69,7 +16,7 @@ const relationsToItems = (thisId: string, ids: ItemDataService) => styleUrls: ['./person-page-fields.component.scss'], templateUrl: './person-page-fields.component.html' }) -export class PersonPageFieldsComponent implements OnInit { +export class PersonPageFieldsComponent extends EntityPageFieldsComponent { publications$: Observable; projects$: Observable; orgUnits$: Observable; @@ -77,46 +24,25 @@ export class PersonPageFieldsComponent implements OnInit { constructor( @Inject(ITEM) public item: Item, private ids: ItemDataService - ) {} - + ) { + super(item); + } ngOnInit(): void { - const relsCurrentPage$ = this.item.relationships.pipe( - filter((rd: RemoteData>) => rd.hasSucceeded), - getRemoteDataPayload(), - map((pl: PaginatedList) => pl.page), - distinctUntilChanged(compareArraysUsingIds()) - ); + super.ngOnInit(); - const relTypesCurrentPage$ = relsCurrentPage$.pipe( - flatMap((rels: Relationship[]) => - Observable.zip( - ...rels.map((rel: Relationship) => rel.relationshipType), - (...arr: Array>) => - arr.map((d: RemoteData) => d.payload) - ) - ), - distinctUntilChanged(compareArraysUsingIds()) - ); - - const resolvedRelsAndTypes$ = Observable.combineLatest( - relsCurrentPage$, - relTypesCurrentPage$ - ); - - this.publications$ = resolvedRelsAndTypes$.pipe( + this.publications$ = this.resolvedRelsAndTypes$.pipe( filterRelationsByTypeLabel('isPublicationOfAuthor'), relationsToItems(this.item.id, this.ids) ); - this.projects$ = resolvedRelsAndTypes$.pipe( + this.projects$ = this.resolvedRelsAndTypes$.pipe( filterRelationsByTypeLabel('isProjectOfPerson'), relationsToItems(this.item.id, this.ids) ); - this.orgUnits$ = resolvedRelsAndTypes$.pipe( + this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( filterRelationsByTypeLabel('isOrgUnitOfPerson'), relationsToItems(this.item.id, this.ids) ); } - } diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index 65a7c7f270..51168011f1 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -20,6 +20,18 @@
+ + + + + + diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index d648247a58..844d0f3808 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -1,8 +1,14 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; @rendersEntityType('Project', ElementViewMode.Full) @Component({ @@ -10,9 +16,34 @@ import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher. styleUrls: ['./project-page-fields.component.scss'], templateUrl: './project-page-fields.component.html' }) -export class ProjectPageFieldsComponent { +export class ProjectPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { + people$: Observable; + publications$: Observable; + orgUnits$: Observable; - constructor(@Inject(ITEM) public item: Item) { + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); } + ngOnInit(): void { + super.ngOnInit(); + + this.people$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPersonOfProject'), + relationsToItems(this.item.id, this.ids) + ); + + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfProject'), + relationsToItems(this.item.id, this.ids) + ); + + this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isOrgUnitOfProject'), + relationsToItems(this.item.id, this.ids) + ); + } } diff --git a/src/app/+item-page/simple/entity-types/item/item-page-fields.component.html b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html similarity index 61% rename from src/app/+item-page/simple/entity-types/item/item-page-fields.component.html rename to src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html index 6301bc1bc9..f83338095a 100644 --- a/src/app/+item-page/simple/entity-types/item/item-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html @@ -6,9 +6,21 @@ - +
+ + + + + + diff --git a/src/app/+item-page/simple/entity-types/item/item-page-fields.component.scss b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/item/item-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.scss diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts new file mode 100644 index 0000000000..e65bba2945 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts @@ -0,0 +1,54 @@ +import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Item } from '../../../../core/shared/item.model'; +import { + DEFAULT_ENTITY_TYPE, + rendersEntityType +} from '../../../../shared/entities/entity-type-decorator'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { + EntityPageFieldsComponent, + filterRelationsByTypeLabel, relationsToItems +} from '../shared/entity-page-fields.component'; + +@rendersEntityType('Publication', ElementViewMode.Full) +@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full) +@Component({ + selector: 'ds-publication-page-fields', + styleUrls: ['./publication-page-fields.component.scss'], + templateUrl: './publication-page-fields.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PublicationPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { + authors$: Observable; + projects$: Observable; + orgUnits$: Observable; + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); + } + + ngOnInit(): void { + super.ngOnInit(); + + this.authors$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isAuthorOfPublication'), + relationsToItems(this.item.id, this.ids) + ); + + this.projects$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isProjectOfPublication'), + relationsToItems(this.item.id, this.ids) + ); + + this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isOrgUnitOfPublication'), + relationsToItems(this.item.id, this.ids) + ); + } +} diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts new file mode 100644 index 0000000000..712b84a37b --- /dev/null +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts @@ -0,0 +1,100 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; +import { Relationship } from '../../../../core/shared/entities/relationship.model'; +import { Item } from '../../../../core/shared/item.model'; +import { getRemoteDataPayload } from '../../../../core/shared/operators'; +import { hasValue } from '../../../../shared/empty.util'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; + +const compareArraysUsing = (mapFn: (t: T) => any) => + (a: T[], b: T[]): boolean => { + if (!Array.isArray(a) || ! Array.isArray(b)) { + return false + } + + const aIds = a.map(mapFn); + const bIds = b.map(mapFn); + + return aIds.length === bIds.length && + aIds.every((e) => bIds.includes(e)) && + bIds.every((e) => aIds.includes(e)); + }; + +const compareArraysUsingIds = () => + compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); + +export const filterRelationsByTypeLabel = (label: string) => + (source: Observable<[Relationship[], RelationshipType[]]>): Observable => + source.pipe( + map(([relsCurrentPage, relTypesCurrentPage]) => + relsCurrentPage.filter((rel: Relationship, idx: number) => + hasValue(relTypesCurrentPage[idx]) && (relTypesCurrentPage[idx].leftLabel === label || + relTypesCurrentPage[idx].rightLabel === label) + ) + ), + distinctUntilChanged(compareArraysUsingIds()) + ); + +export const relationsToItems = (thisId: string, ids: ItemDataService) => + (source: Observable): Observable => + source.pipe( + flatMap((rels: Relationship[]) => + Observable.zip( + ...rels.map((rel: Relationship) => { + let queryId = rel.leftId; + if (rel.leftId === thisId) { + queryId = rel.rightId; + } + return ids.findById(queryId); + }) + ) + ), + map((arr: Array>) => + arr + .filter((d: RemoteData) => d.hasSucceeded) + .map((d: RemoteData) => d.payload)), + distinctUntilChanged(compareArraysUsingIds()), + ); + +@Component({ + selector: 'ds-entity-page-fields', + template: '' +}) +export class EntityPageFieldsComponent implements OnInit { + resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]> + + constructor( + @Inject(ITEM) public item: Item + ) {} + + ngOnInit(): void { + const relsCurrentPage$ = this.item.relationships.pipe( + filter((rd: RemoteData>) => rd.hasSucceeded), + getRemoteDataPayload(), + map((pl: PaginatedList) => pl.page), + distinctUntilChanged(compareArraysUsingIds()) + ); + + const relTypesCurrentPage$ = relsCurrentPage$.pipe( + flatMap((rels: Relationship[]) => + Observable.zip( + ...rels.map((rel: Relationship) => rel.relationshipType), + (...arr: Array>) => + arr.map((d: RemoteData) => d.payload) + ) + ), + distinctUntilChanged(compareArraysUsingIds()) + ); + + this.resolvedRelsAndTypes$ = Observable.combineLatest( + relsCurrentPage$, + relTypesCurrentPage$ + ); + } + +} diff --git a/src/app/+item-page/simple/related-entities/related-entities-component.ts b/src/app/+item-page/simple/related-entities/related-entities-component.ts new file mode 100644 index 0000000000..b49e89c7aa --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities-component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; +import { Item } from '../../../core/shared/item.model'; +import * as viewMode from '../../../shared/view-mode'; + +@Component({ + selector: 'ds-related-entities', + styleUrls: ['./related-entities.component.scss'], + templateUrl: './related-entities.component.html' +}) +export class RelatedEntitiesComponent { + @Input() entities: Item[]; + @Input() label: string; + ElementViewMode = viewMode.ElementViewMode +} diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.html b/src/app/+item-page/simple/related-entities/related-entities.component.html new file mode 100644 index 0000000000..f09f0ccdef --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities.component.html @@ -0,0 +1,5 @@ + + + + diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.scss b/src/app/+item-page/simple/related-entities/related-entities.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html index 901d521033..824a90a3de 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html @@ -3,7 +3,7 @@ [routerLink]="['/items/' + item.id]" class="lead" [innerHTML]="getFirstValue('orgunit.identifier.name')"> - + - ((, ) + [innerHTML]="getFirstValue('dc.date.issued')">) diff --git a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.scss rename to src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts similarity index 54% rename from src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts rename to src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts index db68ecec7b..3f8b7a59f3 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/item/item-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts @@ -3,13 +3,13 @@ import { DEFAULT_ENTITY_TYPE, rendersEntityType } from '../../../../entities/ent import { ElementViewMode } from '../../../../view-mode'; import { EntitySearchResultComponent } from '../entity-search-result-component'; -@rendersEntityType('Item', ElementViewMode.SetElement) +@rendersEntityType('Publication', ElementViewMode.SetElement) @rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.SetElement) @Component({ - selector: 'ds-item-list-element', - styleUrls: ['./item-list-element.component.scss'], - templateUrl: './item-list-element.component.html' + selector: 'ds-publication-list-element', + styleUrls: ['./publication-list-element.component.scss'], + templateUrl: './publication-list-element.component.html' }) -export class ItemListElementComponent extends EntitySearchResultComponent { +export class PublicationListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 90d498eedd..0da82f5e2f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -10,7 +10,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { NgxPaginationModule } from 'ngx-pagination'; import { EntityTypeSwitcherComponent } from './entities/switcher/entity-type-switcher.component'; import { EntitySearchResultComponent } from './object-list/item-list-element/entity-types/entity-search-result-component'; -import { ItemListElementComponent } from './object-list/item-list-element/entity-types/item/item-list-element.component'; +import { PublicationListElementComponent } from './object-list/item-list-element/entity-types/publication/publication-list-element.component'; import { OrgUnitListElementComponent } from './object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component'; import { PersonListElementComponent } from './object-list/item-list-element/entity-types/person/person-list-element.component'; import { ProjectListElementComponent } from './object-list/item-list-element/entity-types/project/project-list-element.component'; @@ -109,7 +109,7 @@ const ENTRY_COMPONENTS = [ CollectionGridElementComponent, CommunityGridElementComponent, SearchResultGridElementComponent, - ItemListElementComponent, + PublicationListElementComponent, PersonListElementComponent, OrgUnitListElementComponent, ProjectListElementComponent From f50e0edbc07fdce4c02db47ca9966cb960e82178 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 31 May 2018 12:40:05 +0200 Subject: [PATCH 009/205] 52213: Item lists: displayed metadata journal-types --- .../journal-issue-list-element.component.html | 20 +++++++++++++++++++ .../journal-issue-list-element.component.scss | 0 .../journal-issue-list-element.component.ts | 14 +++++++++++++ ...journal-volume-list-element.component.html | 20 +++++++++++++++++++ ...journal-volume-list-element.component.scss | 0 .../journal-volume-list-element.component.ts | 14 +++++++++++++ .../journal-list-element.component.html | 15 ++++++++++++++ .../journal-list-element.component.scss | 0 .../journal/journal-list-element.component.ts | 14 +++++++++++++ src/app/shared/shared.module.ts | 8 +++++++- 10 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.scss create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html new file mode 100644 index 0000000000..08011cf04e --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html @@ -0,0 +1,20 @@ + + + + + + + + + + + - + + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts new file mode 100644 index 0000000000..8580757997 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('JournalIssue', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-issue-list-element', + styleUrls: ['./journal-issue-list-element.component.scss'], + templateUrl: './journal-issue-list-element.component.html' +}) + +export class JournalIssueListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html new file mode 100644 index 0000000000..b071179950 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + () + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts new file mode 100644 index 0000000000..956afe99be --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('JournalVolume', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-volume-list-element', + styleUrls: ['./journal-volume-list-element.component.scss'], + templateUrl: './journal-volume-list-element.component.html' +}) + +export class JournalVolumeListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html new file mode 100644 index 0000000000..fb5284d398 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.scss b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts new file mode 100644 index 0000000000..d5d2cee1cc --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import { rendersEntityType } from '../../../../entities/entity-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { EntitySearchResultComponent } from '../entity-search-result-component'; + +@rendersEntityType('Journal', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-list-element', + styleUrls: ['./journal-list-element.component.scss'], + templateUrl: './journal-list-element.component.html' +}) + +export class JournalListElementComponent extends EntitySearchResultComponent { +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 0da82f5e2f..2dd35cb6f8 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -14,6 +14,9 @@ import { PublicationListElementComponent } from './object-list/item-list-element import { OrgUnitListElementComponent } from './object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component'; import { PersonListElementComponent } from './object-list/item-list-element/entity-types/person/person-list-element.component'; import { ProjectListElementComponent } from './object-list/item-list-element/entity-types/project/project-list-element.component'; +import { JournalListElementComponent } from './object-list/item-list-element/entity-types/journal/journal-list-element.component'; +import { JournalVolumeListElementComponent } from './object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component'; +import { JournalIssueListElementComponent } from './object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component'; import { EnumKeysPipe } from './utils/enum-keys-pipe'; import { FileSizePipe } from './utils/file-size-pipe'; @@ -112,7 +115,10 @@ const ENTRY_COMPONENTS = [ PublicationListElementComponent, PersonListElementComponent, OrgUnitListElementComponent, - ProjectListElementComponent + ProjectListElementComponent, + JournalListElementComponent, + JournalVolumeListElementComponent, + JournalIssueListElementComponent ]; const PROVIDERS = [ From c99d05c5f98d3d8b6b17459fb31e98f2a71201a3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 31 May 2018 14:02:04 +0200 Subject: [PATCH 010/205] 52211: Item display regular metadata journal-types --- resources/i18n/en.json | 22 ++++++++++++++ src/app/+item-page/item-page.module.ts | 13 ++++++-- .../journal-issue-page-fields.component.html | 28 +++++++++++++++++ .../journal-issue-page-fields.component.scss | 1 + .../journal-issue-page-fields.component.ts | 30 +++++++++++++++++++ .../journal-volume-page-fields.component.html | 24 +++++++++++++++ .../journal-volume-page-fields.component.scss | 1 + .../journal-volume-page-fields.component.ts | 30 +++++++++++++++++++ .../journal-page-fields.component.html | 24 +++++++++++++++ .../journal-page-fields.component.scss | 1 + .../journal/journal-page-fields.component.ts | 30 +++++++++++++++++++ 11 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html create mode 100644 src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss create mode 100644 src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts create mode 100644 src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html create mode 100644 src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss create mode 100644 src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts create mode 100644 src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html create mode 100644 src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss create mode 100644 src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 5057410ff8..ab53d312e5 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -84,6 +84,28 @@ "description": "Description" } }, + "journal": { + "page": { + "issn": "ISSN", + "publisher": "Publisher", + "description": "Description" + } + }, + "journalvolume": { + "page": { + "volume": "Volume", + "issuedate": "Issue Date", + "description": "Description" + } + }, + "journalissue": { + "page": { + "number": "Number", + "issuedate": "Issue Date", + "description": "Description", + "keyword": "Keywords" + } + }, "nav": { "home": "Home" }, diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index 763f549b26..ce859e0168 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -25,6 +25,9 @@ import { OrgUnitPageFieldsComponent } from './simple/entity-types/orgunit/orguni import { PersonPageFieldsComponent } from './simple/entity-types/person/person-page-fields.component'; import { ProjectPageFieldsComponent } from './simple/entity-types/project/project-page-fields.component'; import { RelatedEntitiesComponent } from './simple/related-entities/related-entities-component'; +import { JournalPageFieldsComponent } from './simple/entity-types/journal/journal-page-fields.component'; +import { JournalIssuePageFieldsComponent } from './simple/entity-types/journal-issue/journal-issue-page-fields.component'; +import { JournalVolumePageFieldsComponent } from './simple/entity-types/journal-volume/journal-volume-page-fields.component'; @NgModule({ imports: [ @@ -53,13 +56,19 @@ import { RelatedEntitiesComponent } from './simple/related-entities/related-enti PersonPageFieldsComponent, RelatedEntitiesComponent, EntityPageFieldsComponent, - GenericItemPageFieldComponent + GenericItemPageFieldComponent, + JournalPageFieldsComponent, + JournalIssuePageFieldsComponent, + JournalVolumePageFieldsComponent ], entryComponents: [ PublicationPageFieldsComponent, ProjectPageFieldsComponent, OrgUnitPageFieldsComponent, - PersonPageFieldsComponent + PersonPageFieldsComponent, + JournalPageFieldsComponent, + JournalIssuePageFieldsComponent, + JournalVolumePageFieldsComponent ] }) export class ItemPageModule { diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html new file mode 100644 index 0000000000..ae1e6f9376 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html @@ -0,0 +1,28 @@ +

+ +

+
+
+ + + + + + + +
+
+ + + + +
+
diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts new file mode 100644 index 0000000000..bbf2515945 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; + +@rendersEntityType('JournalIssue', ElementViewMode.Full) +@Component({ + selector: 'ds-journal-issue-page-fields', + styleUrls: ['./journal-issue-page-fields.component.scss'], + templateUrl: './journal-issue-page-fields.component.html' +}) +export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); + } + ngOnInit(): void { + super.ngOnInit(); + } +} diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html new file mode 100644 index 0000000000..eabf0ede98 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html @@ -0,0 +1,24 @@ +

+ +

+
+
+ + + + + + + +
+
+ + +
+
diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts new file mode 100644 index 0000000000..1f0a046ce6 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; + +@rendersEntityType('JournalVolume', ElementViewMode.Full) +@Component({ + selector: 'ds-journal-volume-page-fields', + styleUrls: ['./journal-volume-page-fields.component.scss'], + templateUrl: './journal-volume-page-fields.component.html' +}) +export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent { + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); + } + ngOnInit(): void { + super.ngOnInit(); + } +} diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html new file mode 100644 index 0000000000..eaa72b8e95 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html @@ -0,0 +1,24 @@ +

+ +

+
+
+ + + + + + + +
+
+ + +
+
diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss new file mode 100644 index 0000000000..3575cae797 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss @@ -0,0 +1 @@ +@import '../../../../../styles/variables.scss'; diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts new file mode 100644 index 0000000000..27b4710886 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts @@ -0,0 +1,30 @@ +import { Component, Inject } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Item } from '../../../../core/shared/item.model'; +import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ElementViewMode } from '../../../../shared/view-mode'; +import { + EntityPageFieldsComponent, filterRelationsByTypeLabel, + relationsToItems +} from '../shared/entity-page-fields.component'; + +@rendersEntityType('Journal', ElementViewMode.Full) +@Component({ + selector: 'ds-journal-page-fields', + styleUrls: ['./journal-page-fields.component.scss'], + templateUrl: './journal-page-fields.component.html' +}) +export class JournalPageFieldsComponent extends EntityPageFieldsComponent { + + constructor( + @Inject(ITEM) public item: Item, + private ids: ItemDataService + ) { + super(item); + } + ngOnInit(): void { + super.ngOnInit(); + } +} From 34dbc24ba3677a4e156359aa763afd8f5b52ff64 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 31 May 2018 14:38:12 +0200 Subject: [PATCH 011/205] 52212: Item display relations journal-types --- resources/i18n/en.json | 7 ++++++- .../journal-issue-page-fields.component.html | 4 ++++ .../journal-issue-page-fields.component.ts | 6 ++++++ .../journal-volume-page-fields.component.html | 8 ++++++++ .../journal-volume-page-fields.component.ts | 11 +++++++++++ .../journal/journal-page-fields.component.html | 4 ++++ .../journal/journal-page-fields.component.ts | 6 ++++++ 7 files changed, 45 insertions(+), 1 deletion(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index ab53d312e5..ab0d82aabe 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -50,7 +50,12 @@ "isProjectOf": "Projects", "isOrgUnitOf": "Org Units", "isAuthorOf": "Authors", - "isPersonOf": "Authors" + "isPersonOf": "Authors", + "isJournalOf": "Journals", + "isSingleJournalOf": "Journal", + "isVolumeOf": "Volumes", + "isSingleVolumeOf": "Volume", + "isIssueOf": "Issues" }, "person": { "page": { diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html index ae1e6f9376..3f86ab912b 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html @@ -16,6 +16,10 @@
+ + diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts index bbf2515945..2f98c187f2 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts @@ -17,6 +17,7 @@ import { templateUrl: './journal-issue-page-fields.component.html' }) export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { + volumes$: Observable; constructor( @Inject(ITEM) public item: Item, @@ -26,5 +27,10 @@ export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { } ngOnInit(): void { super.ngOnInit(); + + this.volumes$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isJournalVolumeOfIssue'), + relationsToItems(this.item.id, this.ids) + ); } } diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html index eabf0ede98..b1dbc5429f 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html @@ -16,6 +16,14 @@
+ + + + diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts index 1f0a046ce6..cd3725018d 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts @@ -17,6 +17,8 @@ import { templateUrl: './journal-volume-page-fields.component.html' }) export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent { + journals$: Observable; + issues$: Observable; constructor( @Inject(ITEM) public item: Item, @@ -26,5 +28,14 @@ export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent } ngOnInit(): void { super.ngOnInit(); + + this.journals$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isJournalOfVolume'), + relationsToItems(this.item.id, this.ids) + ); + this.issues$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isIssueOfJournalVolume'), + relationsToItems(this.item.id, this.ids) + ); } } diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html index eaa72b8e95..452e1d61ea 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html @@ -16,6 +16,10 @@
+ + diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts index 27b4710886..b43805ec52 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts @@ -17,6 +17,7 @@ import { templateUrl: './journal-page-fields.component.html' }) export class JournalPageFieldsComponent extends EntityPageFieldsComponent { + volumes$: Observable; constructor( @Inject(ITEM) public item: Item, @@ -26,5 +27,10 @@ export class JournalPageFieldsComponent extends EntityPageFieldsComponent { } ngOnInit(): void { super.ngOnInit(); + + this.volumes$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isVolumeOfJournal'), + relationsToItems(this.item.id, this.ids) + ); } } From 733ca643cc9f8e01d72bfe615d2ffcf797d22673 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Thu, 31 May 2018 16:48:27 +0200 Subject: [PATCH 012/205] fix for entity page changes when going back/forth using browser --- src/app/+item-page/full/full-item-page.component.ts | 7 ++++--- src/app/+item-page/simple/item-page.component.ts | 8 +++++--- src/app/shared/view-mode.ts | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index aa1fc4cc78..97e08099c1 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs/Observable'; @@ -34,8 +34,9 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit { metadataObs: Observable; - constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) { - super(route, items, metadataService); + constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService, + ref: ChangeDetectorRef) { + super(route, items, metadataService, ref); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index fee7753c82..cbcad3aa99 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs/Observable'; @@ -41,7 +41,8 @@ export class ItemPageComponent implements OnInit { constructor( private route: ActivatedRoute, private items: ItemDataService, - private metadataService: MetadataService + private metadataService: MetadataService, + private ref: ChangeDetectorRef ) { } @@ -50,16 +51,17 @@ export class ItemPageComponent implements OnInit { this.sub = this.route.params.subscribe((params) => { this.initialize(params); }); + } initialize(params) { this.id = +params.id; this.itemRDObs = this.items.findById(params.id); + this.ref.detectChanges(); this.metadataService.processRemoteData(this.itemRDObs); this.thumbnailObs = this.itemRDObs .map((rd: RemoteData) => rd.payload) .filter((item: Item) => hasValue(item)) .flatMap((item: Item) => item.getThumbnail()); } - } diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts index e78da4fa83..b0e180c290 100644 --- a/src/app/shared/view-mode.ts +++ b/src/app/shared/view-mode.ts @@ -1,6 +1,6 @@ export enum SetViewMode { - List, - Grid + List = 'list', + Grid = 'grid' } export enum ElementViewMode { From 691b466d9679cbb36aa21305244d51375ba47921 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 1 Jun 2018 09:48:31 +0200 Subject: [PATCH 013/205] 52425: Publication - JournalIssue relation --- resources/i18n/en.json | 4 +++- .../journal-issue/journal-issue-page-fields.component.html | 4 ++++ .../journal-issue/journal-issue-page-fields.component.ts | 5 +++++ .../publication/publication-page-fields.component.html | 6 +++++- .../publication/publication-page-fields.component.ts | 6 ++++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index ab0d82aabe..0de22af7e5 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -55,7 +55,9 @@ "isSingleJournalOf": "Journal", "isVolumeOf": "Volumes", "isSingleVolumeOf": "Volume", - "isIssueOf": "Issues" + "isIssueOf": "Issues", + "isJournalIssueOf": "Journal Issue", + "isPublicationOfJournalIssue": "Articles" }, "person": { "page": { diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html index 3f86ab912b..f22e98795d 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html @@ -20,6 +20,10 @@ [entities]="volumes$ | async" [label]="'relationships.isSingleVolumeOf' | translate"> + + diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts index 2f98c187f2..eab4f14fa3 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts @@ -18,6 +18,7 @@ import { }) export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { volumes$: Observable; + publications$: Observable; constructor( @Inject(ITEM) public item: Item, @@ -32,5 +33,9 @@ export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { filterRelationsByTypeLabel('isJournalVolumeOfIssue'), relationsToItems(this.item.id, this.ids) ); + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfJournalIssue'), + relationsToItems(this.item.id, this.ids) + ); } } diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html index f83338095a..84cd5a151b 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html @@ -18,9 +18,13 @@ [label]="'relationships.isProjectOf' | translate"> + + diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts index e65bba2945..419d415122 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts @@ -25,6 +25,7 @@ export class PublicationPageFieldsComponent extends EntityPageFieldsComponent im authors$: Observable; projects$: Observable; orgUnits$: Observable; + journalIssues$: Observable; constructor( @Inject(ITEM) public item: Item, @@ -50,5 +51,10 @@ export class PublicationPageFieldsComponent extends EntityPageFieldsComponent im filterRelationsByTypeLabel('isOrgUnitOfPublication'), relationsToItems(this.item.id, this.ids) ); + + this.journalIssues$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isJournalIssueOfPublication'), + relationsToItems(this.item.id, this.ids) + ); } } From 90126888bbede4e3f0e59146e52855bcd16f2645 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 1 Jun 2018 10:06:50 +0200 Subject: [PATCH 014/205] fixed with subject instead of changedetection hack --- src/app/+item-page/full/full-item-page.component.ts | 8 ++++---- src/app/+item-page/simple/item-page.component.ts | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index 97e08099c1..778478a6b6 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -14,6 +14,7 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; /** * This component renders a simple item page. @@ -30,13 +31,12 @@ import { hasValue } from '../../shared/empty.util'; }) export class FullItemPageComponent extends ItemPageComponent implements OnInit { - itemRDObs: Observable>; + itemRDObs: BehaviorSubject>; metadataObs: Observable; - constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService, - ref: ChangeDetectorRef) { - super(route, items, metadataService, ref); + constructor(route: ActivatedRoute, items: ItemDataService, metadataService: MetadataService) { + super(route, items, metadataService); } /*** AoT inheritance fix, will hopefully be resolved in the near future **/ diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index cbcad3aa99..55dfe8a7b0 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -13,6 +13,7 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; import * as viewMode from '../../shared/view-mode'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; /** * This component renders a simple item page. @@ -32,17 +33,16 @@ export class ItemPageComponent implements OnInit { private sub: any; - itemRDObs: Observable>; - thumbnailObs: Observable; + itemRDObs?: BehaviorSubject> = new BehaviorSubject(new RemoteData(true, true, false, null, null)); + ElementViewMode = viewMode.ElementViewMode; constructor( private route: ActivatedRoute, private items: ItemDataService, private metadataService: MetadataService, - private ref: ChangeDetectorRef ) { } @@ -56,8 +56,7 @@ export class ItemPageComponent implements OnInit { initialize(params) { this.id = +params.id; - this.itemRDObs = this.items.findById(params.id); - this.ref.detectChanges(); + this.items.findById(params.id).filter((rd) => hasValue(rd.payload)).first().subscribe((item) => this.itemRDObs.next(item)); this.metadataService.processRemoteData(this.itemRDObs); this.thumbnailObs = this.itemRDObs .map((rd: RemoteData) => rd.payload) From 8ab6eb6ec882d19fcb53d4ba5fc7945fbcd9ab44 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 1 Jun 2018 12:06:21 +0200 Subject: [PATCH 015/205] item page reload fix --- .../+item-page/simple/item-page.component.html | 8 ++++---- src/app/+item-page/simple/item-page.component.ts | 16 +++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 17d271f368..58dbbe3aa1 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,9 +1,9 @@
-
+
- +
- - + +
diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 55dfe8a7b0..1dfa93f52d 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -14,6 +14,7 @@ import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; import * as viewMode from '../../shared/view-mode'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Subscription } from 'rxjs/Subscription'; /** * This component renders a simple item page. @@ -31,11 +32,11 @@ export class ItemPageComponent implements OnInit { id: number; - private sub: any; - + private sub: Subscription; + private itemSub: Subscription; thumbnailObs: Observable; - itemRDObs?: BehaviorSubject> = new BehaviorSubject(new RemoteData(true, true, false, null, null)); + itemRDObs: BehaviorSubject> = new BehaviorSubject(new RemoteData(true, true, undefined, null, null)); ElementViewMode = viewMode.ElementViewMode; @@ -43,9 +44,7 @@ export class ItemPageComponent implements OnInit { private route: ActivatedRoute, private items: ItemDataService, private metadataService: MetadataService, - ) { - - } + ) { } ngOnInit(): void { this.sub = this.route.params.subscribe((params) => { @@ -56,7 +55,10 @@ export class ItemPageComponent implements OnInit { initialize(params) { this.id = +params.id; - this.items.findById(params.id).filter((rd) => hasValue(rd.payload)).first().subscribe((item) => this.itemRDObs.next(item)); + if (hasValue(this.itemSub)) { + this.itemSub.unsubscribe(); + } + this.itemSub = this.items.findById(params.id).subscribe((item) => this.itemRDObs.next(item)); this.metadataService.processRemoteData(this.itemRDObs); this.thumbnailObs = this.itemRDObs .map((rd: RemoteData) => rd.payload) From f1a8517b5f36ba449da8222e31db2379f3fbb760 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 1 Jun 2018 12:09:22 +0200 Subject: [PATCH 016/205] clean up after fix --- src/app/+item-page/simple/item-page.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 58dbbe3aa1..17d271f368 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,9 +1,9 @@
-
+
- +
- - + +
From 8ea8f1194468276103df4eea6fcafd78f24a67fe Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 18 Jun 2018 11:27:48 +0200 Subject: [PATCH 017/205] 52935: entity filter messages --- resources/i18n/en.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 0de22af7e5..5e30d08ed0 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -197,6 +197,10 @@ "has_content_in_original_bundle": { "placeholder": "Has files", "head": "Has files" + }, + "entityType": { + "placeholder": "Entity Type", + "head": "Entity Type" } } } From d4c00dbaa07e94f892ec3a86657e0a4aeebd41f7 Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 18 Jun 2018 12:07:00 +0200 Subject: [PATCH 018/205] Fixed typo that broke AoT build --- .../entity-types/project/project-page-fields.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index 51168011f1..a1c5502307 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -29,7 +29,7 @@ [label]="'relationships.isPublicationOf' | translate"> Date: Mon, 18 Jun 2018 12:11:25 +0200 Subject: [PATCH 019/205] fixed another typo to fix AoT build.. --- .../entity-types/person/person-page-fields.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index 16a33a65de..d7cc3f1158 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -33,7 +33,7 @@ [label]="'relationships.isProjectOf' | translate"> Date: Fri, 29 Jun 2018 15:21:47 +0200 Subject: [PATCH 020/205] 53029: start of filtered discovery pages --- .../search-fixed-filter.service.ts | 78 +++++++++++++++++++ .../data/base-response-parsing.service.ts | 2 +- .../data/search-response-parsing.service.ts | 2 +- src/app/shared/route.service.ts | 26 +++---- 4 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts new file mode 100644 index 0000000000..ed5d2c5496 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { debounceTime, distinctUntilChanged, flatMap, map } from 'rxjs/operators'; +import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; +import { createSelector, MemoizedSelector, Store } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; +import { + SearchFilterCollapseAction, + SearchFilterDecrementPageAction, SearchFilterExpandAction, + SearchFilterIncrementPageAction, + SearchFilterInitialCollapseAction, + SearchFilterInitialExpandAction, SearchFilterResetPageAction, + SearchFilterToggleAction +} from './search-filter.actions'; +import { hasValue, isEmpty, isNotEmpty, } from '../../../shared/empty.util'; +import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; +import { SearchService } from '../../search-service/search.service'; +import { RouteService } from '../../../shared/route.service'; +import ObjectExpression from 'rollup/dist/typings/ast/nodes/ObjectExpression'; +import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; +import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +import { SearchOptions } from '../../search-options.model'; +import { PaginatedSearchOptions } from '../../paginated-search-options.model'; +import { RemoteData } from '../../../core/data/remote-data'; +import { configureRequest } from '../../../core/shared/operators'; +import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; +import { GetRequest, RestRequest } from '../../../core/data/request.models'; +import { FacetConfigSuccessResponse } from '../../../core/cache/response-cache.models'; +import { ResponseCacheEntry } from '../../../core/cache/response-cache.reducer'; +import { RequestService } from '../../../core/data/request.service'; +import { ResponseCacheService } from '../../../core/cache/response-cache.service'; +import { SearchResponseParsingService } from '../../../core/data/search-response-parsing.service'; +import { ResponseParsingService } from '../../../core/data/parsing.service'; +import { GenericConstructor } from '../../../core/shared/generic-constructor'; + + +@Injectable() +export class SearchFixedFilterService { + private queryByFilterPath = 'config/filtered-discovery-pages'; + + constructor(private routeService: RouteService, + protected requestService: RequestService, + protected responseCache: ResponseCacheService, + private halService: HALEndpointService) { + + } + + getQueryByFilterName(filterName?: string): Observable> { + const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( + map((url: string) => { + url += ('/' + filterName); + const request = new GetRequest(this.requestService.generateRequestId(), url); + return Object.assign(request, { + getResponseParser(): GenericConstructor { + return FilterDiscoveryPageResponseParsingService; + } + }); + }), + ); + + const requestEntryObs = requestObs.pipe( + flatMap((request: RestRequest) => this.requestService.getByHref(request.href)) + ); + + const responseCacheObs = requestObs.pipe( + flatMap((request: RestRequest) => this.responseCache.get(request.href)) + ); + + // get search results from response cache + const fixedFilterConfigObs: Observable = responseCacheObs.pipe( + map((entry: ResponseCacheEntry) => entry.response), + map((response: FacetConfigSuccessResponse) => + response.results.map((result: any) => Object.assign(new SearchFilterConfig(), result))) + ); + + return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, facetConfigObs); + } + +} diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 050b3c2da5..3f64be7515 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -13,7 +13,7 @@ function isObjectLevel(halObj: any) { } function isPaginatedResponse(halObj: any) { - return isNotEmpty(halObj.page) && hasValue(halObj._embedded); + return isNotEmpty(halObj.page) /** && hasValue(halObj._embedded)**/; } /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index c7456aa2f9..ebefbe4ba4 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -16,7 +16,7 @@ export class SearchResponseParsingService implements ResponseParsingService { } parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { - const payload = data.payload; + const payload = data.payload._embedded.searchResult; const hitHighlights = payload._embedded.objects .map((object) => object.hitHighlights) .map((hhObject) => { diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 9c2b64ede1..716a77213b 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -1,10 +1,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { - ActivatedRoute, convertToParamMap, NavigationExtras, Params, - Router, -} from '@angular/router'; -import { isNotEmpty } from './empty.util'; +import { ActivatedRoute, Params, } from '@angular/router'; @Injectable() export class RouteService { @@ -28,16 +24,20 @@ export class RouteService { return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1).distinctUntilChanged(); } + getRouteParameterValue(paramName: string): Observable { + return this.route.params.map((params) => params[paramName]).distinctUntilChanged(); + } + getQueryParamsWithPrefix(prefix: string): Observable { return this.route.queryParamMap .map((map) => { - const params = {}; - map.keys - .filter((key) => key.startsWith(prefix)) - .forEach((key) => { - params[key] = [...map.getAll(key)]; - }); - return params; - }).distinctUntilChanged(); + const params = {}; + map.keys + .filter((key) => key.startsWith(prefix)) + .forEach((key) => { + params[key] = [...map.getAll(key)]; + }); + return params; + }).distinctUntilChanged(); } } From 1231d3ebadc644407d668add4a60a63eb353088a Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 3 Jul 2018 15:24:31 +0200 Subject: [PATCH 021/205] 53029: almost finished filtered discovery pages --- .../filtered-search-page.component.ts | 34 ++++++++++++++ .../search-filter/search-filter.service.ts | 23 ++++++--- .../search-fixed-filter.service.ts | 47 +++++-------------- src/app/+search-page/search-options.model.ts | 5 +- .../search-page-routing.module.ts | 4 +- src/app/+search-page/search-page.component.ts | 13 +++-- src/app/+search-page/search-page.module.ts | 6 ++- src/app/core/cache/response-cache.models.ts | 10 ++++ ...discovery-page-response-parsing.service.ts | 24 ++++++++++ src/app/shared/route.service.ts | 4 ++ 10 files changed, 120 insertions(+), 50 deletions(-) create mode 100644 src/app/+search-page/filtered-search-page.component.ts create mode 100644 src/app/core/data/filtered-discovery-page-response-parsing.service.ts diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts new file mode 100644 index 0000000000..5e68556e59 --- /dev/null +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -0,0 +1,34 @@ +import { CommunityDataService } from '../core/data/community-data.service'; +import { HostWindowService } from '../shared/host-window.service'; +import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; +import { SearchService } from './search-service/search.service'; +import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; +import { SearchPageComponent } from './search-page.component'; +import { RouteService } from '../shared/route.service'; +import { ChangeDetectionStrategy, Component, Injectable } from '@angular/core'; +import { pushInOut } from '../shared/animations/push'; + +/** + * This component renders a simple item page. + * The route parameter 'id' is used to request the item it represents. + * All fields of the item that should be displayed, are defined in its template. + */ +@Component({selector: 'ds-filtered-search-page', + styleUrls: ['./search-page.component.scss'], + templateUrl: './search-page.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + animations: [pushInOut] +}) + +export class FilteredSearchPageComponent extends SearchPageComponent { + + constructor(protected service: SearchService, + protected communityService: CommunityDataService, + protected sidebarService: SearchSidebarService, + protected windowService: HostWindowService, + protected filterService: SearchFilterService, + protected routeService: RouteService) { + super(service, communityService, sidebarService, windowService, filterService, routeService); + } + +} diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 44d9c7e709..f6227645db 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -20,6 +20,7 @@ import { SortDirection, SortOptions } from '../../../core/cache/models/sort-opti import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../../search-options.model'; import { PaginatedSearchOptions } from '../../paginated-search-options.model'; +import { SearchFixedFilterService } from './search-fixed-filter.service'; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; @@ -27,7 +28,8 @@ const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; export class SearchFilterService { constructor(private store: Store, - private routeService: RouteService) { + private routeService: RouteService, + private fixedFilterService: SearchFixedFilterService) { } isFilterActiveWithValue(paramName: string, filterValue: string): Observable { @@ -72,6 +74,11 @@ export class SearchFilterService { return this.routeService.getQueryParamsWithPrefix('f.'); } + getCurrentFixedFilter(): Observable { + const filter: Observable = this.routeService.getRouteParameterValue('filter'); + return filter.flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)); + } + getCurrentView() { return this.routeService.getQueryParameterValue('view'); } @@ -83,9 +90,10 @@ export class SearchFilterService { this.getCurrentView(), this.getCurrentScope(), this.getCurrentQuery(), - this.getCurrentFilters()).pipe( + this.getCurrentFilters(), + this.getCurrentFixedFilter()).pipe( distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), - map(([pagination, sort, view, scope, query, filters]) => { + map(([pagination, sort, view, scope, query, filters, fixedFilter]) => { return Object.assign(new PaginatedSearchOptions(), defaults, { @@ -94,7 +102,8 @@ export class SearchFilterService { view: view, scope: scope || defaults.scope, query: query, - filters: filters + filters: filters, + fixedFilter: fixedFilter }) }) ) @@ -106,14 +115,16 @@ export class SearchFilterService { this.getCurrentScope(), this.getCurrentQuery(), this.getCurrentFilters(), - (view, scope, query, filters) => { + this.getCurrentFixedFilter(), + (view, scope, query, filters, fixedFilter) => { return Object.assign(new SearchOptions(), defaults, { view: view, scope: scope || defaults.scope, query: query, - filters: filters + filters: filters, + fixedFilter: fixedFilter }) } ) diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index ed5d2c5496..f562f4c680 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -1,41 +1,20 @@ import { Injectable } from '@angular/core'; -import { debounceTime, distinctUntilChanged, flatMap, map } from 'rxjs/operators'; -import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; -import { createSelector, MemoizedSelector, Store } from '@ngrx/store'; +import { flatMap, map } from 'rxjs/operators'; import { Observable } from 'rxjs/Observable'; -import { - SearchFilterCollapseAction, - SearchFilterDecrementPageAction, SearchFilterExpandAction, - SearchFilterIncrementPageAction, - SearchFilterInitialCollapseAction, - SearchFilterInitialExpandAction, SearchFilterResetPageAction, - SearchFilterToggleAction -} from './search-filter.actions'; -import { hasValue, isEmpty, isNotEmpty, } from '../../../shared/empty.util'; -import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; -import { SearchService } from '../../search-service/search.service'; import { RouteService } from '../../../shared/route.service'; -import ObjectExpression from 'rollup/dist/typings/ast/nodes/ObjectExpression'; -import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; -import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; -import { SearchOptions } from '../../search-options.model'; -import { PaginatedSearchOptions } from '../../paginated-search-options.model'; -import { RemoteData } from '../../../core/data/remote-data'; -import { configureRequest } from '../../../core/shared/operators'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { GetRequest, RestRequest } from '../../../core/data/request.models'; -import { FacetConfigSuccessResponse } from '../../../core/cache/response-cache.models'; +import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response-cache.models'; import { ResponseCacheEntry } from '../../../core/cache/response-cache.reducer'; import { RequestService } from '../../../core/data/request.service'; import { ResponseCacheService } from '../../../core/cache/response-cache.service'; -import { SearchResponseParsingService } from '../../../core/data/search-response-parsing.service'; import { ResponseParsingService } from '../../../core/data/parsing.service'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; - +import { FilteredDiscoveryPageResponseParsingService } from '../../../core/data/filtered-discovery-page-response-parsing.service'; @Injectable() export class SearchFixedFilterService { - private queryByFilterPath = 'config/filtered-discovery-pages'; + private queryByFilterPath = 'filtered-discovery-pages'; constructor(private routeService: RouteService, protected requestService: RequestService, @@ -44,35 +23,31 @@ export class SearchFixedFilterService { } - getQueryByFilterName(filterName?: string): Observable> { + getQueryByFilterName(filterName?: string): Observable { const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( map((url: string) => { url += ('/' + filterName); const request = new GetRequest(this.requestService.generateRequestId(), url); return Object.assign(request, { getResponseParser(): GenericConstructor { - return FilterDiscoveryPageResponseParsingService; + return FilteredDiscoveryPageResponseParsingService; } }); }), ); - const requestEntryObs = requestObs.pipe( - flatMap((request: RestRequest) => this.requestService.getByHref(request.href)) - ); - const responseCacheObs = requestObs.pipe( flatMap((request: RestRequest) => this.responseCache.get(request.href)) ); // get search results from response cache - const fixedFilterConfigObs: Observable = responseCacheObs.pipe( + const filterQuery: Observable = responseCacheObs.pipe( map((entry: ResponseCacheEntry) => entry.response), - map((response: FacetConfigSuccessResponse) => - response.results.map((result: any) => Object.assign(new SearchFilterConfig(), result))) - ); + map((response: FilteredDiscoveryQueryResponse) => + response.filterQuery + )); - return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, facetConfigObs); + return filterQuery; } } diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index 770d123f92..64a1df4d2d 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -8,9 +8,12 @@ export class SearchOptions { scope?: string; query?: string; filters?: any; + fixedFilter?: any; toRestUrl(url: string, args: string[] = []): string { - + if (isNotEmpty(this.fixedFilter)) { + args.push(this.fixedFilter); + } if (isNotEmpty(this.query)) { args.push(`query=${this.query}`); } diff --git a/src/app/+search-page/search-page-routing.module.ts b/src/app/+search-page/search-page-routing.module.ts index 65cca99a34..84cb0c31bb 100644 --- a/src/app/+search-page/search-page-routing.module.ts +++ b/src/app/+search-page/search-page-routing.module.ts @@ -2,11 +2,13 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SearchPageComponent } from './search-page.component'; +import { FilteredSearchPageComponent } from './filtered-search-page.component'; @NgModule({ imports: [ RouterModule.forChild([ - { path: '', component: SearchPageComponent, data: { title: 'search.title' } } + { path: '', component: SearchPageComponent, data: { title: 'search.title' } }, + { path: ':filter', component: FilteredSearchPageComponent, data: { title: 'search.title.:filter' } } ]) ] }) diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 4f50723ced..b38386d44c 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -14,6 +14,7 @@ import { SearchFilterService } from './search-filters/search-filter/search-filte import { SearchResult } from './search-result.model'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; +import { RouteService } from '../shared/route.service'; /** * This component renders a simple item page. @@ -46,12 +47,14 @@ export class SearchPageComponent implements OnInit { query: '', scope: '' }; + title; - constructor(private service: SearchService, - private communityService: CommunityDataService, - private sidebarService: SearchSidebarService, - private windowService: HostWindowService, - private filterService: SearchFilterService) { + constructor(protected service: SearchService, + protected communityService: CommunityDataService, + protected sidebarService: SearchSidebarService, + protected windowService: HostWindowService, + protected filterService: SearchFilterService, + protected routeService: RouteService) { this.isMobileView$ = Observable.combineLatest( this.windowService.isXs(), this.windowService.isSm(), diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 1468fe532e..206d18267d 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -21,6 +21,8 @@ import { SearchFiltersComponent } from './search-filters/search-filters.componen import { SearchFilterComponent } from './search-filters/search-filter/search-filter.component'; import { SearchFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component'; import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; +import { FilteredSearchPageComponent } from './filtered-search-page.component'; +import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service'; const effects = [ SearchSidebarEffects @@ -36,6 +38,7 @@ const effects = [ ], declarations: [ SearchPageComponent, + FilteredSearchPageComponent, SearchResultsComponent, SearchSidebarComponent, SearchSettingsComponent, @@ -53,7 +56,8 @@ const effects = [ providers: [ SearchService, SearchSidebarService, - SearchFilterService + SearchFilterService, + SearchFixedFilterService ], entryComponents: [ ItemSearchResultListElementComponent, diff --git a/src/app/core/cache/response-cache.models.ts b/src/app/core/cache/response-cache.models.ts index 81fa9ac759..769027f204 100644 --- a/src/app/core/cache/response-cache.models.ts +++ b/src/app/core/cache/response-cache.models.ts @@ -108,4 +108,14 @@ export class ConfigSuccessResponse extends RestResponse { super(true, statusCode); } } + +export class FilteredDiscoveryQueryResponse extends RestResponse { + constructor( + public filterQuery: string, + public statusCode: string, + public pageInfo?: PageInfo + ) { + super(true, statusCode); + } +} /* tslint:enable:max-classes-per-file */ diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts new file mode 100644 index 0000000000..ee87230214 --- /dev/null +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts @@ -0,0 +1,24 @@ +import { Inject, Injectable } from '@angular/core'; +import { FilteredDiscoveryQueryResponse, RestResponse } from '../cache/response-cache.models'; +import { ResponseParsingService } from './parsing.service'; +import { RestRequest } from './request.models'; +import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; +import { BaseResponseParsingService } from './base-response-parsing.service'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { GlobalConfig } from '../../../config/global-config.interface'; +import { GLOBAL_CONFIG } from '../../../config'; + +@Injectable() +export class FilteredDiscoveryPageResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { + objectFactory = {}; + toCache = false; + constructor( + @Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, + protected objectCache: ObjectCacheService, + ) { super(); + } + parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { + const query = data.payload['discovery-query']; + return new FilteredDiscoveryQueryResponse(query, data.statusCode); + } +} diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 716a77213b..3eb629e60c 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -28,6 +28,10 @@ export class RouteService { return this.route.params.map((params) => params[paramName]).distinctUntilChanged(); } + getRouteDataValue(datafield: string): Observable { + return this.route.data.map((data) => data[datafield]).distinctUntilChanged(); + } + getQueryParamsWithPrefix(prefix: string): Observable { return this.route.queryParamMap .map((map) => { From d06e8d93dc2f3b3639fb45992233f3182d0a1ef5 Mon Sep 17 00:00:00 2001 From: lotte Date: Wed, 4 Jul 2018 10:14:43 +0200 Subject: [PATCH 022/205] small fixes --- .../search-fixed-filter.service.ts | 48 ++++++++++--------- .../data/search-response-parsing.service.ts | 2 +- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index f562f4c680..252e4b101e 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -11,6 +11,7 @@ import { ResponseCacheService } from '../../../core/cache/response-cache.service import { ResponseParsingService } from '../../../core/data/parsing.service'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { FilteredDiscoveryPageResponseParsingService } from '../../../core/data/filtered-discovery-page-response-parsing.service'; +import { hasValue } from '../../../shared/empty.util'; @Injectable() export class SearchFixedFilterService { @@ -23,31 +24,34 @@ export class SearchFixedFilterService { } - getQueryByFilterName(filterName?: string): Observable { - const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( - map((url: string) => { - url += ('/' + filterName); - const request = new GetRequest(this.requestService.generateRequestId(), url); - return Object.assign(request, { - getResponseParser(): GenericConstructor { - return FilteredDiscoveryPageResponseParsingService; - } - }); - }), - ); + getQueryByFilterName(filterName: string): Observable { + if (hasValue(filterName)) { + const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( + map((url: string) => { + url += ('/' + filterName); + const request = new GetRequest(this.requestService.generateRequestId(), url); + return Object.assign(request, { + getResponseParser(): GenericConstructor { + return FilteredDiscoveryPageResponseParsingService; + } + }); + }), + ); - const responseCacheObs = requestObs.pipe( - flatMap((request: RestRequest) => this.responseCache.get(request.href)) - ); + const responseCacheObs = requestObs.pipe( + flatMap((request: RestRequest) => this.responseCache.get(request.href)) + ); - // get search results from response cache - const filterQuery: Observable = responseCacheObs.pipe( - map((entry: ResponseCacheEntry) => entry.response), - map((response: FilteredDiscoveryQueryResponse) => - response.filterQuery - )); + // get search results from response cache + const filterQuery: Observable = responseCacheObs.pipe( + map((entry: ResponseCacheEntry) => entry.response), + map((response: FilteredDiscoveryQueryResponse) => + response.filterQuery + )); - return filterQuery; + return filterQuery; + } + return Observable.of(undefined); } } diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index ebefbe4ba4..6b0e8d050a 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -16,7 +16,7 @@ export class SearchResponseParsingService implements ResponseParsingService { } parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { - const payload = data.payload._embedded.searchResult; + const payload = data.payload/*._embedded.searchResult*/; const hitHighlights = payload._embedded.objects .map((object) => object.hitHighlights) .map((hhObject) => { From cef2ea827b76cfeb2f3d320b07645d6247534401 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 6 Jul 2018 13:59:02 +0200 Subject: [PATCH 023/205] finished entity search endpoints --- resources/i18n/en.json | 18 +++++++++++++++ .../filtered-search-page.guard.ts | 18 +++++++++++++++ .../search-filter.service.spec.ts | 9 +++++++- .../search-fixed-filter.service.ts | 3 +++ .../search-page-routing.module.ts | 3 ++- .../+search-page/search-page.component.html | 2 +- src/app/+search-page/search-page.component.ts | 3 ++- src/app/+search-page/search-page.module.ts | 4 +++- src/app/+search-page/search-result.model.ts | 1 - .../search-results.component.html | 2 +- .../search-results.component.ts | 12 +++++++++- src/app/core/core.module.ts | 2 ++ ...discovery-page-response-parsing.service.ts | 2 ++ src/app/shared/route.service.spec.ts | 9 ++++++-- src/app/shared/route.service.ts | 22 ++++++++++++++++--- 15 files changed, 97 insertions(+), 13 deletions(-) create mode 100644 src/app/+search-page/filtered-search-page.guard.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 5e30d08ed0..afd1c48e94 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -145,6 +145,24 @@ } }, "search": { + "journal": { + "title": "DSpace Angular :: Journal Search", + "results": { + "head": "Journal Search Results" + } + }, + "person": { + "title": "DSpace Angular :: Person Search", + "results": { + "head": "Person Search Results" + } + }, + "publication": { + "title": "DSpace Angular :: Publication Search", + "results": { + "head": "Publication Search Results" + } + }, "title": "DSpace Angular :: Search", "description": "", "form": { diff --git a/src/app/+search-page/filtered-search-page.guard.ts b/src/app/+search-page/filtered-search-page.guard.ts new file mode 100644 index 0000000000..7d022b81da --- /dev/null +++ b/src/app/+search-page/filtered-search-page.guard.ts @@ -0,0 +1,18 @@ +import { Injectable } from '@angular/core'; +import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +@Injectable() + +export class FilteredSearchPageGuard implements CanActivate { + canActivate( + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot): Observable | Promise | boolean { + const filter = route.params.filter; + + const newTitle = route.data.title + filter + '.title'; + + route.data = { title: newTitle }; + return true; + } +} diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts index 26eb961c53..6a112fef1c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts @@ -10,6 +10,7 @@ import { import { SearchFiltersState } from './search-filter.reducer'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { FilterType } from '../../search-service/filter-type.model'; +import { SearchFixedFilterService } from './search-fixed-filter.service'; describe('SearchFilterService', () => { let service: SearchFilterService; @@ -21,6 +22,12 @@ describe('SearchFilterService', () => { isOpenByDefault: false, pageSize: 2 }); + + const mockFixedFilterService: SearchFixedFilterService = { + getQueryByFilterName: (filter: string) => { + return Observable.of(undefined) + } + } as SearchFixedFilterService const value1 = 'random value'; // const value2 = 'another value'; const store: Store = jasmine.createSpyObj('store', { @@ -50,7 +57,7 @@ describe('SearchFilterService', () => { }; beforeEach(() => { - service = new SearchFilterService(store, routeServiceStub); + service = new SearchFilterService(store, routeServiceStub, mockFixedFilterService); }); describe('when the initialCollapse method is triggered', () => { diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 252e4b101e..fe46417e59 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -12,6 +12,7 @@ import { ResponseParsingService } from '../../../core/data/parsing.service'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { FilteredDiscoveryPageResponseParsingService } from '../../../core/data/filtered-discovery-page-response-parsing.service'; import { hasValue } from '../../../shared/empty.util'; +import { configureRequest } from '../../../core/shared/operators'; @Injectable() export class SearchFixedFilterService { @@ -30,12 +31,14 @@ export class SearchFixedFilterService { map((url: string) => { url += ('/' + filterName); const request = new GetRequest(this.requestService.generateRequestId(), url); + console.log(url); return Object.assign(request, { getResponseParser(): GenericConstructor { return FilteredDiscoveryPageResponseParsingService; } }); }), + configureRequest(this.requestService) ); const responseCacheObs = requestObs.pipe( diff --git a/src/app/+search-page/search-page-routing.module.ts b/src/app/+search-page/search-page-routing.module.ts index 84cb0c31bb..8c138c0d52 100644 --- a/src/app/+search-page/search-page-routing.module.ts +++ b/src/app/+search-page/search-page-routing.module.ts @@ -3,12 +3,13 @@ import { RouterModule } from '@angular/router'; import { SearchPageComponent } from './search-page.component'; import { FilteredSearchPageComponent } from './filtered-search-page.component'; +import { FilteredSearchPageGuard } from './filtered-search-page.guard'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', component: SearchPageComponent, data: { title: 'search.title' } }, - { path: ':filter', component: FilteredSearchPageComponent, data: { title: 'search.title.:filter' } } + { path: ':filter', component: FilteredSearchPageComponent, canActivate: [FilteredSearchPageGuard], data: { title: 'search.' }} ]) ] }) diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 1a1f379920..478403388e 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -30,7 +30,7 @@
+ [searchConfig]="searchOptions$ | async" [sortConfig]="sortConfig" [fixedFilter]="fixedFilter | async">
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index b38386d44c..e347688072 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -47,7 +47,7 @@ export class SearchPageComponent implements OnInit { query: '', scope: '' }; - title; + fixedFilter; constructor(protected service: SearchService, protected communityService: CommunityDataService, @@ -68,6 +68,7 @@ export class SearchPageComponent implements OnInit { this.resultsRD$ = this.searchOptions$.pipe( flatMap((searchOptions) => this.service.search(searchOptions)) ); + this.fixedFilter = this.routeService.getRouteParameterValue('filter'); } public closeSidebar(): void { diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 206d18267d..25d4b561c3 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -23,6 +23,7 @@ import { SearchFacetFilterComponent } from './search-filters/search-filter/searc import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { FilteredSearchPageComponent } from './filtered-search-page.component'; import { SearchFixedFilterService } from './search-filters/search-filter/search-fixed-filter.service'; +import { FilteredSearchPageGuard } from './filtered-search-page.guard'; const effects = [ SearchSidebarEffects @@ -57,7 +58,8 @@ const effects = [ SearchService, SearchSidebarService, SearchFilterService, - SearchFixedFilterService + SearchFixedFilterService, + FilteredSearchPageGuard ], entryComponents: [ ItemSearchResultListElementComponent, diff --git a/src/app/+search-page/search-result.model.ts b/src/app/+search-page/search-result.model.ts index 2298f453e1..cc2bd8cd58 100644 --- a/src/app/+search-page/search-result.model.ts +++ b/src/app/+search-page/search-result.model.ts @@ -1,6 +1,5 @@ import { DSpaceObject } from '../core/shared/dspace-object.model'; import { Metadatum } from '../core/shared/metadatum.model'; -import { hasNoValue, isEmpty } from '../shared/empty.util'; import { ListableObject } from '../shared/object-collection/shared/listable-object.model'; export class SearchResult implements ListableObject { diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index ed6fc18d9c..a9b54a4601 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,4 +1,4 @@ -

{{ 'search.results.head' | translate }}

+

{{ getTitleKey() | translate }}

{ let service: RouteService; @@ -28,12 +30,15 @@ describe('RouteService', () => { queryParamMap: Observable.of(convertToParamMap(paramObject)) }, }, + { + provide: Router, useClass: RouterStub + } ] }); })); beforeEach(() => { - service = new RouteService(TestBed.get(ActivatedRoute)); + service = new RouteService(TestBed.get(ActivatedRoute), TestBed.get(Router)); }); describe('hasQueryParam', () => { diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 3eb629e60c..f374f63432 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -1,11 +1,15 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { ActivatedRoute, Params, } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, Params, Router, } from '@angular/router'; +import { filter } from 'rxjs/operators'; @Injectable() export class RouteService { + params: Observable; + + constructor(private route: ActivatedRoute, private router: Router) { + this.subscribeToRouterParams(); - constructor(private route: ActivatedRoute) { } getQueryParameterValues(paramName: string): Observable { @@ -25,7 +29,7 @@ export class RouteService { } getRouteParameterValue(paramName: string): Observable { - return this.route.params.map((params) => params[paramName]).distinctUntilChanged(); + return this.params.map((params) => params[paramName]).distinctUntilChanged(); } getRouteDataValue(datafield: string): Observable { @@ -44,4 +48,16 @@ export class RouteService { return params; }).distinctUntilChanged(); } + + subscribeToRouterParams() { + this.router.events.pipe( + filter((event) => event instanceof NavigationEnd)) + .subscribe(() => { + let active = this.route; + while (active.firstChild) { + active = active.firstChild; + } + this.params = active.params; + }); + } } From b68ed37cb3ce5857b48662bfed81635ca1560612 Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 6 Jul 2018 14:07:11 +0200 Subject: [PATCH 024/205] 53029: removed console logs and small bugs --- .../search-filter/search-fixed-filter.service.ts | 1 - .../search-settings/search-settings.component.ts | 7 +++---- src/app/core/data/base-response-parsing.service.ts | 3 +-- .../filtered-discovery-page-response-parsing.service.ts | 2 -- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index fe46417e59..045c282fa3 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -31,7 +31,6 @@ export class SearchFixedFilterService { map((url: string) => { url += ('/' + filterName); const request = new GetRequest(this.requestService.generateRequestId(), url); - console.log(url); return Object.assign(request, { getResponseParser(): GenericConstructor { return FilteredDiscoveryPageResponseParsingService; diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index bd26663583..8c7925df9d 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -1,7 +1,6 @@ -import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { SetViewMode } from '../../shared/view-mode'; import { SearchService } from '../search-service/search.service'; -import { SearchOptions} from '../search-options.model'; import { SortDirection } from '../../core/cache/models/sort-options.model'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; @@ -64,7 +63,7 @@ export class SearchSettingsComponent implements OnInit { pageSize: value }) }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } reloadOrder(event: Event) { @@ -74,6 +73,6 @@ export class SearchSettingsComponent implements OnInit { sortDirection: value }) }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } } diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 3f64be7515..a9b399e827 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -6,14 +6,13 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList } from './paginated-list'; -import { NormalizedObject } from '../cache/models/normalized-object.model'; function isObjectLevel(halObj: any) { return isNotEmpty(halObj._links) && hasValue(halObj._links.self); } function isPaginatedResponse(halObj: any) { - return isNotEmpty(halObj.page) /** && hasValue(halObj._embedded)**/; + return isNotEmpty(halObj.page) /* && hasValue(halObj._embedded)*/; } /* tslint:disable:max-classes-per-file */ diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts index ec1f2fbe2c..ee87230214 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts @@ -18,8 +18,6 @@ export class FilteredDiscoveryPageResponseParsingService extends BaseResponsePar ) { super(); } parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { - console.log('test'); - const query = data.payload['discovery-query']; return new FilteredDiscoveryQueryResponse(query, data.statusCode); } From 46daefd16e761a12ef4275725c61fed38eadf347 Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 27 Aug 2018 14:36:04 +0200 Subject: [PATCH 025/205] 55407: updated home page --- resources/images/atmire-logo.svg | 37 +++++++++++++++++++ .../home-news/home-news.component.html | 13 +++---- src/app/footer/footer.component.html | 1 + src/app/footer/footer.component.scss | 4 ++ 4 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 resources/images/atmire-logo.svg diff --git a/resources/images/atmire-logo.svg b/resources/images/atmire-logo.svg new file mode 100644 index 0000000000..5a416693ce --- /dev/null +++ b/resources/images/atmire-logo.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/+home-page/home-news/home-news.component.html b/src/app/+home-page/home-news/home-news.component.html index ffc88be574..c7943bb7f5 100644 --- a/src/app/+home-page/home-news/home-news.component.html +++ b/src/app/+home-page/home-news/home-news.component.html @@ -2,19 +2,16 @@
- +
-

Welcome to DSpace

-

DSpace is an open source software platform that enables organisations to:

+

DSpace 7 entities prototype

+

This is the demo site of the prototype implementation of a flexible data model for DSpace 7:

    -
  • capture and describe digital material using a submission workflow module, or a variety of programmatic ingest options -
  • -
  • distribute an organisation's digital assets over the web through a search and retrieval system -
  • -
  • preserve digital assets over the long term
  • +
  • The functionality is governed and discussed by the DSpace 7 Entities Working Group, if you would like to participate, please visit the wiki
  • +
  • Documentation on this prototype implementation and links to the source code can be found here
diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index c7f41a07a3..fec75b2fd3 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -1,5 +1,6 @@
diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index d7cc3f1158..f958f9593f 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -48,5 +48,10 @@ [fields]="['person.identifier.firstname']" [label]="'person.page.firstname'"> +
diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index a1c5502307..36a71bd71a 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -40,5 +40,10 @@ [fields]="['project.identifier.keyword']" [label]="'project.page.keyword'">
+
From d8ce01b27a4f8871fa1301e2979be8b95addf7dc Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 18 Sep 2018 16:08:29 +0200 Subject: [PATCH 031/205] 55647: Intermediate FixedFilter fixes --- src/app/+search-page/filtered-search-page.component.ts | 5 +++-- src/app/+search-page/search-options.model.ts | 3 ++- src/app/+search-page/search-page.component.html | 3 ++- src/app/+search-page/search-page.component.ts | 7 ++++++- .../search-settings/search-settings.component.ts | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts index 648926e52a..e4a75e3481 100644 --- a/src/app/+search-page/filtered-search-page.component.ts +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -27,8 +27,9 @@ export class FilteredSearchPageComponent extends SearchPageComponent { protected sidebarService: SearchSidebarService, protected windowService: HostWindowService, protected filterService: SearchFilterService, - protected searchConfigService: SearchConfigurationService) { - super(service, sidebarService, windowService, filterService, searchConfigService); + protected searchConfigService: SearchConfigurationService, + protected routeService: RouteService) { + super(service, sidebarService, windowService, filterService, searchConfigService, routeService); } } diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index 7a4a810f80..69309bed66 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -16,11 +16,12 @@ export class SearchOptions { filters?: any; fixedFilter?: any; - constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) { + constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], fixedFilter?: any}) { this.scope = options.scope; this.query = options.query; this.dsoType = options.dsoType; this.filters = options.filters; + this.fixedFilter = options.fixedFilter; } /** diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 653f5e8cd4..aa7a117dcd 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -31,7 +31,8 @@ + [searchConfig]="searchOptions$ | async" + [fixedFilter]="fixedFilter | async"> diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 78e30c2968..c646318de3 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -16,6 +16,7 @@ import { hasValue } from '../shared/empty.util'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { getSucceededRemoteData } from '../core/shared/operators'; +import { RouteService } from '../shared/services/route.service'; /** * This component renders a simple item page. @@ -61,11 +62,14 @@ export class SearchPageComponent implements OnInit { */ sub: Subscription; + fixedFilter; + constructor(protected service: SearchService, protected sidebarService: SearchSidebarService, protected windowService: HostWindowService, protected filterService: SearchFilterService, - protected searchConfigService: SearchConfigurationService) { + protected searchConfigService: SearchConfigurationService, + protected routeService: RouteService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); } @@ -86,6 +90,7 @@ export class SearchPageComponent implements OnInit { this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) ); + this.fixedFilter = this.routeService.getRouteParameterValue('filter'); } /** diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 81e2366e39..cf9dea35f1 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -54,7 +54,7 @@ export class SearchSettingsComponent implements OnInit { }, queryParamsHandling: 'merge' }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } /** @@ -71,6 +71,6 @@ export class SearchSettingsComponent implements OnInit { }, queryParamsHandling: 'merge' }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } } From 8e3e826a5ffec3161fde6b28f0273d786dca0ccc Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Sep 2018 09:36:33 +0200 Subject: [PATCH 032/205] 55647: FixedFilter fixed --- .../search-configuration.service.ts | 25 +++++++++++++++-- src/app/shared/services/route.service.ts | 28 +++++++++---------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index b4c06e83f3..edd30ac383 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -13,6 +13,7 @@ import { Subscription } from 'rxjs/Subscription'; import { getSucceededRemoteData } from '../../core/shared/operators'; import { SearchFilter } from '../search-filter.model'; import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; +import { SearchFixedFilterService } from '../search-filters/search-filter/search-fixed-filter.service'; /** * Service that performs all actions that have to do with the current search configuration @@ -69,6 +70,7 @@ export class SearchConfigurationService implements OnDestroy { * @param {ActivatedRoute} route */ constructor(private routeService: RouteService, + private fixedFilterService: SearchFixedFilterService, private route: ActivatedRoute) { this.defaults .pipe(getSucceededRemoteData()) @@ -166,6 +168,14 @@ export class SearchConfigurationService implements OnDestroy { }); } + /** + * @returns {Observable} Emits the current fixed filter as a string + */ + getCurrentFixedFilter(): Observable { + const fixedFilter: Observable = this.routeService.getRouteParameterValue('filter'); + return fixedFilter.flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)); + } + /** * @returns {Observable} Emits the current active filters with their values as they are displayed in the frontend URL */ @@ -183,7 +193,8 @@ export class SearchConfigurationService implements OnDestroy { this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), - this.getFiltersPart() + this.getFiltersPart(), + this.getFixedFilterPart() ).subscribe((update) => { const currentValue: SearchOptions = this.searchOptions.getValue(); const updatedValue: SearchOptions = Object.assign(currentValue, update); @@ -203,7 +214,8 @@ export class SearchConfigurationService implements OnDestroy { this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), - this.getFiltersPart() + this.getFiltersPart(), + this.getFixedFilterPart() ).subscribe((update) => { const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue(); const updatedValue: PaginatedSearchOptions = Object.assign(currentValue, update); @@ -289,4 +301,13 @@ export class SearchConfigurationService implements OnDestroy { return { filters } }); } + + /** + * @returns {Observable} Emits the current fixed filter as a partial SearchOptions object + */ + private getFixedFilterPart(): Observable { + return this.getCurrentFixedFilter().map((fixedFilter) => { + return { fixedFilter } + }); + } } diff --git a/src/app/shared/services/route.service.ts b/src/app/shared/services/route.service.ts index f374f63432..d008c11c43 100644 --- a/src/app/shared/services/route.service.ts +++ b/src/app/shared/services/route.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { ActivatedRoute, NavigationEnd, Params, Router, } from '@angular/router'; -import { filter } from 'rxjs/operators'; +import { ActivatedRoute, ActivationEnd, NavigationEnd, Params, Router, } from '@angular/router'; +import { filter, flatMap, map } from 'rxjs/operators'; @Injectable() export class RouteService { @@ -13,19 +13,19 @@ export class RouteService { } getQueryParameterValues(paramName: string): Observable { - return this.route.queryParamMap.map((map) => [...map.getAll(paramName)]).distinctUntilChanged(); + return this.route.queryParamMap.map((paramMap) => [...paramMap.getAll(paramName)]).distinctUntilChanged(); } getQueryParameterValue(paramName: string): Observable { - return this.route.queryParamMap.map((map) => map.get(paramName)).distinctUntilChanged(); + return this.route.queryParamMap.map((paramMap) => paramMap.get(paramName)).distinctUntilChanged(); } hasQueryParam(paramName: string): Observable { - return this.route.queryParamMap.map((map) => map.has(paramName)).distinctUntilChanged(); + return this.route.queryParamMap.map((paramMap) => paramMap.has(paramName)).distinctUntilChanged(); } hasQueryParamWithValue(paramName: string, paramValue: string): Observable { - return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1).distinctUntilChanged(); + return this.route.queryParamMap.map((paramMap) => paramMap.getAll(paramName).indexOf(paramValue) > -1).distinctUntilChanged(); } getRouteParameterValue(paramName: string): Observable { @@ -38,26 +38,26 @@ export class RouteService { getQueryParamsWithPrefix(prefix: string): Observable { return this.route.queryParamMap - .map((map) => { + .map((paramMap) => { const params = {}; - map.keys + paramMap.keys .filter((key) => key.startsWith(prefix)) .forEach((key) => { - params[key] = [...map.getAll(key)]; + params[key] = [...paramMap.getAll(key)]; }); return params; }).distinctUntilChanged(); } subscribeToRouterParams() { - this.router.events.pipe( - filter((event) => event instanceof NavigationEnd)) - .subscribe(() => { + this.params = this.router.events.pipe( + flatMap((event) => { let active = this.route; while (active.firstChild) { active = active.firstChild; } - this.params = active.params; - }); + return active.params; + }) + ); } } From 4af12c21865bdab9a6ce9baf85518b2ce2a22331 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 19 Sep 2018 14:26:15 +0200 Subject: [PATCH 033/205] 55647: Related publications on person pages --- src/app/+item-page/item-page.module.ts | 4 ++- .../person/person-page-fields.component.html | 11 +++++--- .../person/person-page-fields.component.ts | 9 ++++++- .../filtered-search-page.component.ts | 18 ++++++++++++- .../search-fixed-filter.service.ts | 4 +++ .../+search-page/search-page.component.html | 6 ++--- src/app/+search-page/search-page.component.ts | 26 +++++++++++++++---- src/app/+search-page/search-page.module.ts | 3 +++ .../search-configuration.service.ts | 22 +++++++++++++--- 9 files changed, 84 insertions(+), 19 deletions(-) diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index ce859e0168..cbc8dde575 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -28,12 +28,14 @@ import { RelatedEntitiesComponent } from './simple/related-entities/related-enti import { JournalPageFieldsComponent } from './simple/entity-types/journal/journal-page-fields.component'; import { JournalIssuePageFieldsComponent } from './simple/entity-types/journal-issue/journal-issue-page-fields.component'; import { JournalVolumePageFieldsComponent } from './simple/entity-types/journal-volume/journal-volume-page-fields.component'; +import { SearchPageModule } from '../+search-page/search-page.module'; @NgModule({ imports: [ CommonModule, SharedModule, - ItemPageRoutingModule + ItemPageRoutingModule, + SearchPageModule ], declarations: [ ItemPageComponent, diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index d7cc3f1158..9b7d09a338 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -24,10 +24,6 @@
- - @@ -49,4 +45,11 @@ [label]="'person.page.firstname'">
+
+ + +
diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index 61b2d87389..71e1fe8640 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; @rendersEntityType('Person', ElementViewMode.Full) @Component({ @@ -20,10 +21,13 @@ export class PersonPageFieldsComponent extends EntityPageFieldsComponent { publications$: Observable; projects$: Observable; orgUnits$: Observable; + fixedFilter$: Observable; + fixedFilterQuery: string; constructor( @Inject(ITEM) public item: Item, - private ids: ItemDataService + private ids: ItemDataService, + private fixedFilterService: SearchFixedFilterService ) { super(item); } @@ -44,5 +48,8 @@ export class PersonPageFieldsComponent extends EntityPageFieldsComponent { filterRelationsByTypeLabel('isOrgUnitOfPerson'), relationsToItems(this.item.id, this.ids) ); + + this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); + this.fixedFilter$ = Observable.of('publication'); } } diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts index e4a75e3481..5feae2171e 100644 --- a/src/app/+search-page/filtered-search-page.component.ts +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -4,10 +4,15 @@ import { SearchFilterService } from './search-filters/search-filter/search-filte import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchPageComponent } from './search-page.component'; -import { ChangeDetectionStrategy, Component, Injectable } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injectable, Input } from '@angular/core'; import { pushInOut } from '../shared/animations/push'; import { RouteService } from '../shared/services/route.service'; import { SearchConfigurationService } from './search-service/search-configuration.service'; +import { switchMap, tap } from 'rxjs/operators'; +import { getSucceededRemoteData } from '../core/shared/operators'; +import { Observable } from 'rxjs/Observable'; +import { PaginatedSearchOptions } from './paginated-search-options.model'; +import { isNotEmpty } from '../shared/empty.util'; /** * This component renders a simple item page. @@ -23,6 +28,12 @@ import { SearchConfigurationService } from './search-service/search-configuratio export class FilteredSearchPageComponent extends SearchPageComponent { + /** + * The actual query for the fixed filter. + * If empty, the query will be determined by the route parameter called 'filter' + */ + @Input() fixedFilterQuery: string; + constructor(protected service: SearchService, protected sidebarService: SearchSidebarService, protected windowService: HostWindowService, @@ -32,4 +43,9 @@ export class FilteredSearchPageComponent extends SearchPageComponent { super(service, sidebarService, windowService, filterService, searchConfigService, routeService); } + protected getSearchOptions(): Observable { + this.searchConfigService.updateFixedFilter(this.fixedFilterQuery); + return this.searchConfigService.paginatedSearchOptions; + } + } diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 144bac98c4..66fc82023a 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -56,4 +56,8 @@ export class SearchFixedFilterService { return Observable.of(undefined); } + getQueryByRelations(relationType: string, itemUUID: string): string { + return `query=relation.${relationType}:${itemUUID}`; + } + } diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index aa7a117dcd..0dec357c07 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -4,13 +4,13 @@ id="search-sidebar" [resultCount]="(resultsRD$ | async)?.payload.totalElements">
- - +
+ [fixedFilter]="fixedFilter$ | async">
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index c646318de3..6948653d18 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { flatMap, switchMap, } from 'rxjs/operators'; import { PaginatedList } from '../core/data/paginated-list'; @@ -12,7 +12,7 @@ import { SearchResult } from './search-result.model'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { Subscription } from 'rxjs/Subscription'; -import { hasValue } from '../shared/empty.util'; +import { hasValue, isNotEmpty } from '../shared/empty.util'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { getSucceededRemoteData } from '../core/shared/operators'; @@ -62,7 +62,17 @@ export class SearchPageComponent implements OnInit { */ sub: Subscription; - fixedFilter; + /** + * Whether or not the search bar should be visible + */ + @Input() + searchEnabled = true; + + /** + * The currently applied filter (determines title of search) + */ + @Input() + fixedFilter$: Observable; constructor(protected service: SearchService, protected sidebarService: SearchSidebarService, @@ -81,7 +91,7 @@ export class SearchPageComponent implements OnInit { * If something changes, update the list of scopes for the dropdown */ ngOnInit(): void { - this.searchOptions$ = this.searchConfigService.paginatedSearchOptions; + this.searchOptions$ = this.getSearchOptions(); this.sub = this.searchOptions$ .switchMap((options) => this.service.search(options).pipe(getSucceededRemoteData())) .subscribe((results) => { @@ -90,7 +100,13 @@ export class SearchPageComponent implements OnInit { this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) ); - this.fixedFilter = this.routeService.getRouteParameterValue('filter'); + if (!isNotEmpty(this.fixedFilter$)) { + this.fixedFilter$ = this.routeService.getRouteParameterValue('filter'); + } + } + + protected getSearchOptions(): Observable { + return this.searchConfigService.paginatedSearchOptions; } /** diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index fd9c2e5adc..658c32d27e 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -89,6 +89,9 @@ const effects = [ SearchTextFilterComponent, SearchHierarchyFilterComponent, SearchBooleanFilterComponent, + ], + exports: [ + FilteredSearchPageComponent ] }) diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index edd30ac383..a3ebf6b860 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -6,7 +6,7 @@ import { ActivatedRoute, Params } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { Injectable, OnDestroy } from '@angular/core'; import { RouteService } from '../../shared/services/route.service'; -import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { hasNoValue, hasValue, isEmpty, isNotEmpty, isNotEmptyOperator } from '../../shared/empty.util'; import { RemoteData } from '../../core/data/remote-data'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Subscription } from 'rxjs/Subscription'; @@ -14,6 +14,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; import { SearchFilter } from '../search-filter.model'; import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; import { SearchFixedFilterService } from '../search-filters/search-filter/search-fixed-filter.service'; +import { map } from 'rxjs/operators'; /** * Service that performs all actions that have to do with the current search configuration @@ -306,8 +307,21 @@ export class SearchConfigurationService implements OnDestroy { * @returns {Observable} Emits the current fixed filter as a partial SearchOptions object */ private getFixedFilterPart(): Observable { - return this.getCurrentFixedFilter().map((fixedFilter) => { - return { fixedFilter } - }); + return this.getCurrentFixedFilter().pipe( + isNotEmptyOperator(), + map((fixedFilter) => { + return { fixedFilter } + }) + ); + } + + public updateFixedFilter(fixedFilter: string) { + const currentPaginatedValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue(); + const updatedPaginatedValue: PaginatedSearchOptions = Object.assign(currentPaginatedValue, { fixedFilter: fixedFilter }); + this.paginatedSearchOptions.next(updatedPaginatedValue); + + const currentValue: SearchOptions = this.searchOptions.getValue(); + const updatedValue: SearchOptions = Object.assign(currentValue, { fixedFilter: fixedFilter }); + this.searchOptions.next(updatedValue); } } From 41c8577d8b4ff0a1610fc67a436b1525161ef0c2 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 24 Sep 2018 10:48:14 +0200 Subject: [PATCH 034/205] add missing filterservice to SearchConfigurationService spec --- .../search-service/search-configuration.service.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/app/+search-page/search-service/search-configuration.service.spec.ts b/src/app/+search-page/search-service/search-configuration.service.spec.ts index 9f2e6d5045..33d3dbef3b 100644 --- a/src/app/+search-page/search-service/search-configuration.service.spec.ts +++ b/src/app/+search-page/search-service/search-configuration.service.spec.ts @@ -23,17 +23,20 @@ describe('SearchConfigurationService', () => { const backendFilters = [new SearchFilter('f.author', ['another value']), new SearchFilter('f.date', ['[2013 TO 2018]'])]; - const spy = jasmine.createSpyObj('RouteService', { + const routeService = jasmine.createSpyObj('RouteService', { getQueryParameterValue: Observable.of(value1), getQueryParamsWithPrefix: Observable.of(prefixFilter) }); + const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', { + getQueryByFilterName: Observable.of(''), + }); + const activatedRoute: any = new ActivatedRouteStub(); beforeEach(() => { - service = new SearchConfigurationService(spy, activatedRoute); + service = new SearchConfigurationService(routeService, fixedFilterService, activatedRoute); }); - describe('when the scope is called', () => { beforeEach(() => { service.getCurrentScope(''); From 193d751b4f45e0f8786eb6ed3b3ac224311fd3dd Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 24 Sep 2018 15:23:44 +0200 Subject: [PATCH 035/205] 55647: Feedback 20/09 --- .../entity-types/person/person-page-fields.component.html | 5 +++-- src/app/+item-page/simple/item-page.component.scss | 8 ++++++++ src/app/+search-page/search-page.component.html | 7 ++++--- src/app/+search-page/search-page.component.ts | 6 ++++++ .../search-results/search-results.component.html | 2 +- .../search-results/search-results.component.ts | 1 + 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index 9b7d09a338..0af545ce82 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -45,11 +45,12 @@ [label]="'person.page.firstname'"> -
+
+ [searchEnabled]="false" + [sideBarWidth]="4">
diff --git a/src/app/+item-page/simple/item-page.component.scss b/src/app/+item-page/simple/item-page.component.scss index 50be6f5ad0..4c26cf08fb 100644 --- a/src/app/+item-page/simple/item-page.component.scss +++ b/src/app/+item-page/simple/item-page.component.scss @@ -1 +1,9 @@ @import '../../../styles/variables.scss'; +@import '../../../styles/mixins.scss'; + +@include media-breakpoint-down(md) { + .container { + width: 100%; + max-width: none; + } +} diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 0dec357c07..cbb82a59c6 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -1,9 +1,9 @@
- -
+
+ [fixedFilter]="fixedFilter$ | async" + [disableHeader]="!searchEnabled">
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 6948653d18..7b2ad5294a 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -68,6 +68,12 @@ export class SearchPageComponent implements OnInit { @Input() searchEnabled = true; + /** + * The width of the sidebar (bootstrap columns) + */ + @Input() + sideBarWidth = 3; + /** * The currently applied filter (determines title of search) */ diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index a9b54a4601..b685d0b28d 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,4 +1,4 @@ -

{{ getTitleKey() | translate }}

+

{{ getTitleKey() | translate }}

Date: Tue, 25 Sep 2018 14:10:52 +0200 Subject: [PATCH 036/205] 55725: item page bug fix --- resources/i18n/en.json | 3 ++- src/app/+item-page/full/full-item-page.component.ts | 4 ---- src/app/+item-page/item-page.resolver.ts | 1 + src/app/+item-page/simple/item-page.component.ts | 7 ------- src/app/core/data/data.service.ts | 2 +- src/app/core/metadata/metadata.service.ts | 9 ++++++--- src/app/core/shared/operators.ts | 4 ++-- .../pagination/pagination-component-options.model.ts | 4 +++- 8 files changed, 15 insertions(+), 19 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index cfba749f17..db7119c021 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -205,7 +205,8 @@ "f.dateIssued.min": "Start date", "f.dateIssued.max": "End date", "f.subject": "Subject", - "f.has_content_in_original_bundle": "Has files" + "f.has_content_in_original_bundle": "Has files", + "f.entityType": "Entity Type" }, "filter": { "show-more": "Show more", diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index df3ab16202..fd39631948 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -42,10 +42,6 @@ export class FullItemPageComponent extends ItemPageComponent implements OnInit { /*** AoT inheritance fix, will hopefully be resolved in the near future **/ ngOnInit(): void { super.ngOnInit(); - } - - initialize(params) { - super.initialize(params); this.metadata$ = this.itemRD$ .map((rd: RemoteData) => rd.payload) .filter((item: Item) => hasValue(item)) diff --git a/src/app/+item-page/item-page.resolver.ts b/src/app/+item-page/item-page.resolver.ts index c0f4147f47..7badca19bf 100644 --- a/src/app/+item-page/item-page.resolver.ts +++ b/src/app/+item-page/item-page.resolver.ts @@ -5,6 +5,7 @@ import { RemoteData } from '../core/data/remote-data'; import { getSucceededRemoteData } from '../core/shared/operators'; import { ItemDataService } from '../core/data/item-data.service'; import { Item } from '../core/shared/item.model'; +import { tap } from 'rxjs/operators'; /** * This class represents a resolver that requests a specific item before the route is activated diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 53603cd4f8..1ad67d287c 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -47,13 +47,6 @@ export class ItemPageComponent implements OnInit { ) { } ngOnInit(): void { - this.sub = this.route.params.subscribe((params) => { - this.initialize(params); - }); - - } - - initialize(params) { this.itemRD$ = this.route.data.map((data) => data.item); this.metadataService.processRemoteData(this.itemRD$); this.thumbnail$ = this.itemRD$ diff --git a/src/app/core/data/data.service.ts b/src/app/core/data/data.service.ts index c7588a5231..da547f0ae5 100644 --- a/src/app/core/data/data.service.ts +++ b/src/app/core/data/data.service.ts @@ -80,7 +80,7 @@ export abstract class DataService .map((endpoint: string) => this.getFindByIDHref(endpoint, id)); hrefObs - .first((href: string) => hasValue(href)) + .find((href: string) => hasValue(href)) .subscribe((href: string) => { const request = new FindByIDRequest(this.requestService.generateRequestId(), href, id); this.requestService.configure(request); diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index cf597195e9..6ee8ef96f0 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -62,8 +62,8 @@ export class MetadataService { route = this.getCurrentRoute(route); return { params: route.params, data: route.data }; }).subscribe((routeInfo: any) => { - this.processRouteChange(routeInfo); - }); + this.processRouteChange(routeInfo); + }); } public processRemoteData(remoteData: Observable>): void { @@ -269,7 +269,10 @@ export class MetadataService { private setCitationPdfUrlTag(): void { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item; - item.getFiles().filter((files) => isNotEmpty(files)).first().subscribe((bitstreams: Bitstream[]) => { + item.getFiles() + .first((files) => isNotEmpty(files)) + .catch((error) => { console.debug(error); return [] }) + .subscribe((bitstreams: Bitstream[]) => { for (const bitstream of bitstreams) { bitstream.format.first() .map((rd: RemoteData) => rd.payload) diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index fcb4c2c256..dd604393e2 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs/Observable'; -import { filter, first, flatMap, map, tap } from 'rxjs/operators'; +import { filter, find, first, flatMap, map, tap } from 'rxjs/operators'; import { hasValueOperator } from '../../shared/empty.util'; import { DSOSuccessResponse } from '../cache/response-cache.models'; import { ResponseCacheEntry } from '../cache/response-cache.reducer'; @@ -51,7 +51,7 @@ export const getRemoteDataPayload = () => export const getSucceededRemoteData = () => (source: Observable>): Observable> => - source.pipe(first((rd: RemoteData) => rd.hasSucceeded)); + source.pipe(find((rd: RemoteData) => rd.hasSucceeded)); export const toDSpaceObjectListRD = () => (source: Observable>>>): Observable>> => diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts index 30ed2becd2..e93e4c6130 100644 --- a/src/app/shared/pagination/pagination-component-options.model.ts +++ b/src/app/shared/pagination/pagination-component-options.model.ts @@ -1,5 +1,7 @@ import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; +import { Injectable } from '@angular/core'; +@Injectable() export class PaginationComponentOptions extends NgbPaginationConfig { /** * ID for the pagination instance. Only useful if you wish to @@ -19,4 +21,4 @@ export class PaginationComponentOptions extends NgbPaginationConfig { pageSize: number; -} +} \ No newline at end of file From 5067793ce47cb2be53f10304e2344d67324e73c4 Mon Sep 17 00:00:00 2001 From: lotte Date: Tue, 25 Sep 2018 14:14:36 +0200 Subject: [PATCH 037/205] removed unnecessary injectable annotation --- src/app/shared/pagination/pagination-component-options.model.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts index e93e4c6130..ac2c09bbb7 100644 --- a/src/app/shared/pagination/pagination-component-options.model.ts +++ b/src/app/shared/pagination/pagination-component-options.model.ts @@ -1,7 +1,5 @@ import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; -import { Injectable } from '@angular/core'; -@Injectable() export class PaginationComponentOptions extends NgbPaginationConfig { /** * ID for the pagination instance. Only useful if you wish to From 26b075882d79af08c07da186f4ebb05b8a16b39f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 19 Oct 2018 16:16:12 +0200 Subject: [PATCH 038/205] 56434: Fixed existing tests and added entity-list-element component tests --- .../search-page.component.spec.ts | 13 +- .../search-configuration.service.spec.ts | 3 +- .../search-configuration.service.ts | 7 +- .../entity-list-element.component.spec.ts | 108 ----------- .../journal-issue-list-element.component.html | 3 +- ...urnal-issue-list-element.component.spec.ts | 111 ++++++++++++ ...journal-volume-list-element.component.html | 3 +- ...rnal-volume-list-element.component.spec.ts | 111 ++++++++++++ .../journal-list-element.component.spec.ts | 82 +++++++++ .../orgunit-list-element.component.html | 2 +- .../orgunit-list-element.component.spec.ts | 82 +++++++++ .../person/person-list-element.component.html | 2 +- .../person-list-element.component.spec.ts | 82 +++++++++ .../project-list-element.component.html | 2 +- .../project-list-element.component.spec.ts | 82 +++++++++ ...publication-list-element.component.spec.ts | 169 ++++++++++++++++++ ...arch-result-list-element.component.spec.ts | 120 ------------- 17 files changed, 744 insertions(+), 238 deletions(-) delete mode 100644 src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts delete mode 100644 src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index 3ca18ce4c5..85283e7255 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -21,6 +21,7 @@ import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { RemoteData } from '../core/data/remote-data'; +import { RouteService } from '../shared/services/route.service'; describe('SearchPageComponent', () => { let comp: SearchPageComponent; @@ -62,6 +63,11 @@ describe('SearchPageComponent', () => { collapse: () => this.isCollapsed = Observable.of(true), expand: () => this.isCollapsed = Observable.of(false) }; + const routeServiceStub = { + getRouteParameterValue: () => { + return Observable.of(''); + } + }; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -92,7 +98,8 @@ describe('SearchPageComponent', () => { { provide: SearchFilterService, useValue: {} - }, { + }, + { provide: SearchConfigurationService, useValue: { paginatedSearchOptions: hot('a', { @@ -101,6 +108,10 @@ describe('SearchPageComponent', () => { getCurrentScope: (a) => Observable.of('test-id') } }, + { + provide: RouteService, + useValue: routeServiceStub + } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchPageComponent, { diff --git a/src/app/+search-page/search-service/search-configuration.service.spec.ts b/src/app/+search-page/search-service/search-configuration.service.spec.ts index 33d3dbef3b..6588da858f 100644 --- a/src/app/+search-page/search-service/search-configuration.service.spec.ts +++ b/src/app/+search-page/search-service/search-configuration.service.spec.ts @@ -25,7 +25,8 @@ describe('SearchConfigurationService', () => { const routeService = jasmine.createSpyObj('RouteService', { getQueryParameterValue: Observable.of(value1), - getQueryParamsWithPrefix: Observable.of(prefixFilter) + getQueryParamsWithPrefix: Observable.of(prefixFilter), + getRouteParameterValue: Observable.of('') }); const fixedFilterService = jasmine.createSpyObj('SearchFixedFilterService', { diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index a3ebf6b860..b2fb91417e 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -14,7 +14,7 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; import { SearchFilter } from '../search-filter.model'; import { DSpaceObjectType } from '../../core/shared/dspace-object-type.model'; import { SearchFixedFilterService } from '../search-filters/search-filter/search-fixed-filter.service'; -import { map } from 'rxjs/operators'; +import { flatMap, map } from 'rxjs/operators'; /** * Service that performs all actions that have to do with the current search configuration @@ -173,8 +173,9 @@ export class SearchConfigurationService implements OnDestroy { * @returns {Observable} Emits the current fixed filter as a string */ getCurrentFixedFilter(): Observable { - const fixedFilter: Observable = this.routeService.getRouteParameterValue('filter'); - return fixedFilter.flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)); + return this.routeService.getRouteParameterValue('filter').pipe( + flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)) + ); } /** diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts deleted file mode 100644 index 8ff375c1ea..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { EntityListElementComponent } from './entity-list-element.component'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { TruncatePipe } from '../../utils/truncate.pipe'; -import { Item } from '../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; - -let itemListElementComponent: EntityListElementComponent; -let fixture: ComponentFixture; - -const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), - metadata: [ - { - key: 'dc.contributor.author', - language: 'en_US', - value: 'Smith, Donald' - }, - { - key: 'dc.date.issued', - language: null, - value: '2015-06-26' - }] -}); -const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'dc.type', - language: null, - value: 'Article' - }] -}); - -describe('EntityListElementComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ EntityListElementComponent , TruncatePipe], - providers: [ - { provide: 'objectElementProvider', useValue: {mockItemWithAuthorAndDate}} - ], - - schemas: [ NO_ERRORS_SCHEMA ] - }).overrideComponent(EntityListElementComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(EntityListElementComponent); - itemListElementComponent = fixture.componentInstance; - - })); - - describe('When the item has an author', () => { - beforeEach(() => { - itemListElementComponent.object = mockItemWithAuthorAndDate; - fixture.detectChanges(); - }); - - it('should show the author paragraph', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); - expect(itemAuthorField).not.toBeNull(); - }); - }); - - describe('When the item has no author', () => { - beforeEach(() => { - itemListElementComponent.object = mockItemWithoutAuthorAndDate; - fixture.detectChanges(); - }); - - it('should not show the author paragraph', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); - expect(itemAuthorField).toBeNull(); - }); - }); - - describe('When the item has an issuedate', () => { - beforeEach(() => { - itemListElementComponent.object = mockItemWithAuthorAndDate; - fixture.detectChanges(); - }); - - it('should show the issuedate span', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-date')); - expect(itemAuthorField).not.toBeNull(); - }); - }); - - describe('When the item has no issuedate', () => { - beforeEach(() => { - itemListElementComponent.object = mockItemWithoutAuthorAndDate; - fixture.detectChanges(); - }); - - it('should not show the issuedate span', () => { - const dateField = fixture.debugElement.query(By.css('span.item-list-date')); - expect(dateField).toBeNull(); - }); - }); -}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html index 08011cf04e..3f73460f04 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html @@ -9,7 +9,8 @@ - + - diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts new file mode 100644 index 0000000000..58a27ffa8b --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts @@ -0,0 +1,111 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { JournalIssueListElementComponent } from './journal-issue-list-element.component'; + +let journalIssueListElementComponent: JournalIssueListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'journalvolume.identifier.volume', + language: 'en_US', + value: '1234' + }, + { + key: 'journalissue.identifier.number', + language: 'en_US', + value: '5678' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('JournalIssueListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JournalIssueListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(JournalIssueListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalIssueListElementComponent); + journalIssueListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has a journal identifier', () => { + beforeEach(() => { + journalIssueListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the journal issues span', () => { + const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues')); + expect(journalIdentifierField).not.toBeNull(); + }); + }); + + describe('When the item has no journal identifier', () => { + beforeEach(() => { + journalIssueListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the journal issues span', () => { + const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues')); + expect(journalIdentifierField).toBeNull(); + }); + }); + + describe('When the item has a journal number', () => { + beforeEach(() => { + journalIssueListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the journal issue numbers span', () => { + const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers')); + expect(journalNumberField).not.toBeNull(); + }); + }); + + describe('When the item has no journal number', () => { + beforeEach(() => { + journalIssueListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the journal issue numbers span', () => { + const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers')); + expect(journalNumberField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html index b071179950..ec7acf1087 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html @@ -10,7 +10,8 @@ - + () diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts new file mode 100644 index 0000000000..9c7eb7a029 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts @@ -0,0 +1,111 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { JournalVolumeListElementComponent } from './journal-volume-list-element.component'; + +let journalVolumeListElementComponent: JournalVolumeListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'journal.title', + language: 'en_US', + value: 'This is just another journal title' + }, + { + key: 'journalvolume.identifier.volume', + language: 'en_US', + value: '1234' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('JournalVolumeListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JournalVolumeListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(JournalVolumeListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalVolumeListElementComponent); + journalVolumeListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has a journal title', () => { + beforeEach(() => { + journalVolumeListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the journal title span', () => { + const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes')); + expect(journalTitleField).not.toBeNull(); + }); + }); + + describe('When the item has no journal title', () => { + beforeEach(() => { + journalVolumeListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the journal title span', () => { + const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes')); + expect(journalTitleField).toBeNull(); + }); + }); + + describe('When the item has a journal identifier', () => { + beforeEach(() => { + journalVolumeListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the journal identifiers span', () => { + const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers')); + expect(journalIdentifierField).not.toBeNull(); + }); + }); + + describe('When the item has no journal identifier', () => { + beforeEach(() => { + journalVolumeListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the journal identifiers span', () => { + const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers')); + expect(journalIdentifierField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts new file mode 100644 index 0000000000..bcf6cb7058 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts @@ -0,0 +1,82 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { JournalListElementComponent } from './journal-list-element.component'; + +let journalListElementComponent: JournalListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'journal.identifier.issn', + language: 'en_US', + value: '1234' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('JournalListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ JournalListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(JournalListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalListElementComponent); + journalListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has an issn', () => { + beforeEach(() => { + journalListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the journals span', () => { + const issnField = fixture.debugElement.query(By.css('span.item-list-journals')); + expect(issnField).not.toBeNull(); + }); + }); + + describe('When the item has no issn', () => { + beforeEach(() => { + journalListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the journals span', () => { + const issnField = fixture.debugElement.query(By.css('span.item-list-journals')); + expect(issnField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html index 824a90a3de..0a07901abb 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html @@ -5,7 +5,7 @@ + class="item-list-orgunit-description"> diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts new file mode 100644 index 0000000000..2bc69e94fd --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts @@ -0,0 +1,82 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { OrgUnitListElementComponent } from './orgunit-list-element.component'; + +let orgUnitListElementComponent: OrgUnitListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'orgunit.identifier.description', + language: 'en_US', + value: 'A description about the OrgUnit' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('OrgUnitListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ OrgUnitListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(OrgUnitListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(OrgUnitListElementComponent); + orgUnitListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has an orgunit description', () => { + beforeEach(() => { + orgUnitListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the description span', () => { + const orgunitDescriptionField = fixture.debugElement.query(By.css('span.item-list-orgunit-description')); + expect(orgunitDescriptionField).not.toBeNull(); + }); + }); + + describe('When the item has no orgunit description', () => { + beforeEach(() => { + orgUnitListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the description span', () => { + const orgunitDescriptionField = fixture.debugElement.query(By.css('span.item-list-orgunit-description')); + expect(orgunitDescriptionField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html index 81e9300bb3..803674b56e 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html @@ -5,7 +5,7 @@ + class="item-list-job-title"> diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts new file mode 100644 index 0000000000..60910474fd --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts @@ -0,0 +1,82 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { PersonListElementComponent } from './person-list-element.component'; + +let personListElementComponent: PersonListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'person.identifier.jobtitle', + language: 'en_US', + value: 'Developer' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('PersonListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PersonListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(PersonListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PersonListElementComponent); + personListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has a job title', () => { + beforeEach(() => { + personListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the job title span', () => { + const jobTitleField = fixture.debugElement.query(By.css('span.item-list-job-title')); + expect(jobTitleField).not.toBeNull(); + }); + }); + + describe('When the item has no job title', () => { + beforeEach(() => { + personListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the job title span', () => { + const jobTitleField = fixture.debugElement.query(By.css('span.item-list-job-title')); + expect(jobTitleField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html index 7eafd91317..e1d7814f40 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html @@ -5,7 +5,7 @@ + class="item-list-status"> diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts new file mode 100644 index 0000000000..e0cba5f11e --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts @@ -0,0 +1,82 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ProjectListElementComponent } from './project-list-element.component'; + +let projectListElementComponent: ProjectListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'project.identifier.status', + language: 'en_US', + value: 'A status about the project' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('ProjectListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ProjectListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(ProjectListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ProjectListElementComponent); + projectListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has a status', () => { + beforeEach(() => { + projectListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the status span', () => { + const statusField = fixture.debugElement.query(By.css('span.item-list-status')); + expect(statusField).not.toBeNull(); + }); + }); + + describe('When the item has no status', () => { + beforeEach(() => { + projectListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the status span', () => { + const statusField = fixture.debugElement.query(By.css('span.item-list-status')); + expect(statusField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts new file mode 100644 index 0000000000..0e96999c3f --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts @@ -0,0 +1,169 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { Observable } from 'rxjs/Observable'; +import { PublicationListElementComponent } from './publication-list-element.component'; +import { Item } from '../../../../../core/shared/item.model'; +import { TruncatePipe } from '../../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../../truncatable/truncatable.service'; +import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; + +let publicationListElementComponent: PublicationListElementComponent; +let fixture: ComponentFixture; + +const mockItemWithMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }, + { + key: 'dc.contributor.author', + language: 'en_US', + value: 'Smith, Donald' + }, + { + key: 'dc.publisher', + language: 'en_US', + value: 'Atmire' + }, + { + key: 'dc.date.issued', + language: null, + value: '2015-06-26' + }, + { + key: 'dc.description.abstract', + language: 'en_US', + value: 'This is the abstract' + }] +}); +const mockItemWithoutMetadata: Item = Object.assign(new Item(), { + bitstreams: Observable.of({}), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'This is just another title' + }] +}); + +describe('PublicationListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ PublicationListElementComponent , TruncatePipe], + providers: [ + { provide: ITEM, useValue: mockItemWithMetadata}, + { provide: TruncatableService, useValue: {} } + ], + + schemas: [ NO_ERRORS_SCHEMA ] + }).overrideComponent(PublicationListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PublicationListElementComponent); + publicationListElementComponent = fixture.componentInstance; + + })); + + describe('When the item has an author', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the author paragraph', () => { + const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); + expect(itemAuthorField).not.toBeNull(); + }); + }); + + describe('When the item has no author', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the author paragraph', () => { + const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); + expect(itemAuthorField).toBeNull(); + }); + }); + + describe('When the item has a publisher', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the publisher span', () => { + const publisherField = fixture.debugElement.query(By.css('span.item-list-publisher')); + expect(publisherField).not.toBeNull(); + }); + }); + + describe('When the item has no publisher', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the publisher span', () => { + const publisherField = fixture.debugElement.query(By.css('span.item-list-publisher')); + expect(publisherField).toBeNull(); + }); + }); + + describe('When the item has an issuedate', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the issuedate span', () => { + const dateField = fixture.debugElement.query(By.css('span.item-list-date')); + expect(dateField).not.toBeNull(); + }); + }); + + describe('When the item has no issuedate', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the issuedate span', () => { + const dateField = fixture.debugElement.query(By.css('span.item-list-date')); + expect(dateField).toBeNull(); + }); + }); + + describe('When the item has an abstract', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithMetadata; + fixture.detectChanges(); + }); + + it('should show the abstract span', () => { + const abstractField = fixture.debugElement.query(By.css('div.item-list-abstract')); + expect(abstractField).not.toBeNull(); + }); + }); + + describe('When the item has no abstract', () => { + beforeEach(() => { + publicationListElementComponent.item = mockItemWithoutMetadata; + fixture.detectChanges(); + }); + + it('should not show the abstract span', () => { + const abstractField = fixture.debugElement.query(By.css('div.item-list-abstract')); + expect(abstractField).toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts deleted file mode 100644 index f492c58483..0000000000 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { ItemSearchResultListElementComponent } from './item-search-result-list-element.component'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { Observable } from 'rxjs/Observable'; -import { NO_ERRORS_SCHEMA, ChangeDetectionStrategy } from '@angular/core'; -import { By } from '@angular/platform-browser'; -import { TruncatePipe } from '../../../utils/truncate.pipe'; -import { Item } from '../../../../core/shared/item.model'; -import { TruncatableService } from '../../../truncatable/truncatable.service'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; - -let itemSearchResultListElementComponent: ItemSearchResultListElementComponent; -let fixture: ComponentFixture; - -const truncatableServiceStub: any = { - isCollapsed: (id: number) => Observable.of(true), -}; - -const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult(); -mockItemWithAuthorAndDate.hitHighlights = []; -mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { - bitstreams: Observable.of({}), - metadata: [ - { - key: 'dc.contributor.author', - language: 'en_US', - value: 'Smith, Donald' - }, - { - key: 'dc.date.issued', - language: null, - value: '2015-06-26' - }] -}); - -const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult(); -mockItemWithoutAuthorAndDate.hitHighlights = []; -mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), { - bitstreams: Observable.of({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'dc.type', - language: null, - value: 'Article' - }] -}); - -describe('ItemSearchResultListElementComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [NoopAnimationsModule], - declarations: [ItemSearchResultListElementComponent, TruncatePipe], - providers: [ - { provide: TruncatableService, useValue: truncatableServiceStub }, - { provide: 'objectElementProvider', useValue: (mockItemWithoutAuthorAndDate) } - ], - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ItemSearchResultListElementComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(ItemSearchResultListElementComponent); - itemSearchResultListElementComponent = fixture.componentInstance; - })); - - describe('When the item has an author', () => { - beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; - fixture.detectChanges(); - }); - - it('should show the author paragraph', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); - expect(itemAuthorField).not.toBeNull(); - }); - }); - - describe('When the item has no author', () => { - beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; - fixture.detectChanges(); - }); - - it('should not show the author paragraph', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); - expect(itemAuthorField).toBeNull(); - }); - }); - - describe('When the item has an issuedate', () => { - beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; - fixture.detectChanges(); - }); - - it('should show the issuedate span', () => { - const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-date')); - expect(itemAuthorField).not.toBeNull(); - }); - }); - - describe('When the item has no issuedate', () => { - beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; - fixture.detectChanges(); - }); - - it('should not show the issuedate span', () => { - const dateField = fixture.debugElement.query(By.css('span.item-list-date')); - expect(dateField).toBeNull(); - }); - }); -}); From eab6698f9fe9386a4385e7d74f49ee0766e087c8 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 22 Oct 2018 16:50:48 +0200 Subject: [PATCH 039/205] 56434: Entity-page-fields component tests --- .../journal-issue-page-fields.component.html | 8 +- ...ournal-issue-page-fields.component.spec.ts | 93 +++++++++++++++ .../journal-issue-page-fields.component.ts | 19 +-- .../journal-volume-page-fields.component.html | 6 +- ...urnal-volume-page-fields.component.spec.ts | 88 ++++++++++++++ .../journal-volume-page-fields.component.ts | 19 +-- .../orgunit-page-fields.component.html | 10 +- .../orgunit-page-fields.component.spec.ts | 98 ++++++++++++++++ .../orgunit/orgunit-page-fields.component.ts | 27 +++-- .../person/person-page-fields.component.html | 14 +-- .../person-page-fields.component.spec.ts | 110 ++++++++++++++++++ .../person/person-page-fields.component.ts | 31 ++--- .../project-page-fields.component.html | 10 +- .../project-page-fields.component.spec.ts | 98 ++++++++++++++++ .../project/project-page-fields.component.ts | 27 +++-- 15 files changed, 580 insertions(+), 78 deletions(-) create mode 100644 src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts create mode 100644 src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts create mode 100644 src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts create mode 100644 src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts create mode 100644 src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html index 73fee41f8f..9a7262334b 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html @@ -6,11 +6,11 @@ - - @@ -24,11 +24,11 @@ [entities]="publications$ | async" [label]="'relationships.isPublicationOfJournalIssue' | translate"> - - diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts new file mode 100644 index 0000000000..adf5529212 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts @@ -0,0 +1,93 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { JournalIssuePageFieldsComponent } from './journal-issue-page-fields.component'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; + +let comp: JournalIssuePageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'journalissue.identifier.number', + language: 'en_US', + value: '1234' + }, + { + key: 'journalissue.issuedate', + language: 'en_US', + value: '2018' + }, + { + key: 'journalissue.identifier.description', + language: 'en_US', + value: 'desc' + }, + { + key: 'journalissue.identifier.keyword', + language: 'en_US', + value: 'keyword' + }] +}); + +describe('JournalIssuePageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [JournalIssuePageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalIssuePageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalIssuePageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts index eab4f14fa3..33b79a639a 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('JournalIssue', ElementViewMode.Full) @Component({ @@ -29,13 +30,15 @@ export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { ngOnInit(): void { super.ngOnInit(); - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isJournalVolumeOfIssue'), - relationsToItems(this.item.id, this.ids) - ); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPublicationOfJournalIssue'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.volumes$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isJournalVolumeOfIssue'), + relationsToItems(this.item.id, this.ids) + ); + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfJournalIssue'), + relationsToItems(this.item.id, this.ids) + ); + } } } diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html index df770b06d8..ebac09b398 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html @@ -6,11 +6,11 @@ - - @@ -24,7 +24,7 @@ [entities]="issues$ | async" [label]="'relationships.isIssueOf' | translate"> - diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts new file mode 100644 index 0000000000..ab77c4ae62 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts @@ -0,0 +1,88 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { JournalVolumePageFieldsComponent } from './journal-volume-page-fields.component'; + +let comp: JournalVolumePageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'journalvolume.identifier.volume', + language: 'en_US', + value: '1234' + }, + { + key: 'journalvolume.issuedate', + language: 'en_US', + value: '2018' + }, + { + key: 'journalvolume.identifier.description', + language: 'en_US', + value: 'desc' + }] +}); + +describe('JournalVolumePageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [JournalVolumePageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalVolumePageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalVolumePageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts index cd3725018d..ae92ad7993 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('JournalVolume', ElementViewMode.Full) @Component({ @@ -29,13 +30,15 @@ export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent ngOnInit(): void { super.ngOnInit(); - this.journals$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isJournalOfVolume'), - relationsToItems(this.item.id, this.ids) - ); - this.issues$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isIssueOfJournalVolume'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.journals$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isJournalOfVolume'), + relationsToItems(this.item.id, this.ids) + ); + this.issues$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isIssueOfJournalVolume'), + relationsToItems(this.item.id, this.ids) + ); + } } } diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html index e5a02b54e5..8314eeae08 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html @@ -6,19 +6,19 @@ - - - - @@ -36,7 +36,7 @@ [entities]="publications$ | async" [label]="'relationships.isPublicationOf' | translate"> - diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts new file mode 100644 index 0000000000..cb066c0783 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts @@ -0,0 +1,98 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { OrgUnitPageFieldsComponent } from './orgunit-page-fields.component'; + +let comp: OrgUnitPageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'orgunit.identifier.dateestablished', + language: 'en_US', + value: '2018' + }, + { + key: 'orgunit.identifier.city', + language: 'en_US', + value: 'New York' + }, + { + key: 'orgunit.identifier.country', + language: 'en_US', + value: 'USA' + }, + { + key: 'orgunit.identifier.id', + language: 'en_US', + value: '1' + }, + { + key: 'orgunit.identifier.description', + language: 'en_US', + value: 'desc' + }] +}); + +describe('OrgUnitPageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [OrgUnitPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(OrgUnitPageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(OrgUnitPageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index 9f3437c227..a19136f62e 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('OrgUnit', ElementViewMode.Full) @Component({ @@ -32,18 +33,20 @@ export class OrgUnitPageFieldsComponent extends EntityPageFieldsComponent implem ngOnInit(): void { super.ngOnInit(); - this.people$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPersonOfOrgUnit'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.people$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPersonOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); - this.projects$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isProjectOfOrgUnit'), - relationsToItems(this.item.id, this.ids) - ); + this.projects$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isProjectOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPublicationOfOrgUnit'), - relationsToItems(this.item.id, this.ids) - ); + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfOrgUnit'), + relationsToItems(this.item.id, this.ids) + ); + } }} diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index 548f46624d..b712c5e479 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -6,19 +6,19 @@ - - - - @@ -32,15 +32,15 @@ [entities]="orgUnits$ | async" [label]="'relationships.isOrgUnitOf' | translate"> - - - diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts new file mode 100644 index 0000000000..02f75966da --- /dev/null +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts @@ -0,0 +1,110 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { PersonPageFieldsComponent } from './person-page-fields.component'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; + +let comp: PersonPageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'person.identifier.email', + language: 'en_US', + value: 'fake@email.com' + }, + { + key: 'person.identifier.orcid', + language: 'en_US', + value: 'ORCID-1' + }, + { + key: 'person.identifier.birthdate', + language: 'en_US', + value: '1993' + }, + { + key: 'person.identifier.staffid', + language: 'en_US', + value: '1' + }, + { + key: 'person.identifier.jobtitle', + language: 'en_US', + value: 'Developer' + }, + { + key: 'person.identifier.lastname', + language: 'en_US', + value: 'Doe' + }, + { + key: 'person.identifier.firstname', + language: 'en_US', + value: 'John' + }] +}); + +describe('PersonPageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [PersonPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: SearchFixedFilterService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PersonPageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PersonPageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index 71e1fe8640..a371cc7322 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -10,6 +10,7 @@ import { relationsToItems } from '../shared/entity-page-fields.component'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('Person', ElementViewMode.Full) @Component({ @@ -34,22 +35,24 @@ export class PersonPageFieldsComponent extends EntityPageFieldsComponent { ngOnInit(): void { super.ngOnInit(); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPublicationOfAuthor'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfAuthor'), + relationsToItems(this.item.id, this.ids) + ); - this.projects$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isProjectOfPerson'), - relationsToItems(this.item.id, this.ids) - ); + this.projects$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isProjectOfPerson'), + relationsToItems(this.item.id, this.ids) + ); - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isOrgUnitOfPerson'), - relationsToItems(this.item.id, this.ids) - ); + this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isOrgUnitOfPerson'), + relationsToItems(this.item.id, this.ids) + ); - this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); - this.fixedFilter$ = Observable.of('publication'); + this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); + this.fixedFilter$ = Observable.of('publication'); + } } } diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index 36a71bd71a..159ae84584 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -6,15 +6,15 @@ - - - @@ -32,11 +32,11 @@ [entities]="orgUnits$ | async" [label]="'relationships.isOrgUnitOf' | translate"> - - diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts new file mode 100644 index 0000000000..6494b19215 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts @@ -0,0 +1,98 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { ProjectPageFieldsComponent } from './project-page-fields.component'; + +let comp: ProjectPageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'project.identifier.status', + language: 'en_US', + value: 'published' + }, + { + key: 'project.identifier.id', + language: 'en_US', + value: '1' + }, + { + key: 'project.identifier.expectedcompletion', + language: 'en_US', + value: 'exp comp' + }, + { + key: 'project.identifier.description', + language: 'en_US', + value: 'keyword' + }, + { + key: 'project.identifier.keyword', + language: 'en_US', + value: 'keyword' + }] +}); + +describe('ProjectPageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ProjectPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ProjectPageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ProjectPageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index 844d0f3808..8d4412bdbd 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('Project', ElementViewMode.Full) @Component({ @@ -31,19 +32,21 @@ export class ProjectPageFieldsComponent extends EntityPageFieldsComponent implem ngOnInit(): void { super.ngOnInit(); - this.people$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPersonOfProject'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.people$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPersonOfProject'), + relationsToItems(this.item.id, this.ids) + ); - this.publications$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isPublicationOfProject'), - relationsToItems(this.item.id, this.ids) - ); + this.publications$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isPublicationOfProject'), + relationsToItems(this.item.id, this.ids) + ); - this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isOrgUnitOfProject'), - relationsToItems(this.item.id, this.ids) - ); + this.orgUnits$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isOrgUnitOfProject'), + relationsToItems(this.item.id, this.ids) + ); + } } } From b8a9fe0598aeebf56c4fe7532c565355ef6fb6cb Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 22 Oct 2018 16:59:31 +0200 Subject: [PATCH 040/205] 56434: JournalPageFieldsComponent tests --- .../journal-page-fields.component.html | 6 +- .../journal-page-fields.component.spec.ts | 88 +++++++++++++++++++ .../journal/journal-page-fields.component.ts | 11 ++- 3 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html index c2e9fa1c05..b02000a9e4 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html @@ -6,11 +6,11 @@ - - @@ -20,7 +20,7 @@ [entities]="volumes$ | async" [label]="'relationships.isVolumeOf' | translate"> - diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts new file mode 100644 index 0000000000..1b7142b108 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts @@ -0,0 +1,88 @@ +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { Observable } from 'rxjs/Observable'; +import { Item } from '../../../../core/shared/item.model'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { JournalPageFieldsComponent } from './journal-page-fields.component'; + +let comp: JournalPageFieldsComponent; +let fixture: ComponentFixture; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'journal.identifier.issn', + language: 'en_US', + value: '1234' + }, + { + key: 'journal.publisher', + language: 'en_US', + value: 'Atmire' + }, + { + key: 'journal.identifier.description', + language: 'en_US', + value: 'desc' + }] +}); + +describe('JournalPageFieldsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [JournalPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(JournalPageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(JournalPageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } +}); + +function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts index b43805ec52..e7640fdd82 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts @@ -9,6 +9,7 @@ import { EntityPageFieldsComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity-page-fields.component'; +import { isNotEmpty } from '../../../../shared/empty.util'; @rendersEntityType('Journal', ElementViewMode.Full) @Component({ @@ -28,9 +29,11 @@ export class JournalPageFieldsComponent extends EntityPageFieldsComponent { ngOnInit(): void { super.ngOnInit(); - this.volumes$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isVolumeOfJournal'), - relationsToItems(this.item.id, this.ids) - ); + if (isNotEmpty(this.resolvedRelsAndTypes$)) { + this.volumes$ = this.resolvedRelsAndTypes$.pipe( + filterRelationsByTypeLabel('isVolumeOfJournal'), + relationsToItems(this.item.id, this.ids) + ); + } } } From 221e20f495d12da4ecfaa09dbaf305a6eb7ec378 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 22 Oct 2018 17:51:51 +0200 Subject: [PATCH 041/205] 56434: ItemPageFieldComponent tests --- ...item-page-abstract-field.component.spec.ts | 41 +++++++++++++ .../item-page-author-field.component.spec.ts | 45 ++++++++++++++ .../item-page-date-field.component.spec.ts | 41 +++++++++++++ .../generic-item-page-field.component.spec.ts | 45 ++++++++++++++ .../item-page-field.component.spec.ts | 61 +++++++++++++++++++ .../item-page-title-field.component.spec.ts | 41 +++++++++++++ .../uri/item-page-uri-field.component.spec.ts | 41 +++++++++++++ 7 files changed, 315 insertions(+) create mode 100644 src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts create mode 100644 src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts diff --git a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts new file mode 100644 index 0000000000..9461ee0950 --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.spec.ts @@ -0,0 +1,41 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ItemPageAbstractFieldComponent } from './item-page-abstract-field.component'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; + +let comp: ItemPageAbstractFieldComponent; +let fixture: ComponentFixture; + +const mockField = 'dc.description.abstract'; +const mockValue = 'test value'; + +describe('ItemPageAbstractFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageAbstractFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageAbstractFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageAbstractFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); diff --git a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts new file mode 100644 index 0000000000..d865caff8a --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.spec.ts @@ -0,0 +1,45 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; +import { ItemPageAuthorFieldComponent } from './item-page-author-field.component'; + +let comp: ItemPageAuthorFieldComponent; +let fixture: ComponentFixture; + +const mockFields = ['dc.contributor.author', 'dc.creator', 'dc.contributor']; +const mockValue = 'test value'; + +describe('ItemPageAuthorFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageAuthorFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageAuthorFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + for (const field of mockFields) { + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageAuthorFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(field, mockValue); + fixture.detectChanges(); + })); + + describe(`when the item contains metadata for ${field}`, () => { + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); + }); + } +}); diff --git a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts new file mode 100644 index 0000000000..2adada582b --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.spec.ts @@ -0,0 +1,41 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; +import { ItemPageDateFieldComponent } from './item-page-date-field.component'; + +let comp: ItemPageDateFieldComponent; +let fixture: ComponentFixture; + +const mockField = 'dc.date.issued'; +const mockValue = 'test value'; + +describe('ItemPageDateFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageDateFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageDateFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageDateFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); diff --git a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts new file mode 100644 index 0000000000..d8abd39cf3 --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.spec.ts @@ -0,0 +1,45 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; +import { GenericItemPageFieldComponent } from './generic-item-page-field.component'; + +let comp: GenericItemPageFieldComponent; +let fixture: ComponentFixture; + +const mockValue = 'test value'; +const mockField = 'dc.test'; +const mockLabel = 'test label'; +const mockFields = [mockField]; + +describe('GenericItemPageFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [GenericItemPageFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(GenericItemPageFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(GenericItemPageFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + comp.fields = mockFields; + comp.label = mockLabel; + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts new file mode 100644 index 0000000000..daa1e64ac5 --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts @@ -0,0 +1,61 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Item } from '../../../../core/shared/item.model'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { Observable } from 'rxjs/Observable'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { ItemPageFieldComponent } from './item-page-field.component'; +import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component'; + +let comp: ItemPageFieldComponent; +let fixture: ComponentFixture; + +const mockValue = 'test value'; +const mockField = 'dc.test'; +const mockLabel = 'test label'; +const mockFields = [mockField]; + +describe('ItemPageFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + comp.fields = mockFields; + comp.label = mockLabel; + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); + +export function mockItemWithMetadataFieldAndValue(field: string, value: string): Item { + return Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: field, + language: 'en_US', + value: value + }] + }); +} diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts new file mode 100644 index 0000000000..cb1ba6a4bc --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.spec.ts @@ -0,0 +1,41 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { MetadataValuesComponent } from '../../../../field-components/metadata-values/metadata-values.component'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; +import { ItemPageTitleFieldComponent } from './item-page-title-field.component'; + +let comp: ItemPageTitleFieldComponent; +let fixture: ComponentFixture; + +const mockField = 'dc.title'; +const mockValue = 'test value'; + +describe('ItemPageTitleFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageTitleFieldComponent, MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageTitleFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageTitleFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts new file mode 100644 index 0000000000..4511f16aae --- /dev/null +++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.spec.ts @@ -0,0 +1,41 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../../../shared/testing/mock-translate-loader'; +import { mockItemWithMetadataFieldAndValue } from '../item-page-field.component.spec'; +import { ItemPageUriFieldComponent } from './item-page-uri-field.component'; +import { MetadataUriValuesComponent } from '../../../../field-components/metadata-uri-values/metadata-uri-values.component'; + +let comp: ItemPageUriFieldComponent; +let fixture: ComponentFixture; + +const mockField = 'dc.identifier.uri'; +const mockValue = 'test value'; + +describe('ItemPageUriFieldComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [ItemPageUriFieldComponent, MetadataUriValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageUriFieldComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageUriFieldComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + fixture.detectChanges(); + })); + + it('should display display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); From 57ba31e289319075d644ce550e8a2c778435aa9f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 10:34:42 +0200 Subject: [PATCH 042/205] 56434: metadata-values component tests --- .../metadata-uri-values.component.html | 4 +- .../metadata-uri-values.component.spec.ts | 97 +++++++++++++++++++ .../metadata-values.component.html | 2 +- .../metadata-values.component.spec.ts | 67 +++++++++++++ 4 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts create mode 100644 src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html index cc618bcd50..10676e93c0 100644 --- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html @@ -1,5 +1,5 @@ - - {{ linktext || metadatum.value }} + diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts new file mode 100644 index 0000000000..f2ec0bedbc --- /dev/null +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts @@ -0,0 +1,97 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader'; +import { By } from '@angular/platform-browser'; +import { MetadataUriValuesComponent } from './metadata-uri-values.component'; +import { isNotEmpty } from '../../../shared/empty.util'; + +let comp: MetadataUriValuesComponent; +let fixture: ComponentFixture; + +const mockMetadata = [ + { + key: 'dc.identifier.uri', + language: 'en_US', + value: 'http://fakelink.org' + }, + { + key: 'dc.identifier.uri', + language: 'en_US', + value: 'http://another.fakelink.org' + }]; +const mockSeperator = '
'; +const mockLabel = 'fake.message'; +const mockLinkText = 'fake link text'; + +describe('MetadataUriValuesComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [MetadataUriValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MetadataUriValuesComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(MetadataUriValuesComponent); + comp = fixture.componentInstance; + comp.values = mockMetadata; + comp.separator = mockSeperator; + comp.label = mockLabel; + fixture.detectChanges(); + })); + + it('should display all metadata values', () => { + const innerHTML = fixture.nativeElement.innerHTML; + for (const metadatum of mockMetadata) { + expect(innerHTML).toContain(metadatum.value); + } + }); + + it('should contain the correct hrefs', () => { + const links = fixture.debugElement.queryAll(By.css('.metadata-value-links')); + for (const metadatum of mockMetadata) { + expect(containsHref(links, metadatum.value)).toBeTruthy(); + } + }); + + it('should contain separators equal to the amount of metadata values minus one', () => { + const separators = fixture.debugElement.queryAll(By.css('.metadata-value-separator')); + expect(separators.length).toBe(mockMetadata.length - 1); + }); + + describe('when linktext is defined', () => { + + beforeEach(() => { + comp.linktext = mockLinkText; + fixture.detectChanges(); + }); + + it('should replace the metadata value with the linktext', () => { + const link = fixture.debugElement.query(By.css('.metadata-value-links')); + expect(link.nativeElement.textContent).toContain(mockLinkText); + }); + + }); + +}); + +function containsHref(links: DebugElement[], href: string): boolean { + for (const link of links) { + const hrefAtt = link.properties.href; + if (isNotEmpty(hrefAtt)) { + if (hrefAtt === href) { + return true; + } + } + } + return false; +} diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.html b/src/app/+item-page/field-components/metadata-values/metadata-values.component.html index f16655c63c..c85699b092 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.html +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.html @@ -1,5 +1,5 @@ - {{metadatum.value}} + {{metadatum.value}} diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts new file mode 100644 index 0000000000..fdcf6679cc --- /dev/null +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts @@ -0,0 +1,67 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader'; +import { MetadataValuesComponent } from './metadata-values.component'; +import { By } from '@angular/platform-browser'; + +let comp: MetadataValuesComponent; +let fixture: ComponentFixture; + +const mockMetadata = [ + { + key: 'journal.identifier.issn', + language: 'en_US', + value: '1234' + }, + { + key: 'journal.publisher', + language: 'en_US', + value: 'Atmire' + }, + { + key: 'journal.identifier.description', + language: 'en_US', + value: 'desc' + }]; +const mockSeperator = '
'; +const mockLabel = 'fake.message'; + +describe('MetadataValuesComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [MetadataValuesComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MetadataValuesComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(MetadataValuesComponent); + comp = fixture.componentInstance; + comp.values = mockMetadata; + comp.separator = mockSeperator; + comp.label = mockLabel; + fixture.detectChanges(); + })); + + it('should display all metadata values', () => { + const innerHTML = fixture.nativeElement.innerHTML; + for (const metadatum of mockMetadata) { + expect(innerHTML).toContain(metadatum.value); + } + }); + + it('should contain separators equal to the amount of metadata values minus one', () => { + const separators = fixture.debugElement.queryAll(By.css('.metadata-value-separator')); + expect(separators.length).toBe(mockMetadata.length - 1); + }); + +}); From a66007c114fcddc484a1ddae36f1818655cb361f Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 11:49:15 +0200 Subject: [PATCH 043/205] 56434: SearchFixedFilterService tests --- .../search-fixed-filter.service.spec.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts new file mode 100644 index 0000000000..9f745d4ca5 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts @@ -0,0 +1,64 @@ +import { SearchFixedFilterService } from './search-fixed-filter.service'; +import { ResponseCacheEntry } from '../../../core/cache/response-cache.reducer'; +import { RouteService } from '../../../shared/services/route.service'; +import { RequestService } from '../../../core/data/request.service'; +import { ResponseCacheService } from '../../../core/cache/response-cache.service'; +import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; +import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response-cache.models'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { Observable } from 'rxjs/Observable'; + +describe('SearchFixedFilterService', () => { + let service: SearchFixedFilterService; + + const filterQuery = 'filter:query'; + + const routeServiceStub = {} as RouteService; + const requestServiceStub = Object.assign({ + /* tslint:disable:no-empty */ + configure: () => {}, + /* tslint:enable:no-empty */ + generateRequestId: () => 'fake-id' + }) as RequestService; + const responseCacheStub = Object.assign(new ResponseCacheService(undefined), { + get: () => Observable.of(Object.assign(new ResponseCacheEntry(), { + response: new FilteredDiscoveryQueryResponse(filterQuery, '200', new PageInfo()) + })) + }); + const halServiceStub = Object.assign(new HALEndpointService(responseCacheStub, requestServiceStub, undefined), { + getEndpoint: () => Observable.of('fake-url') + }); + + beforeEach(() => { + service = new SearchFixedFilterService(routeServiceStub, requestServiceStub, responseCacheStub, halServiceStub); + }); + + describe('when getQueryByFilterName is called with a filterName', () => { + it('should return the filter query', () => { + service.getQueryByFilterName('filter').subscribe((query) => { + expect(query).toBe(filterQuery); + }); + }); + }); + + describe('when getQueryByFilterName is called without a filterName', () => { + it('should return undefined', () => { + service.getQueryByFilterName(undefined).subscribe((query) => { + expect(query).toBeUndefined(); + }); + }); + }); + + describe('when getQueryByRelations is called', () => { + const relationType = 'isRelationOf'; + const itemUUID = 'c5b277e6-2477-48bb-8993-356710c285f3'; + + it('should contain the relationType and itemUUID', () => { + const query = service.getQueryByRelations(relationType, itemUUID); + expect(query.length).toBeGreaterThan(relationType.length + itemUUID.length); + expect(query).toContain(relationType); + expect(query).toContain(itemUUID); + }); + }); + +}); From 78567383328e3069490c232125d6e5799a26fb75 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 13:22:53 +0200 Subject: [PATCH 044/205] 56434: FilteredSearchPageComponent tests --- .../filtered-search-page.component.spec.ts | 37 ++++ .../filtered-search-page.component.ts | 6 +- .../search-page.component.spec.ts | 192 +++++++++--------- 3 files changed, 139 insertions(+), 96 deletions(-) create mode 100644 src/app/+search-page/filtered-search-page.component.spec.ts diff --git a/src/app/+search-page/filtered-search-page.component.spec.ts b/src/app/+search-page/filtered-search-page.component.spec.ts new file mode 100644 index 0000000000..5c49767ed2 --- /dev/null +++ b/src/app/+search-page/filtered-search-page.component.spec.ts @@ -0,0 +1,37 @@ +import { FilteredSearchPageComponent } from './filtered-search-page.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { configureSearchComponentTestingModule } from './search-page.component.spec'; +import { SearchConfigurationService } from './search-service/search-configuration.service'; + +describe('FilteredSearchPageComponent', () => { + let comp: FilteredSearchPageComponent; + let fixture: ComponentFixture; + let searchConfigService: SearchConfigurationService; + + beforeEach(async(() => { + configureSearchComponentTestingModule(FilteredSearchPageComponent); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FilteredSearchPageComponent); + comp = fixture.componentInstance; + searchConfigService = (comp as any).searchConfigService; + fixture.detectChanges(); + }); + + describe('when fixedFilterQuery is defined', () => { + const fixedFilterQuery = 'fixedFilterQuery'; + + beforeEach(() => { + spyOn(searchConfigService, 'updateFixedFilter').and.callThrough(); + comp.fixedFilterQuery = fixedFilterQuery; + comp.ngOnInit(); + fixture.detectChanges(); + }); + + it('should update the paginated search options', () => { + expect(searchConfigService.updateFixedFilter).toHaveBeenCalledWith(fixedFilterQuery); + }); + }); + +}); diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts index 5feae2171e..1639c95fac 100644 --- a/src/app/+search-page/filtered-search-page.component.ts +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -1,18 +1,14 @@ -import { CommunityDataService } from '../core/data/community-data.service'; import { HostWindowService } from '../shared/host-window.service'; import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchPageComponent } from './search-page.component'; -import { ChangeDetectionStrategy, Component, Injectable, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { pushInOut } from '../shared/animations/push'; import { RouteService } from '../shared/services/route.service'; import { SearchConfigurationService } from './search-service/search-configuration.service'; -import { switchMap, tap } from 'rxjs/operators'; -import { getSucceededRemoteData } from '../core/shared/operators'; import { Observable } from 'rxjs/Observable'; import { PaginatedSearchOptions } from './paginated-search-options.model'; -import { isNotEmpty } from '../shared/empty.util'; /** * This component renders a simple item page. diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index 85283e7255..54418cca44 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -23,100 +23,110 @@ import { SearchConfigurationService } from './search-service/search-configuratio import { RemoteData } from '../core/data/remote-data'; import { RouteService } from '../shared/services/route.service'; -describe('SearchPageComponent', () => { - let comp: SearchPageComponent; - let fixture: ComponentFixture; - let searchServiceObject: SearchService; - const store: Store = jasmine.createSpyObj('store', { - /* tslint:disable:no-empty */ - dispatch: {}, - /* tslint:enable:no-empty */ - select: Observable.of(true) - }); - const pagination: PaginationComponentOptions = new PaginationComponentOptions(); - pagination.id = 'search-results-pagination'; - pagination.currentPage = 1; - pagination.pageSize = 10; - const sort: SortOptions = new SortOptions('score', SortDirection.DESC); - const mockResults = Observable.of(new RemoteData(false, false, true, null, ['test', 'data'])); - const searchServiceStub = jasmine.createSpyObj('SearchService', { - search: mockResults, - getSearchLink: '/search', - getScopes: Observable.of(['test-scope']) - }); - const queryParam = 'test query'; - const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; - const paginatedSearchOptions = { +let comp: SearchPageComponent; +let fixture: ComponentFixture; +let searchServiceObject: SearchService; +const store: Store = jasmine.createSpyObj('store', { + /* tslint:disable:no-empty */ + dispatch: {}, + /* tslint:enable:no-empty */ + select: Observable.of(true) +}); +const pagination: PaginationComponentOptions = new PaginationComponentOptions(); +pagination.id = 'search-results-pagination'; +pagination.currentPage = 1; +pagination.pageSize = 10; +const sort: SortOptions = new SortOptions('score', SortDirection.DESC); +const mockResults = Observable.of(new RemoteData(false, false, true, null, ['test', 'data'])); +const searchServiceStub = jasmine.createSpyObj('SearchService', { + search: mockResults, + getSearchLink: '/search', + getScopes: Observable.of(['test-scope']) +}); +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const fixedFilter = 'fixed filter'; +const paginatedSearchOptions = { + query: queryParam, + scope: scopeParam, + fixedFilter: fixedFilter, + pagination, + sort +}; +const activatedRouteStub = { + queryParams: Observable.of({ query: queryParam, - scope: scopeParam, - pagination, - sort - }; - const activatedRouteStub = { - queryParams: Observable.of({ - query: queryParam, - scope: scopeParam - }) - }; - const sidebarService = { - isCollapsed: Observable.of(true), - collapse: () => this.isCollapsed = Observable.of(true), - expand: () => this.isCollapsed = Observable.of(false) - }; - const routeServiceStub = { - getRouteParameterValue: () => { - return Observable.of(''); - } - }; + scope: scopeParam + }) +}; +const sidebarService = { + isCollapsed: Observable.of(true), + collapse: () => this.isCollapsed = Observable.of(true), + expand: () => this.isCollapsed = Observable.of(false) +}; +const routeServiceStub = { + getRouteParameterValue: () => { + return Observable.of(''); + } +}; + +export function configureSearchComponentTestingModule(compType) { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], + declarations: [compType], + providers: [ + { provide: SearchService, useValue: searchServiceStub }, + { + provide: CommunityDataService, + useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) + }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { + provide: Store, useValue: store + }, + { + provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', + { + isXs: Observable.of(true), + isSm: Observable.of(false), + isXsOrSm: Observable.of(true) + }) + }, + { + provide: SearchSidebarService, + useValue: sidebarService + }, + { + provide: SearchFilterService, + useValue: {} + }, + { + provide: SearchConfigurationService, + useValue: { + paginatedSearchOptions: hot('a', { + a: paginatedSearchOptions + }), + getCurrentScope: (a) => Observable.of('test-id'), + /* tslint:disable:no-empty */ + updateFixedFilter: (newFilter) => {} + /* tslint:enable:no-empty */ + } + }, + { + provide: RouteService, + useValue: routeServiceStub + } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(compType, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); +} + +describe('SearchPageComponent', () => { beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], - declarations: [SearchPageComponent], - providers: [ - { provide: SearchService, useValue: searchServiceStub }, - { - provide: CommunityDataService, - useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) - }, - { provide: ActivatedRoute, useValue: activatedRouteStub }, - { - provide: Store, useValue: store - }, - { - provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', - { - isXs: Observable.of(true), - isSm: Observable.of(false), - isXsOrSm: Observable.of(true) - }) - }, - { - provide: SearchSidebarService, - useValue: sidebarService - }, - { - provide: SearchFilterService, - useValue: {} - }, - { - provide: SearchConfigurationService, - useValue: { - paginatedSearchOptions: hot('a', { - a: paginatedSearchOptions - }), - getCurrentScope: (a) => Observable.of('test-id') - } - }, - { - provide: RouteService, - useValue: routeServiceStub - } - ], - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(SearchPageComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } - }).compileComponents(); + configureSearchComponentTestingModule(SearchPageComponent); })); beforeEach(() => { From 33eb59ae9ad9fd9cfd312804584ac1dea2f207bb Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 14:40:38 +0200 Subject: [PATCH 045/205] 56434: SearchFilterService added tests --- .../search-filter.service.spec.ts | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts index 61766b8f4e..8a511c7ffc 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts @@ -12,6 +12,7 @@ import { SearchFilterConfig } from '../../search-service/search-filter-config.mo import { FilterType } from '../../search-service/filter-type.model'; import { SearchFixedFilterService } from './search-fixed-filter.service'; import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub'; +import { SortDirection, SortOptions } from '../../../core/cache/models/sort-options.model'; describe('SearchFilterService', () => { let service: SearchFilterService; @@ -48,11 +49,15 @@ describe('SearchFilterService', () => { }, addQueryParameterValue: (param: string, value: string) => { }, + getQueryParameterValue: (param: string) => { + }, getQueryParameterValues: (param: string) => { return Observable.of({}); }, getQueryParamsWithPrefix: (param: string) => { return Observable.of({}); + }, + getRouteParameterValue: (param: string) => { } /* tslint:enable:no-empty */ }; @@ -182,4 +187,185 @@ describe('SearchFilterService', () => { }); }); + describe('when the getCurrentScope method is called', () => { + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParameterValue'); + service.getCurrentScope(); + }); + + it('should call getQueryParameterValue on the route service with scope', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('scope'); + }); + }); + + describe('when the getCurrentQuery method is called', () => { + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParameterValue'); + service.getCurrentQuery(); + }); + + it('should call getQueryParameterValue on the route service with query', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('query'); + }); + }); + + describe('when the getCurrentPagination method is called', () => { + let result; + const mockReturn = 5; + + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParameterValue').and.returnValue(Observable.of(mockReturn)); + result = service.getCurrentPagination(); + }); + + it('should call getQueryParameterValue on the route service with page', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('page'); + }); + + it('should call getQueryParameterValue on the route service with pageSize', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('pageSize'); + }); + + it('should return an observable containing the correct pagination', () => { + result.subscribe((pagination) => { + expect(pagination.currentPage).toBe(mockReturn); + expect(pagination.pageSize).toBe(mockReturn); + }); + }); + }); + + describe('when the getCurrentSort method is called', () => { + let result; + const field = 'author'; + const direction = SortDirection.ASC; + + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParameterValue').and.returnValue(Observable.of(undefined)); + result = service.getCurrentSort(new SortOptions(field, direction)); + }); + + it('should call getQueryParameterValue on the route service with sortDirection', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('sortDirection'); + }); + + it('should call getQueryParameterValue on the route service with sortField', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('sortField'); + }); + + it('should return an observable containing the correct sortOptions', () => { + result.subscribe((sort) => { + expect(sort.field).toBe(field); + expect(sort.direction).toBe(direction); + }); + }); + }); + + describe('when the getCurrentFilters method is called', () => { + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParamsWithPrefix'); + service.getCurrentFilters(); + }); + + it('should call getQueryParamsWithPrefix on the route service with prefix \'f.\'', () => { + expect(routeServiceStub.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.'); + }); + }); + + describe('when the getCurrentFixedFilter method is called', () => { + const filter = 'filter'; + + beforeEach(() => { + spyOn(routeServiceStub, 'getRouteParameterValue').and.returnValue(Observable.of(filter)); + spyOn(mockFixedFilterService, 'getQueryByFilterName').and.returnValue(Observable.of(filter)); + service.getCurrentFixedFilter().subscribe(); + }); + + it('should call getQueryByFilterName on the fixed-filter service with the correct filter', () => { + expect(mockFixedFilterService.getQueryByFilterName).toHaveBeenCalledWith(filter); + }); + }); + + describe('when the getCurrentView method is called', () => { + beforeEach(() => { + spyOn(routeServiceStub, 'getQueryParameterValue'); + service.getCurrentView(); + }); + + it('should call getQueryParameterValue on the route service with view', () => { + expect(routeServiceStub.getQueryParameterValue).toHaveBeenCalledWith('view'); + }); + }); + + describe('when the getPaginatedSearchOptions method is called', () => { + beforeEach(() => { + spyOn(service, 'getCurrentPagination'); + spyOn(service, 'getCurrentSort'); + spyOn(service, 'getCurrentView'); + spyOn(service, 'getCurrentScope'); + spyOn(service, 'getCurrentQuery'); + spyOn(service, 'getCurrentFilters'); + spyOn(service, 'getCurrentFixedFilter'); + service.getPaginatedSearchOptions(); + }); + + it('should call getCurrentPagination to build the paginated search options', () => { + expect(service.getCurrentPagination).toHaveBeenCalled(); + }); + + it('should call getCurrentSort to build the paginated search options', () => { + expect(service.getCurrentSort).toHaveBeenCalled(); + }); + + it('should call getCurrentView to build the paginated search options', () => { + expect(service.getCurrentView).toHaveBeenCalled(); + }); + + it('should call getCurrentScope to build the paginated search options', () => { + expect(service.getCurrentScope).toHaveBeenCalled(); + }); + + it('should call getCurrentQuery to build the paginated search options', () => { + expect(service.getCurrentQuery).toHaveBeenCalled(); + }); + + it('should call getCurrentFilters to build the paginated search options', () => { + expect(service.getCurrentFilters).toHaveBeenCalled(); + }); + + it('should call getCurrentFixedFilter to build the paginated search options', () => { + expect(service.getCurrentFixedFilter).toHaveBeenCalled(); + }); + }); + + describe('when the getSearchOptions method is called', () => { + beforeEach(() => { + spyOn(service, 'getCurrentView'); + spyOn(service, 'getCurrentScope'); + spyOn(service, 'getCurrentQuery'); + spyOn(service, 'getCurrentFilters'); + spyOn(service, 'getCurrentFixedFilter'); + service.getPaginatedSearchOptions(); + }); + + it('should call getCurrentView to build the search options', () => { + expect(service.getCurrentView).toHaveBeenCalled(); + }); + + it('should call getCurrentScope to build the search options', () => { + expect(service.getCurrentScope).toHaveBeenCalled(); + }); + + it('should call getCurrentQuery to build the search options', () => { + expect(service.getCurrentQuery).toHaveBeenCalled(); + }); + + it('should call getCurrentFilters to build the search options', () => { + expect(service.getCurrentFilters).toHaveBeenCalled(); + }); + + it('should call getCurrentFixedFilter to build the search options', () => { + expect(service.getCurrentFixedFilter).toHaveBeenCalled(); + }); + }); + }); From cc369bf7577201f244bb91d766f1a4e542b0fff3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 15:40:38 +0200 Subject: [PATCH 046/205] 56434: SearchResultsComponent and SearchConfigurationService test additions --- .../search-results.component.html | 2 +- .../search-results.component.spec.ts | 43 +++++++++++++++---- .../search-configuration.service.spec.ts | 25 +++++++++++ 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index b685d0b28d..126efecbf9 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,4 +1,4 @@ -

{{ getTitleKey() | translate }}

+

{{ getTitleKey() | translate }}

{ let comp: SearchResultsComponent; let fixture: ComponentFixture; let heading: DebugElement; + let title: DebugElement; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -23,18 +24,42 @@ describe('SearchResultsComponent', () => { fixture = TestBed.createComponent(SearchResultsComponent); comp = fixture.componentInstance; // SearchFormComponent test instance heading = fixture.debugElement.query(By.css('heading')); + title = fixture.debugElement.query(By.css('.search-results-title')); }); - it('should display heading when results are not empty', fakeAsync(() => { - (comp as any).searchResults = 'test'; - (comp as any).searchConfig = {pagination: ''}; - fixture.detectChanges(); - tick(); - expect(heading).toBeDefined(); - })); + describe('when results are not empty', () => { + beforeEach(() => { + (comp as any).searchResults = 'test'; + (comp as any).searchConfig = {pagination: ''}; + fixture.detectChanges(); + }); - it('should not display heading when results is empty', () => { - expect(heading).toBeNull(); + it('should display heading',() => { + expect(heading).toBeDefined(); + }); + + describe('when disableHeader is not set', () => { + it('should not display a title',() => { + expect(title).toBeNull(); + }); + }); + + describe('when disableHeader is set', () => { + beforeEach(() => { + comp.disableHeader = true; + fixture.detectChanges(); + }); + + it('should display a title',() => { + expect(title).toBeDefined(); + }); + }); + }); + + describe('when results are empty', () => { + it('should not display heading', () => { + expect(heading).toBeNull(); + }); }); }); diff --git a/src/app/+search-page/search-service/search-configuration.service.spec.ts b/src/app/+search-page/search-service/search-configuration.service.spec.ts index 6588da858f..9557fa9c1e 100644 --- a/src/app/+search-page/search-service/search-configuration.service.spec.ts +++ b/src/app/+search-page/search-service/search-configuration.service.spec.ts @@ -147,4 +147,29 @@ describe('SearchConfigurationService', () => { }); }); }); + + describe('when getCurrentFixedFilter is called', () => { + beforeEach(() => { + service.getCurrentFixedFilter(); + }); + it('should call getRouteParameterValue on the routeService with parameter name \'filter\'', () => { + expect((service as any).routeService.getRouteParameterValue).toHaveBeenCalledWith('filter'); + }); + }); + + describe('when updateFixedFilter is called', () => { + const filter = 'filter'; + + beforeEach(() => { + service.updateFixedFilter(filter); + }); + + it('should update the paginated search options with the correct fixed filter', () => { + expect(service.paginatedSearchOptions.getValue().fixedFilter).toEqual(filter); + }); + + it('should update the search options with the correct fixed filter', () => { + expect(service.searchOptions.getValue().fixedFilter).toEqual(filter); + }); + }); }); From 028a2b59a18a9bfa9acc83e0fa690b889241c940 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 16:10:17 +0200 Subject: [PATCH 047/205] 56434: EntityTypeSwitcherComponent test --- .../entity-type-switcher.component.spec.ts | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts new file mode 100644 index 0000000000..d22e4a1c2a --- /dev/null +++ b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts @@ -0,0 +1,59 @@ +import { EntityTypeSwitcherComponent } from './entity-type-switcher.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { Item } from '../../../core/shared/item.model'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { RemoteData } from '../../../core/data/remote-data'; +import * as decorator from '../entity-type-decorator'; +import { ElementViewMode } from '../../view-mode'; +import { getComponentByEntityType } from '../entity-type-decorator'; + +const relationType = 'type'; +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'test item' + }, + { + key: 'relationship.type', + language: 'en_US', + value: relationType + }] +}); +const viewMode = ElementViewMode.Full; + +describe('EntityTypeSwitcherComponent', () => { + let comp: EntityTypeSwitcherComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ EntityTypeSwitcherComponent ], + schemas: [ NO_ERRORS_SCHEMA ] + }).compileComponents(); // compile template and css + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(EntityTypeSwitcherComponent); + comp = fixture.componentInstance; + comp.object = mockItem; + comp.viewMode = viewMode; + spyOn(decorator, 'getComponentByEntityType').and.returnValue('component'); + })); + + describe('when calling getComponent', () => { + beforeEach(() => { + comp.getComponent(); + }); + + it('should call getComponentByEntityType with parameters type and viewMode', () => { + expect(decorator.getComponentByEntityType).toHaveBeenCalledWith(relationType, viewMode); + }); + }); + +}); From 39e1b0f7d59d24dcfa47fe4ae840e17dc176a9c7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Tue, 23 Oct 2018 16:52:03 +0200 Subject: [PATCH 048/205] 56434: Docs for all entity-list-element and entity-page-fields components --- .../journal-issue-page-fields.component.ts | 10 ++++++++++ .../journal-volume-page-fields.component.ts | 3 +++ .../journal/journal-page-fields.component.ts | 6 ++++++ .../orgunit/orgunit-page-fields.component.ts | 3 +++ .../person/person-page-fields.component.ts | 3 +++ .../project/project-page-fields.component.ts | 3 +++ .../shared/entity-page-fields.component.ts | 17 +++++++++++++++++ .../entity-list-element.component.ts | 4 ++++ .../entity-search-result-component.ts | 17 +++++++++++++++++ .../journal-issue-list-element.component.ts | 4 +++- .../journal-volume-list-element.component.ts | 4 +++- .../journal/journal-list-element.component.ts | 4 +++- .../orgunit/orgunit-list-element.component.ts | 4 +++- .../person/person-list-element.component.ts | 4 +++- .../project/project-list-element.component.ts | 4 +++- .../publication-list-element.component.ts | 4 +++- 16 files changed, 87 insertions(+), 7 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts index 33b79a639a..49d24b623b 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts @@ -17,8 +17,18 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./journal-issue-page-fields.component.scss'], templateUrl: './journal-issue-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Journal Issue + */ export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { + /** + * The volumes related to this journal issue + */ volumes$: Observable; + + /** + * The publications related to this journal issue + */ publications$: Observable; constructor( diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts index ae92ad7993..09e43cf427 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts @@ -17,6 +17,9 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./journal-volume-page-fields.component.scss'], templateUrl: './journal-volume-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Journal Volume + */ export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent { journals$: Observable; issues$: Observable; diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts index e7640fdd82..93e2b3bfd2 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts @@ -17,7 +17,13 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./journal-page-fields.component.scss'], templateUrl: './journal-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Journal + */ export class JournalPageFieldsComponent extends EntityPageFieldsComponent { + /** + * The volumes related to this journal + */ volumes$: Observable; constructor( diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index a19136f62e..e1a2c7abe8 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -17,6 +17,9 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./orgunit-page-fields.component.scss'], templateUrl: './orgunit-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Organisation Unit + */ export class OrgUnitPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { people$: Observable; diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index a371cc7322..db74c6e1af 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -18,6 +18,9 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./person-page-fields.component.scss'], templateUrl: './person-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Person + */ export class PersonPageFieldsComponent extends EntityPageFieldsComponent { publications$: Observable; projects$: Observable; diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index 8d4412bdbd..976b3ccec0 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -17,6 +17,9 @@ import { isNotEmpty } from '../../../../shared/empty.util'; styleUrls: ['./project-page-fields.component.scss'], templateUrl: './project-page-fields.component.html' }) +/** + * The component for displaying metadata and relations of an item with entity type Project + */ export class ProjectPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { people$: Observable; publications$: Observable; diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts index de49783570..7b23c87806 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts @@ -28,6 +28,11 @@ const compareArraysUsing = (mapFn: (t: T) => any) => const compareArraysUsingIds = () => compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); +/** + * Fetch the relationships which match the type label given + * @param {string} label Type label + * @returns {(source: Observable<[Relationship[] , RelationshipType[]]>) => Observable} + */ export const filterRelationsByTypeLabel = (label: string) => (source: Observable<[Relationship[], RelationshipType[]]>): Observable => source.pipe( @@ -40,6 +45,12 @@ export const filterRelationsByTypeLabel = (label: string) => distinctUntilChanged(compareArraysUsingIds()) ); +/** + * Operator for turning a list of relationships into a list of the relevant items + * @param {string} thisId The item's id of which the relations belong to + * @param {ItemDataService} ids The ItemDataService to fetch items from the REST API + * @returns {(source: Observable) => Observable} + */ export const relationsToItems = (thisId: string, ids: ItemDataService) => (source: Observable): Observable => source.pipe( @@ -65,7 +76,13 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) => selector: 'ds-entity-page-fields', template: '' }) +/** + * A generic component for displaying metadata and relations of an item + */ export class EntityPageFieldsComponent implements OnInit { + /** + * Resolved relationships and types together in one observable + */ resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]> constructor( diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.ts index 0a284708a9..e71a868579 100644 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.ts @@ -12,6 +12,10 @@ import { SetViewMode } from '../../view-mode'; templateUrl: './entity-list-element.component.html' }) +/** + * The component used to list entities depending on type + * Uses entity-type-switcher to determine which components to use for displaying the list + */ @renderElementsFor(Item, SetViewMode.List) export class EntityListElementComponent extends AbstractListableElementComponent { ElementViewMode = viewMode.ElementViewMode; diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts index bcf2e45a8c..7c05584cf0 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts @@ -8,6 +8,9 @@ import { ItemSearchResult } from '../../../object-collection/shared/item-search- import { TruncatableService } from '../../../truncatable/truncatable.service'; // TODO lot of overlap with SearchResultListElementComponent => refactor! +/** + * A generic component for displaying entity list elements + */ @Component({ selector: 'ds-entity-search-result', template: '' @@ -33,6 +36,11 @@ export class EntitySearchResultComponent { } } + /** + * Get the values of metadata by keys + * @param {string[]} keys List of metadata keys to get values for + * @returns {string[]} List of metadata values + */ getValues(keys: string[]): string[] { const results: string[] = new Array(); this.searchResult.hitHighlights.forEach( @@ -52,6 +60,11 @@ export class EntitySearchResultComponent { return results; } + /** + * Get the first value of a metadatum by key + * @param {string} key Metadatum key + * @returns {string} Metadatum value + */ getFirstValue(key: string): string { let result: string; this.searchResult.hitHighlights.some( @@ -68,6 +81,10 @@ export class EntitySearchResultComponent { return result; } + /** + * Whether or not the item's values are collapsed + * @returns {Observable} + */ isCollapsed(): Observable { return this.truncatableService.isCollapsed(this.item.id); } diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts index 8580757997..7f8278fcd3 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./journal-issue-list-element.component.scss'], templateUrl: './journal-issue-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Journal Issue + */ export class JournalIssueListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts index 956afe99be..e68b7e462d 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./journal-volume-list-element.component.scss'], templateUrl: './journal-volume-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Journal Volume + */ export class JournalVolumeListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts index d5d2cee1cc..23d030c77f 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./journal-list-element.component.scss'], templateUrl: './journal-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Journal + */ export class JournalListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts index 9ae7489e97..72bd143971 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./orgunit-list-element.component.scss'], templateUrl: './orgunit-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Organisation Unit + */ export class OrgUnitListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts index b1156b1372..32e378c8c7 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./person-list-element.component.scss'], templateUrl: './person-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Person + */ export class PersonListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts index f5275c47e8..ffffd70a33 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts @@ -9,6 +9,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./project-list-element.component.scss'], templateUrl: './project-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Project + */ export class ProjectListElementComponent extends EntitySearchResultComponent { } diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts index 3f8b7a59f3..5e98a5657e 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts @@ -10,6 +10,8 @@ import { EntitySearchResultComponent } from '../entity-search-result-component'; styleUrls: ['./publication-list-element.component.scss'], templateUrl: './publication-list-element.component.html' }) - +/** + * The component for displaying a list element for an item with entity type Publication + */ export class PublicationListElementComponent extends EntitySearchResultComponent { } From 4d24802871f40a6cec50adf80721d716923193ea Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 10:17:49 +0200 Subject: [PATCH 049/205] 56434: property docs for entity-page-fields components --- .../journal-volume-page-fields.component.ts | 7 +++++++ .../orgunit/orgunit-page-fields.component.ts | 12 +++++++++++- .../person/person-page-fields.component.ts | 19 +++++++++++++++++++ .../project/project-page-fields.component.ts | 11 +++++++++++ .../publication-page-fields.component.ts | 15 +++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts index 09e43cf427..7549fbd2ff 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts @@ -21,7 +21,14 @@ import { isNotEmpty } from '../../../../shared/empty.util'; * The component for displaying metadata and relations of an item with entity type Journal Volume */ export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent { + /** + * The journals related to this journal volume + */ journals$: Observable; + + /** + * The journal issues related to this journal volume + */ issues$: Observable; constructor( diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts index e1a2c7abe8..28385b5d54 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts @@ -21,9 +21,19 @@ import { isNotEmpty } from '../../../../shared/empty.util'; * The component for displaying metadata and relations of an item with entity type Organisation Unit */ export class OrgUnitPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { - + /** + * The people related to this organisation unit + */ people$: Observable; + + /** + * The projects related to this organisation unit + */ projects$: Observable; + + /** + * The publications related to this organisation unit + */ publications$: Observable; constructor( diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts index db74c6e1af..8c502a6ccf 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts @@ -22,10 +22,29 @@ import { isNotEmpty } from '../../../../shared/empty.util'; * The component for displaying metadata and relations of an item with entity type Person */ export class PersonPageFieldsComponent extends EntityPageFieldsComponent { + /** + * The publications related to this person + */ publications$: Observable; + + /** + * The projects related to this person + */ projects$: Observable; + + /** + * The organisation units related to this person + */ orgUnits$: Observable; + + /** + * The applied fixed filter + */ fixedFilter$: Observable; + + /** + * The query used for applying the fixed filter + */ fixedFilterQuery: string; constructor( diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts index 976b3ccec0..54bed63e6d 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts @@ -21,8 +21,19 @@ import { isNotEmpty } from '../../../../shared/empty.util'; * The component for displaying metadata and relations of an item with entity type Project */ export class ProjectPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { + /** + * The people related to this project + */ people$: Observable; + + /** + * The publications related to this project + */ publications$: Observable; + + /** + * The organisation units related to this project + */ orgUnits$: Observable; constructor( diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts index 5fcccea10e..8f2d980ec3 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts @@ -22,9 +22,24 @@ import { changeDetection: ChangeDetectionStrategy.OnPush, }) export class PublicationPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { + /** + * The authors related to this publication + */ authors$: Observable; + + /** + * The projects related to this publication + */ projects$: Observable; + + /** + * The organisation units related to this publication + */ orgUnits$: Observable; + + /** + * The journal issues related to this publication + */ journalIssues$: Observable; constructor( From e4eee7ce27996c84066486e51d2fc151e1561d89 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 11:05:32 +0200 Subject: [PATCH 050/205] 56434: Docs for all item-page-metadata-field, related-entities, item-page and metadata-values componens --- .../metadata-field-wrapper.component.ts | 3 +++ .../metadata-uri-values.component.ts | 13 +++++++++++++ .../metadata-values/metadata-values.component.ts | 9 +++++++++ .../specific-field/item-page-field.component.ts | 3 +++ 4 files changed, 28 insertions(+) diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts index 8c80384732..f528f06ba5 100644 --- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts +++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts @@ -11,6 +11,9 @@ import { Component, Input } from '@angular/core'; }) export class MetadataFieldWrapperComponent { + /** + * The label (title) for the content + */ @Input() label: string; } diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts index 212dcddee8..8ff3317e06 100644 --- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.ts @@ -16,11 +16,24 @@ import { MetadataValuesComponent } from '../metadata-values/metadata-values.comp }) export class MetadataUriValuesComponent extends MetadataValuesComponent { + /** + * Optional text to replace the links with + * If undefined, the metadata value (uri) is displayed + */ @Input() linktext: any; + /** + * The metadata values to display + */ @Input() values: any; + /** + * The seperator used to split the metadata values (can contain HTML) + */ @Input() separator: string; + /** + * The label for this iteration of metadata values + */ @Input() label: string; } diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts index 1c94b56d57..90a265b9f7 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.ts @@ -12,10 +12,19 @@ import { Metadatum } from '../../../core/shared/metadatum.model'; }) export class MetadataValuesComponent { + /** + * The metadata values to display + */ @Input() values: Metadatum[]; + /** + * The seperator used to split the metadata values (can contain HTML) + */ @Input() separator: string; + /** + * The label for this iteration of metadata values + */ @Input() label: string; } diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts index 525c898c0e..ce2b110efd 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.ts @@ -13,6 +13,9 @@ import { Item } from '../../../../core/shared/item.model'; }) export class ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; /** From 29e60f2b7f42c5040fedb893102be1410c07a766 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 11:48:23 +0200 Subject: [PATCH 051/205] 56434: docs for search-filter, search-fixed-filter and search-configuration service; search-results, filtered-search-page and search-page components --- .../item-page-abstract-field.component.ts | 17 +++++++ .../item-page-author-field.component.ts | 17 +++++++ .../date/item-page-date-field.component.ts | 17 +++++++ .../generic-item-page-field.component.ts | 18 ++++++++ .../title/item-page-title-field.component.ts | 14 ++++++ .../uri/item-page-uri-field.component.ts | 17 +++++++ .../+item-page/simple/item-page.component.ts | 15 ++++++- .../related-entities-component.ts | 16 +++++++ .../filtered-search-page.component.ts | 6 +++ .../search-filter/search-filter.service.ts | 44 +++++++++++++++++++ .../search-fixed-filter.service.ts | 14 ++++++ src/app/+search-page/search-page.component.ts | 4 ++ .../search-results.component.ts | 5 +++ .../search-configuration.service.ts | 4 ++ 14 files changed, 206 insertions(+), 2 deletions(-) diff --git a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts index d7d4c0af0e..00984d6592 100644 --- a/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts @@ -7,16 +7,33 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-item-page-abstract-field', templateUrl: '../item-page-field.component.html' }) +/** + * This component is used for displaying the abstract (dc.description.abstract) of an item + */ export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator: string; + /** + * Fields (schema.element.qualifier) used to render their values. + * In this component, we want to display values for metadata 'dc.description.abstract' + */ fields: string[] = [ 'dc.description.abstract' ]; + /** + * Label i18n key for the rendered metadata + */ label = 'item.page.abstract'; } diff --git a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts index ac566c5e53..51941d2cc8 100644 --- a/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/author/item-page-author-field.component.ts @@ -7,18 +7,35 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-item-page-author-field', templateUrl: '../item-page-field.component.html' }) +/** + * This component is used for displaying the author (dc.contributor.author, dc.creator and dc.contributor) metadata of an item + */ export class ItemPageAuthorFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator: string; + /** + * Fields (schema.element.qualifier) used to render their values. + * In this component, we want to display values for metadata 'dc.contributor.author', 'dc.creator' and 'dc.contributor' + */ fields: string[] = [ 'dc.contributor.author', 'dc.creator', 'dc.contributor' ]; + /** + * Label i18n key for the rendered metadata + */ label = 'item.page.author'; } diff --git a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts index b0f02bb718..5a7d56b7da 100644 --- a/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/date/item-page-date-field.component.ts @@ -7,16 +7,33 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-item-page-date-field', templateUrl: '../item-page-field.component.html' }) +/** + * This component is used for displaying the issue date (dc.date.issued) metadata of an item + */ export class ItemPageDateFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator = ', '; + /** + * Fields (schema.element.qualifier) used to render their values. + * In this component, we want to display values for metadata 'dc.date.issued' + */ fields: string[] = [ 'dc.date.issued' ]; + /** + * Label i18n key for the rendered metadata + */ label = 'item.page.date'; } diff --git a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts index fe1ecec57e..ee7d27a11f 100644 --- a/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts @@ -7,14 +7,32 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-generic-item-page-field', templateUrl: '../item-page-field.component.html' }) +/** + * This component can be used to represent metadata on a simple item page. + * It is the most generic way of displaying metadata values + * It expects 4 parameters: The item, a seperator, the metadata keys and an i18n key + */ export class GenericItemPageFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ @Input() separator: string; + /** + * Fields (schema.element.qualifier) used to render their values. + */ @Input() fields: string[]; + /** + * Label i18n key for the rendered metadata + */ @Input() label: string; } diff --git a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts index 3ec11c8e3d..c67d8bcf62 100644 --- a/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/title/item-page-title-field.component.ts @@ -7,12 +7,26 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-item-page-title-field', templateUrl: './item-page-title-field.component.html' }) +/** + * This component is used for displaying the title (dc.title) of an item + */ export class ItemPageTitleFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator: string; + /** + * Fields (schema.element.qualifier) used to render their values. + * In this component, we want to display values for metadata 'dc.title' + */ fields: string[] = [ 'dc.title' ]; diff --git a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts index 805fdc160f..c9cd5f1a00 100644 --- a/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts +++ b/src/app/+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component.ts @@ -7,16 +7,33 @@ import { ItemPageFieldComponent } from '../item-page-field.component'; selector: 'ds-item-page-uri-field', templateUrl: './item-page-uri-field.component.html' }) +/** + * This component is used for displaying the uri (dc.identifier.uri) metadata of an item + */ export class ItemPageUriFieldComponent extends ItemPageFieldComponent { + /** + * The item to display metadata for + */ @Input() item: Item; + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ separator: string; + /** + * Fields (schema.element.qualifier) used to render their values. + * In this component, we want to display values for metadata 'dc.identifier.uri' + */ fields: string[] = [ 'dc.identifier.uri' ]; + /** + * Label i18n key for the rendered metadata + */ label = 'item.page.uri'; } diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 1ad67d287c..fcf3f5a29b 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -30,14 +30,25 @@ import { Subscription } from 'rxjs/Subscription'; }) export class ItemPageComponent implements OnInit { + /** + * The item's id + */ id: number; - private sub: any; - + /** + * The item wrapped in a remote-data object + */ itemRD$: Observable>; + /** + * The item's thumbnail + */ thumbnail$: Observable; + /** + * The view-mode we're currently on + * @type {ElementViewMode} + */ ElementViewMode = viewMode.ElementViewMode; constructor( diff --git a/src/app/+item-page/simple/related-entities/related-entities-component.ts b/src/app/+item-page/simple/related-entities/related-entities-component.ts index b49e89c7aa..85532eacbe 100644 --- a/src/app/+item-page/simple/related-entities/related-entities-component.ts +++ b/src/app/+item-page/simple/related-entities/related-entities-component.ts @@ -7,8 +7,24 @@ import * as viewMode from '../../../shared/view-mode'; styleUrls: ['./related-entities.component.scss'], templateUrl: './related-entities.component.html' }) +/** + * This component is used for displaying relations between entities + * It expects a list of entities to display and a label to put on top + */ export class RelatedEntitiesComponent { + /** + * A list of entities to display + */ @Input() entities: Item[]; + + /** + * An i18n label to use as a title for the list (usually describes the relation) + */ @Input() label: string; + + /** + * The view-mode we're currently on + * @type {ElementViewMode} + */ ElementViewMode = viewMode.ElementViewMode } diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts index 1639c95fac..6e2875ee39 100644 --- a/src/app/+search-page/filtered-search-page.component.ts +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -39,6 +39,12 @@ export class FilteredSearchPageComponent extends SearchPageComponent { super(service, sidebarService, windowService, filterService, searchConfigService, routeService); } + /** + * Get the current paginated search options after updating the fixed filter using the fixedFilterQuery input + * This is to make sure the fixed filter is included in the paginated search options, as it is not part of any + * query or route parameters + * @returns {Observable} + */ protected getSearchOptions(): Observable { this.searchConfigService.updateFixedFilter(this.fixedFilterQuery); return this.searchConfigService.paginatedSearchOptions; diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index a8d890ae75..15ac284b7c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -57,14 +57,28 @@ export class SearchFilterService { return this.routeService.hasQueryParam(paramName); } + /** + * Fetch the current active scope from the query parameters + * @returns {Observable} + */ getCurrentScope() { return this.routeService.getQueryParameterValue('scope'); } + /** + * Fetch the current query from the query parameters + * @returns {Observable} + */ getCurrentQuery() { return this.routeService.getQueryParameterValue('query'); } + /** + * Fetch the current pagination from query parameters 'page' and 'pageSize' + * and combine them with a given pagination + * @param pagination Pagination options to combine the query parameters with + * @returns {Observable} + */ getCurrentPagination(pagination: any = {}): Observable { const page$ = this.routeService.getQueryParameterValue('page'); const size$ = this.routeService.getQueryParameterValue('pageSize'); @@ -76,6 +90,12 @@ export class SearchFilterService { }); } + /** + * Fetch the current sorting options from query parameters 'sortDirection' and 'sortField' + * and combine them with given sorting options + * @param {SortOptions} defaultSort Sorting options to combine the query parameters with + * @returns {Observable} + */ getCurrentSort(defaultSort: SortOptions): Observable { const sortDirection$ = this.routeService.getQueryParameterValue('sortDirection'); const sortField$ = this.routeService.getQueryParameterValue('sortField'); @@ -87,19 +107,37 @@ export class SearchFilterService { ); } + /** + * Fetch the current active filters from the query parameters + * @returns {Observable} + */ getCurrentFilters() { return this.routeService.getQueryParamsWithPrefix('f.'); } + /** + * Fetch the current active fixed filter from the route parameters and return the query by filter name + * @returns {Observable} + */ getCurrentFixedFilter(): Observable { const filter: Observable = this.routeService.getRouteParameterValue('filter'); return filter.flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)); } + /** + * Fetch the current view from the query parameters + * @returns {Observable} + */ getCurrentView() { return this.routeService.getQueryParameterValue('view'); } + /** + * Fetch the current paginated search options using the getters from above + * and combining them with given defaults + * @param defaults Default paginated search options + * @returns {Observable} + */ getPaginatedSearchOptions(defaults: any = {}): Observable { return Observable.combineLatest( this.getCurrentPagination(defaults.pagination), @@ -125,6 +163,12 @@ export class SearchFilterService { ) } + /** + * Fetch the current search options (not paginated) using the getters from above + * and combining them with given defaults + * @param defaults Default search options + * @returns {Observable} + */ getSearchOptions(defaults: any = {}): Observable { return Observable.combineLatest( this.getCurrentView(), diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 66fc82023a..b156aada01 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -14,6 +14,9 @@ import { hasValue } from '../../../shared/empty.util'; import { configureRequest } from '../../../core/shared/operators'; import { RouteService } from '../../../shared/services/route.service'; +/** + * Service for performing actions on the filtered-discovery-pages REST endpoint + */ @Injectable() export class SearchFixedFilterService { private queryByFilterPath = 'filtered-discovery-pages'; @@ -25,6 +28,11 @@ export class SearchFixedFilterService { } + /** + * Get the filter query for a certain filter by name + * @param {string} filterName Name of the filter + * @returns {Observable} Filter query + */ getQueryByFilterName(filterName: string): Observable { if (hasValue(filterName)) { const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( @@ -56,6 +64,12 @@ export class SearchFixedFilterService { return Observable.of(undefined); } + /** + * Get the query for looking up items by relation type + * @param {string} relationType Relation type + * @param {string} itemUUID Item UUID + * @returns {string} Query + */ getQueryByRelations(relationType: string, itemUUID: string): string { return `query=relation.${relationType}:${itemUUID}`; } diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 7b2ad5294a..7c4879409c 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -111,6 +111,10 @@ export class SearchPageComponent implements OnInit { } } + /** + * Get the current paginated search options + * @returns {Observable} + */ protected getSearchOptions(): Observable { return this.searchConfigService.paginatedSearchOptions; } diff --git a/src/app/+search-page/search-results/search-results.component.ts b/src/app/+search-page/search-results/search-results.component.ts index c9c887701b..62ef393a2e 100644 --- a/src/app/+search-page/search-results/search-results.component.ts +++ b/src/app/+search-page/search-results/search-results.component.ts @@ -41,6 +41,11 @@ export class SearchResultsComponent { @Input() fixedFilter: string; @Input() disableHeader = false; + /** + * Get the i18n key for the title depending on the fixed filter + * Defaults to 'search.results.head' if there's no fixed filter found + * @returns {string} + */ getTitleKey() { if (isNotEmpty(this.fixedFilter)) { return 'search.' + this.fixedFilter + '.results.head' diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index b2fb91417e..50526a44b6 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -316,6 +316,10 @@ export class SearchConfigurationService implements OnDestroy { ); } + /** + * Update the fixed filter in paginated and non-paginated search options with a given value + * @param {string} fixedFilter + */ public updateFixedFilter(fixedFilter: string) { const currentPaginatedValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue(); const updatedPaginatedValue: PaginatedSearchOptions = Object.assign(currentPaginatedValue, { fixedFilter: fixedFilter }); From 7914e1cfb1611e71a9efb6385f64df282f40be0d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 11:58:10 +0200 Subject: [PATCH 052/205] 56434: docs for entity-type-switcher --- .../switcher/entity-type-switcher.component.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.ts b/src/app/shared/entities/switcher/entity-type-switcher.component.ts index a981f2665c..4b910ebebc 100644 --- a/src/app/shared/entities/switcher/entity-type-switcher.component.ts +++ b/src/app/shared/entities/switcher/entity-type-switcher.component.ts @@ -13,9 +13,23 @@ export const ITEM: InjectionToken = new InjectionToken('item'); styleUrls: ['./entity-type-switcher.component.scss'], templateUrl: './entity-type-switcher.component.html' }) +/** + * Component for determining what component to use depending on the item's relationship type (relationship.type) + */ export class EntityTypeSwitcherComponent implements OnInit { + /** + * The item to determine the component for + */ @Input() object: Item | SearchResult; + + /** + * The preferred view-mode to display + */ @Input() viewMode: ElementViewMode; + + /** + * The object injector used to inject the item into the child component + */ objectInjector: Injector; constructor(private injector: Injector) { @@ -29,6 +43,10 @@ export class EntityTypeSwitcherComponent implements OnInit { } + /** + * Fetch the component depending on the item's relationship type + * @returns {string} + */ getComponent(): string { let item: Item; if (hasValue((this.object as any).dspaceObject)) { From c1eab402e86a398f405bf1aea9eb8d89ecf90d38 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 24 Oct 2018 13:03:03 +0200 Subject: [PATCH 053/205] add support for paginated properties on dspace objects --- .../builders/remote-data-build.service.ts | 16 +- .../data/base-response-parsing.service.ts | 5 +- src/app/core/shared/item.model.ts | 2 + src/app/core/shared/operators.ts | 2 +- src/app/shared/loading/loading.component.ts | 3 +- yarn.lock | 1516 +++++++++++++++++ 6 files changed, 1535 insertions(+), 9 deletions(-) diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index d934f60e48..cb4a9a31fe 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import { distinctUntilChanged, flatMap, map, startWith } from 'rxjs/operators'; +import { distinctUntilChanged, filter, flatMap, map, startWith, switchMap } from 'rxjs/operators'; import { hasValue, hasValueOperator, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { PaginatedList } from '../../data/paginated-list'; import { RemoteData } from '../../data/remote-data'; @@ -8,6 +8,8 @@ import { RemoteDataError } from '../../data/remote-data-error'; import { GetRequest } from '../../data/request.models'; import { RequestEntry } from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; +import { Bitstream } from '../../shared/bitstream.model'; +import { Item } from '../../shared/item.model'; import { NormalizedObject } from '../models/normalized-object.model'; import { ObjectCacheService } from '../object-cache.service'; @@ -190,7 +192,7 @@ export class RemoteDataBuildService { } if (hasValue(normalized[relationship].page)) { - links[relationship] = this.aggregatePaginatedList(result, normalized[relationship].pageInfo); + links[relationship] = this.toPaginatedList(result, normalized[relationship].pageInfo); } else { links[relationship] = result; } @@ -254,8 +256,14 @@ export class RemoteDataBuildService { }) } - aggregatePaginatedList(input: Observable>, pageInfo: PageInfo): Observable>> { - return input.map((rd) => Object.assign(rd, {payload: new PaginatedList(pageInfo, rd.payload)})); + private toPaginatedList(input: Observable>>, pageInfo: PageInfo): Observable>> { + return input.map((rd: RemoteData>) => { + if (Array.isArray(rd.payload)) { + return Object.assign(rd, { payload: new PaginatedList(pageInfo, rd.payload) }) + } else { + return Object.assign(rd, { payload: new PaginatedList(pageInfo, rd.payload.page) }); + } + }); } } diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index fdf5b4eb97..63468295d4 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -6,7 +6,6 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; import { GenericConstructor } from '../shared/generic-constructor'; import { PaginatedList } from './paginated-list'; -import { NormalizedObject } from '../cache/models/normalized-object.model'; import { ResourceType } from '../shared/resource-type'; import { RESTURLCombiner } from '../url-combiner/rest-url-combiner'; @@ -15,7 +14,7 @@ function isObjectLevel(halObj: any) { } function isPaginatedResponse(halObj: any) { - return isNotEmpty(halObj.page) && hasValue(halObj._embedded); + return hasValue(halObj.page) && hasValue(halObj._embedded); } /* tslint:disable:max-classes-per-file */ @@ -130,7 +129,7 @@ export abstract class BaseResponseParsingService { } processPageInfo(payload: any): PageInfo { - if (isNotEmpty(payload.page)) { + if (hasValue(payload.page)) { const pageObj = Object.assign({}, payload.page, { _links: payload._links }); const pageInfoObject = new DSpaceRESTv2Serializer(PageInfo).deserialize(pageObj); if (pageInfoObject.currentPage >= 0) { diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index cc84694e84..d15aa95cc4 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -1,4 +1,5 @@ import { Observable } from 'rxjs/Observable'; +import { filter, map, startWith, tap } from 'rxjs/operators'; import { DSpaceObject } from './dspace-object.model'; import { Collection } from './collection.model'; @@ -86,6 +87,7 @@ export class Item extends DSpaceObject { * Retrieves bitstreams by bundle name * @param bundleName The name of the Bundle that should be returned * @returns {Observable} the bitstreams with the given bundleName + * TODO now that bitstreams can be paginated this should move to the server */ getBitstreamsByBundleName(bundleName: string): Observable { return this.bitstreams diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index 07d64a83ab..d340882b8f 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -59,7 +59,7 @@ export const toDSpaceObjectListRD = () => source.pipe( map((rd: RemoteData>>) => { const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult) => searchResult.dspaceObject); - const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList; + const payload = Object.assign(rd.payload, { page: dsoPage }) as any; return Object.assign(rd, {payload: payload}); }) ); diff --git a/src/app/shared/loading/loading.component.ts b/src/app/shared/loading/loading.component.ts index 8276ab574a..91ce24ac49 100644 --- a/src/app/shared/loading/loading.component.ts +++ b/src/app/shared/loading/loading.component.ts @@ -3,6 +3,7 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs/Subscription'; +import { hasValue } from '../empty.util'; @Component({ selector: 'ds-loading', @@ -28,7 +29,7 @@ export class LoadingComponent implements OnDestroy, OnInit { } ngOnDestroy() { - if (this.subscription !== undefined) { + if (hasValue(this.subscription)) { this.subscription.unsubscribe(); } } diff --git a/yarn.lock b/yarn.lock index 81c0c807c6..ac382824c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,18 +5,21 @@ "@angular/animations@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-5.2.5.tgz#3e72184321c4979305619c74902b8be92d76db70" + integrity sha512-70ElCmaeDxLQc2OkgYhJjXj4zjtdjI4K1D5ZZm/uSPLlUcqC6uf6skCXlhMawQoPbsL/SXE5xw2HlMgEbhUysw== dependencies: tslib "^1.7.1" "@angular/common@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/common/-/common-5.2.5.tgz#08dd636fa46077d047066b13a1aae494066f6c55" + integrity sha512-jagCxo+75pcTwjuO1ZheIiTlKBJ6REFKFWoUPTzaSS6fnzReFJ+VPf4Pb0bWtHL1lWvbvnzmITOJPB9wmuM3fg== dependencies: tslib "^1.7.1" "@angular/compiler-cli@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-5.2.5.tgz#b1988bb2c0a956e7fc163acf8c7d794a07a88d08" + integrity sha512-jRFMxUKpodzOBKdZc6OMse+CjK6xfTJssZQrYeIyqz2daobaIsMZP2hZX8s/PCfV8Vxa7XFwCJb7Fq2uyZKfHg== dependencies: chokidar "^1.4.2" minimist "^1.2.0" @@ -26,42 +29,49 @@ "@angular/compiler@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-5.2.5.tgz#5e3b511906048a579fcd007aba72911472e5aa28" + integrity sha512-YU/r5omexkrrBF3bZaseWrc2Iotk6hIdUWkPIL3gPC0hKJ3wBeB3sHCBujPQXktWdMBbQRujNSMZtgra3Oh1xQ== dependencies: tslib "^1.7.1" "@angular/core@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/core/-/core-5.2.5.tgz#24f9cd75c5b2728f2ddd1869777590ea7177bca9" + integrity sha512-Uo7R3LrsvA24JkRbwXWUZWp7NSEpwdTUxT1NScyjrBK+t8ybSL5/42Jo21md5M4pjeCsIgUXlGoCm1QtT5aYnQ== dependencies: tslib "^1.7.1" "@angular/forms@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-5.2.5.tgz#2ad7a420c6ef6cd87a34071c5319ec83f7ed56aa" + integrity sha512-3feqqTuv9rIu7ZOsLCtM/ugNFz5RPujLHkE8bU1gsMM4/eMYruIFir2vbjnhMkD3K6KptEg4iO6tDW18diwXug== dependencies: tslib "^1.7.1" "@angular/http@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/http/-/http-5.2.5.tgz#1208256e36f0e486d96d10a733fdc8424ffa16bf" + integrity sha512-VqTCkAnebe+M9Bqrfp1QYpBQCTbXide/NxrQfwiJY87kjKFeRBuy9/XH/2S5wIwlF5Yx3bmlaIufd9VI5r/0aQ== dependencies: tslib "^1.7.1" "@angular/platform-browser-dynamic@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.2.5.tgz#b89df410bd953e2a6843325f9ac3c09a10eadaf0" + integrity sha512-IMEe2qUTC3CA3KoswmJJs+O2Lkyd5GXgl5ULupqhhm/TOL2FLk00kwv8k3Epaf2d1wXcjK3BMG7aAwc6RLH7QA== dependencies: tslib "^1.7.1" "@angular/platform-browser@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-5.2.5.tgz#eae4af2b742fb901d84d6367cd99f9e88102151f" + integrity sha512-iPAuoG/c3pD3hnk1g0VgJu/pzNITvLQyT0W71MDMSuxLxs291kq+U2jklm40pStISd1mPbCNKmvz/7M+WbdLhg== dependencies: tslib "^1.7.1" "@angular/platform-server@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/platform-server/-/platform-server-5.2.5.tgz#2111bfd836a16c787a10f2099a0c26b6a3a02b05" + integrity sha512-IpuEDNyoVfGO94jd1s+4IgoTBkWigwqD4YQTpcsC1mdY2Ax7NXXTAx28ZQF5EvPbSxsHGB5zG3oR7KE7GMNhYQ== dependencies: domino "^1.0.29" tslib "^1.7.1" @@ -70,44 +80,54 @@ "@angular/router@^5.2.5": version "5.2.5" resolved "https://registry.yarnpkg.com/@angular/router/-/router-5.2.5.tgz#f8f220d5fb85fc10d60fe606b0f2a64732265142" + integrity sha512-I8U0iy59lz0dAxU4zxRQHagfUPWF+MikLNMirRL1lrA49PG+5K1tiuIQ6p+8fZFAJ5UXwNHyXqYuWqsKRiVBHQ== dependencies: tslib "^1.7.1" "@angularclass/bootloader@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@angularclass/bootloader/-/bootloader-1.0.1.tgz#75de7cf3901b445900a419c2aeca44181d465060" + integrity sha1-dd5885AbRFkApBnCrspEGB1GUGA= "@ng-bootstrap/ng-bootstrap@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-1.0.0.tgz#8f2ae70db2fe1dcbf5e0acb49dc2b1bbba2be8d2" + integrity sha1-jyrnDbL+Hcv14Ky0ncKxu7or6NI= "@ng-dynamic-forms/core@5.4.7": version "5.4.7" resolved "https://registry.yarnpkg.com/@ng-dynamic-forms/core/-/core-5.4.7.tgz#203dffe4bb31a3599e906990ad9dc2b35714e37a" + integrity sha512-z0JqPHQ38YYSDp2927/dCYPCc2dvZNC9xt/rUHNx43P7zdpLAS2IkDMzk+UGvo0T6hF1VQrLwIT9l/jnG3sF+A== "@ng-dynamic-forms/ui-ng-bootstrap@5.4.7": version "5.4.7" resolved "https://registry.yarnpkg.com/@ng-dynamic-forms/ui-ng-bootstrap/-/ui-ng-bootstrap-5.4.7.tgz#66d037a226da96fe84c4dbac98e4dba859c551f8" + integrity sha512-6w0xnDEM5H0UMTpwlSYG3B7Wke9w65umRZAjs7Ti1f1jVhdDZo4snXX8Qcwc4RHRVOwi7sDJP0sXCsZ1OTalfA== "@ngrx/effects@^5.1.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@ngrx/effects/-/effects-5.2.0.tgz#aa762b69cb6fd4644d724a1cecd265caa42baf09" + integrity sha1-qnYractv1GRNckoc7NJlyqQrrwk= "@ngrx/router-store@^5.0.1": version "5.2.0" resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-5.2.0.tgz#bf4b174ce19a36eba8211fc1ddeaf1e35ae74368" + integrity sha1-v0sXTOGaNuuoIR/B3erx41rnQ2g= "@ngrx/store-devtools@^5.1.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-5.2.0.tgz#2fff916a9aa349375826772b359dbb64b9e5d622" + integrity sha1-L/+RapqjSTdYJncrNZ27ZLnl1iI= "@ngrx/store@^5.1.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-5.2.0.tgz#627ed74c9cd95462930485d912a557117b23903e" + integrity sha1-Yn7XTJzZVGKTBIXZEqVXEXsjkD4= "@ngtools/webpack@^1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-1.10.0.tgz#1e4f60bdba8cdb8aeb93ebb976e63bf175acd279" + integrity sha512-QdzPjUgb1VIJ/uIMXGe5mJsufp0gxItDZj/3RD+xfqYCgabLRFPZ89qe88tTCzhburuEfHK6+Uqk5HWlVnA4oA== dependencies: chalk "~2.2.0" enhanced-resolve "^3.1.0" @@ -121,28 +141,34 @@ "@nguniversal/express-engine@5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@nguniversal/express-engine/-/express-engine-5.0.0.tgz#e218fa9fbc7b47379bc106f69ada274e05631243" + integrity sha512-USYwZDUymQjxEf3SahdSQIK6qAWNPFDYmBmcxdPxLE3DZCMl0W+PSAv2P6sJoU3SVvaQeW8VA8iiSQy8+0iXlw== "@ngx-translate/core@9.1.1": version "9.1.1" resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-9.1.1.tgz#ae103928836b8a9e069fd2e2e76fa2198cc7e628" + integrity sha1-rhA5KINrip4Gn9Li52+iGYzH5ig= "@ngx-translate/http-loader@2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@ngx-translate/http-loader/-/http-loader-2.0.1.tgz#aa67788e64bfa8652691a77b022b3b4031209113" + integrity sha1-qmd4jmS/qGUmkad7Ais7QDEgkRM= "@nicky-lenaers/ngx-scroll-to@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@nicky-lenaers/ngx-scroll-to/-/ngx-scroll-to-0.6.0.tgz#6d2922f5765a472e3c86499d9e53df5ca210e637" + integrity sha512-wKZKq++kzyXehq8poBJlyU0RyNmFoUCFkwzXQ66sgYkISW0ylp+MQO4F6MiHlHZj4eviZHQ8vVLC4IUGf8skmA== "@types/acorn@^4.0.3": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.3.tgz#d1f3e738dde52536f9aad3d3380d14e448820afd" + integrity sha512-gou/kWQkGPMZjdCKNZGDpqxLm9+ErG/pFZKPX4tvCjr0Xf4FCYYX3nAsu7aDVKJV3KUe27+mvqqyWT/9VZoM/A== dependencies: "@types/estree" "*" "@types/body-parser@*": version "1.16.8" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.16.8.tgz#687ec34140624a3bec2b1a8ea9268478ae8f3be3" + integrity sha512-BdN2PXxOFnTXFcyONPW6t0fHjz2fvRZHVMFpaS0wYr+Y8fWEaNOs4V8LEu/fpzQlMx+ahdndgTaGTwPC+J/EeA== dependencies: "@types/express" "*" "@types/node" "*" @@ -150,30 +176,36 @@ "@types/cookie-parser@1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.1.tgz#e88a39c41960f31549b4c52dd8620aa4a2feb4bb" + integrity sha512-iJY6B3ZGufLiDf2OCAgiAAQuj1sMKC/wz/7XCEjZ+/MDuultfFJuSwrBKcLSmJ5iYApLzCCYBYJZs0Ws8GPmwA== dependencies: "@types/express" "*" "@types/deep-freeze@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@types/deep-freeze/-/deep-freeze-0.1.1.tgz#0e1ee6ceee06f51baeb663deec0bb7780bd72827" + integrity sha512-nP6a+Z7pFv8aag1N6XOI68vaM11jjpI6sSig9fPTPDP6cc33gLXbotarnDzjGjoEkXRPWcSGKeTT8qkgh936Pw== "@types/estree@*": version "0.0.38" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.38.tgz#c1be40aa933723c608820a99a373a16d215a1ca2" + integrity sha512-F/v7t1LwS4vnXuPooJQGBRKRGIoxWUTmA4VHfqjOccFsNDThD5bfUNpITive6s352O7o384wcpEaDV8rHCehDA== "@types/events@*": version "1.1.0" resolved "https://registry.yarnpkg.com/@types/events/-/events-1.1.0.tgz#93b1be91f63c184450385272c47b6496fd028e02" + integrity sha512-y3bR98mzYOo0pAZuiLari+cQyiKk3UXRuT45h1RjhfeCzqkjaVsfZJNaxdgtk7/3tzOm1ozLTqEqMP3VbI48jw== "@types/express-serve-static-core@*": version "4.0.53" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.0.53.tgz#1723a35d1447f2c55e13c8721eab3448e42f4d82" + integrity sha512-zaGeOpEYp5G2EhjaUFdVwysDrfEYc6Q6iPhd3Kl4ip30x0tvVv7SuJvY3yzCUSuFlzAG8N5KsyY6BJg93/cn+Q== dependencies: "@types/node" "*" "@types/express-serve-static-core@4.11.1": version "4.11.1" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.11.1.tgz#f6f7212382d59b19d696677bcaa48a37280f5d45" + integrity sha512-EehCl3tpuqiM8RUb+0255M8PhhSwTtLfmO7zBBdv0ay/VTd/zmrqDfQdZFsa5z/PVMbH2yCMZPXsnrImpATyIw== dependencies: "@types/events" "*" "@types/node" "*" @@ -181,6 +213,7 @@ "@types/express@*": version "4.0.37" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.0.37.tgz#625ac3765169676e01897ca47011c26375784971" + integrity sha512-tIULTLzQpFFs5/PKnFIAFOsXQxss76glppbVKR3/jddPK26SBsD5HF5grn5G2jOGtpRWSBvYmDYoduVv+3wOXg== dependencies: "@types/express-serve-static-core" "*" "@types/serve-static" "*" @@ -188,6 +221,7 @@ "@types/express@^4.11.1": version "4.11.1" resolved "https://registry.yarnpkg.com/@types/express/-/express-4.11.1.tgz#f99663b3ab32d04cb11db612ef5dd7933f75465b" + integrity sha512-ttWle8cnPA5rAelauSWeWJimtY2RsUf2aspYZs7xPHiWgOlPn6nnUfBMtrkcnjFJuIHJF4gNOdVvpLK2Zmvh6g== dependencies: "@types/body-parser" "*" "@types/express-serve-static-core" "*" @@ -196,72 +230,89 @@ "@types/fs-extra@4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-4.0.0.tgz#1dd742ad5c9bce308f7a52d02ebc01421bc9102f" + integrity sha512-PlKJw6ujJXLJjbvB3T0UCbY3jibKM6/Ya5cc9j1q+mYDeK3aR4Dp+20ZwxSuvJr9mIoPxp7+IL4aMOEvsscRTA== dependencies: "@types/node" "*" "@types/hammerjs@2.0.35": version "2.0.35" resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.35.tgz#7b7c950c7d54593e23bffc8d2b4feba9866a7277" + integrity sha512-4mUIMSZ2U4UOWq1b+iV7XUTE4w+Kr3x+Zb/Qz5ROO6BTZLw2c8/ftjq0aRgluguLs4KRuBnrOy/s389HVn1/zA== "@types/handlebars@4.0.31": version "4.0.31" resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.31.tgz#a7fba66fafe42713aee88eeca8db91192efe6e72" + integrity sha1-p/umb6/kJxOu6I7sqNuRGS7+bnI= "@types/highlight.js@9.1.8": version "9.1.8" resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.1.8.tgz#d227f18bcb8f3f187e16965f2444859a04689758" + integrity sha1-0ifxi8uPPxh+FpZfJESFmgRol1g= "@types/jasmine@^2.8.6": version "2.8.6" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.8.6.tgz#14445b6a1613cf4e05dd61c3c3256d0e95c0421e" + integrity sha512-clg9raJTY0EOo5pVZKX3ZlMjlYzVU73L71q5OV1jhE2Uezb7oF94jh4CvwrW6wInquQAdhOxJz5VDF2TLUGmmA== "@types/js-cookie@2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.1.0.tgz#a8916246aa994db646c66d54c854916213300a51" + integrity sha512-vPT5MV1pD71RFUD0ytp6Yw51W6zKJ9Qn2AcJXSD2TZqYKaXUtCxB3WZIXXFZtbAEVMgC59nmvVPSOH0EIvkaZg== "@types/lodash@4.14.74": version "4.14.74" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.74.tgz#ac3bd8db988e7f7038e5d22bd76a7ba13f876168" + integrity sha512-BZknw3E/z3JmCLqQVANcR17okqVTPZdlxvcIz0fJiJVLUCbSH1hK3zs9r634PVSmrzAxN+n/fxlVRiYoArdOIQ== "@types/marked@0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.3.0.tgz#583c223dd33385a1dda01aaf77b0cd0411c4b524" + integrity sha512-CSf9YWJdX1DkTNu9zcNtdCcn6hkRtB5ILjbhRId4ZOQqx30fXmdecuaXhugQL6eyrhuXtaHJ7PHI+Vm7k9ZJjg== "@types/memory-cache@0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/memory-cache/-/memory-cache-0.2.0.tgz#b654a7757b5ca49d1d499bc0e4ce52a4afea2d71" + integrity sha512-zDj+12RJaigGdbd00auPN2KXeQyPVv1ad+54Dcm+cVgiI7cWXher3s9fPZwHMLr1Ji2fcVhccmtDa2mIBlvnlQ== "@types/mime@*", "@types/mime@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b" + integrity sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA== "@types/minimatch@2.0.29": version "2.0.29" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a" + integrity sha1-UALhT3Xi1x5WQoHfBDHIwbSio2o= "@types/node@*": version "8.0.34" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.34.tgz#55f801fa2ddb2a40dd6dfc15ecfe1dde9c129fe9" + integrity sha512-Jnmm57+nHqvJUPwUzt1CLoLzFtF2B2vgG7cWFut+a4nqTp9/L6pL0N+o0Jt3V7AQnCKMsPEqQpLFZYleBCdq3w== "@types/node@^6.0.46": version "6.0.88" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.88.tgz#f618f11a944f6a18d92b5c472028728a3e3d4b66" + integrity sha512-bYDPZTX0/s1aihdjLuAgogUAT5M+TpoWChEMea2p0yOcfn5bu3k6cJb9cp6nw268XeSNIGGr+4+/8V5K6BGzLQ== "@types/node@^9.4.6": version "9.4.6" resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e" + integrity sha512-CTUtLb6WqCCgp6P59QintjHWqzf4VL1uPA27bipLAPxFqrtK1gEYllePzTICGqQ8rYsCbpnsNypXjjDzGAAjEQ== "@types/q@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" + integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= "@types/selenium-webdriver@^2.53.35", "@types/selenium-webdriver@~2.53.39": version "2.53.42" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac" + integrity sha1-dMt3+2BS7a/yqJhN2v2I1BnyXKw= "@types/serve-static@*": version "1.7.32" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.7.32.tgz#0f6732e4dab0813771dd8fc8fe14940f34728b4c" + integrity sha512-WpI0g7M1FiOmJ/a97Qrjafq2I938tjAZ3hZr9O7sXyA6oUhH3bqUNZIt7r1KZg8TQAKxcvxt6JjQ5XuLfIBFvg== dependencies: "@types/express-serve-static-core" "*" "@types/mime" "*" @@ -269,6 +320,7 @@ "@types/serve-static@1.13.1": version "1.13.1" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.1.tgz#1d2801fa635d274cd97d4ec07e26b21b44127492" + integrity sha512-jDMH+3BQPtvqZVIcsH700Dfi8Q3MIcEx16g/VdxjoqiGR/NntekB10xdBpirMKnPe9z2C5cBmL0vte0YttOr3Q== dependencies: "@types/express-serve-static-core" "*" "@types/mime" "*" @@ -276,30 +328,36 @@ "@types/shelljs@0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.0.tgz#229c157c6bc1e67d6b990e6c5e18dbd2ff58cff0" + integrity sha1-IpwVfGvB5n1rmQ5sXhjb0v9Yz/A= dependencies: "@types/node" "*" "@types/strip-bom@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2" + integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I= "@types/strip-json-comments@0.0.30": version "0.0.30" resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1" + integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ== "@types/uuid@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.3.tgz#121ace265f5569ce40f4f6d0ff78a338c732a754" + integrity sha512-5fRLCYhLtDb3hMWqQyH10qtF+Ud2JnNCXTCZ+9ktNdCcgslcuXkDTkFcJNk++MT29yDntDnlF1+jD+uVGumsbw== dependencies: "@types/node" "*" "@types/webfontloader@1.6.29": version "1.6.29" resolved "https://registry.yarnpkg.com/@types/webfontloader/-/webfontloader-1.6.29.tgz#c6b5f6eb8ca31d0aae6b02b6c1300349dd93ea8e" + integrity sha512-wobuM+LvpkzU296NsFVRGDAFWw3X2XEhrLHuvV+VGSbok6aOxQcymmopUFwNB69qy5oudHt9lYC0JF+z+DxFLw== JSONStream@^1.0.3: version "1.3.2" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + integrity sha1-wQI3G27Dp887hHygDCC7D85Mbeo= dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" @@ -307,10 +365,12 @@ JSONStream@^1.0.3: abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= accepts@1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + integrity sha1-w8p0NJOGSMPg2cHjKN1otiLChMo= dependencies: mime-types "~2.1.11" negotiator "0.6.1" @@ -318,6 +378,7 @@ accepts@1.3.3: accepts@~1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f" + integrity sha1-hiRnWMfdbSGmR0/whKR0DsBesh8= dependencies: mime-types "~2.1.16" negotiator "0.6.1" @@ -325,32 +386,39 @@ accepts@~1.3.4: acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" + integrity sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ= dependencies: acorn "^4.0.3" acorn@^4.0.1, acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" + integrity sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c= acorn@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.2.tgz#911cb53e036807cf0fa778dc5d370fbd864246d7" + integrity sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA== acorn@^5.2.1: version "5.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" + integrity sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug== acorn@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" + integrity sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ== addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" + integrity sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y= adjust-sourcemap-loader@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-1.1.0.tgz#412d92404eb61e4113635012cba53a33d008e0e2" + integrity sha1-QS2SQE62HkETY1ASy6U6M9AI4OI= dependencies: assert "^1.3.0" camelcase "^1.2.1" @@ -363,18 +431,22 @@ adjust-sourcemap-loader@^1.1.0: adm-zip@0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.4.tgz#a61ed5ae6905c3aea58b3a657d25033091052736" + integrity sha1-ph7VrmkFw66lizplfSUDMJEFJzY= adm-zip@^0.4.7: version "0.4.7" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.7.tgz#8606c2cbf1c426ce8c8ec00174447fd49b6eafc1" + integrity sha1-hgbCy/HEJs6MjsABdER/1Jtur8E= after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= agent-base@2: version "2.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-2.1.1.tgz#d6de10d5af6132d5bd692427d46fc538539094c7" + integrity sha1-1t4Q1a9hMtW9aSQn1G/FOFOQlMc= dependencies: extend "~3.0.0" semver "~5.0.1" @@ -382,10 +454,12 @@ agent-base@2: ajv-keywords@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" + integrity sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74= ajv@^4.9.1: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + integrity sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY= dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -393,6 +467,7 @@ ajv@^4.9.1: ajv@^5.0.0, ajv@^5.1.0: version "5.2.3" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.3.tgz#c06f598778c44c6b161abafe3466b81ad1814ed2" + integrity sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI= dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" @@ -402,6 +477,7 @@ ajv@^5.0.0, ajv@^5.1.0: ajv@^6.1.0, ajv@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e" + integrity sha1-l41Zf7wrfQ5aXD3esUmmgvKr+g4= dependencies: fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" @@ -410,6 +486,7 @@ ajv@^6.1.0, ajv@^6.1.1: align-text@^0.1.1, align-text@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= dependencies: kind-of "^3.0.2" longest "^1.0.1" @@ -418,14 +495,17 @@ align-text@^0.1.1, align-text@^0.1.3: alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= amdefine@>=0.0.4, amdefine@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= amqplib@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63" + integrity sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA== dependencies: bitsyntax "~0.0.4" bluebird "^3.4.6" @@ -436,86 +516,103 @@ amqplib@^0.5.2: angular-idle-preload@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/angular-idle-preload/-/angular-idle-preload-2.0.4.tgz#7b177c0f52918c090e5c345480b922297cd59a0d" + integrity sha512-D+BY07Zv5yfEAKSBRREcAet1umbJ+TLi4z/dSFLbP5/3iigQUB9rDChlgILUbm/0rDSG+cerGHO+SxMUJAkJUQ== angular-sortablejs@^2.5.0: version "2.5.2" resolved "https://registry.yarnpkg.com/angular-sortablejs/-/angular-sortablejs-2.5.2.tgz#ffd651e47cc93a191db4c023f617db3789fd9af5" + integrity sha512-VpsnlDL4zl9icMi4gp3ZT+ZfmzO/jlQto7d0gW1UU6kz8ojCQq2Tooub37kV5WNhWkSkN0QwgkKA9FKhG0bUZw== angular2-moment@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/angular2-moment/-/angular2-moment-1.9.0.tgz#d198a4d9bc825f61de19106ac7ea07a78569f5a1" + integrity sha512-ybPjYizpKVWAI2Z4AqxAS6s3FMkF3+zRpfvxX1wIdSJUFjl83XxQ5f2yn7retX68NSYZZ/JTK9KGnvOzZfrIZw== dependencies: moment "^2.19.3" angular2-template-loader@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/angular2-template-loader/-/angular2-template-loader-0.6.2.tgz#c0d44e90fff0fac95e8b23f043acda7fd1c51d7c" + integrity sha1-wNROkP/w+sleiyPwQ6zaf9HFHXw= dependencies: loader-utils "^0.2.15" angular2-text-mask@8.0.4: version "8.0.4" resolved "https://registry.yarnpkg.com/angular2-text-mask/-/angular2-text-mask-8.0.4.tgz#07e485746cfb9f27e710b27b2785eac4cc4871fc" + integrity sha1-B+SFdGz7nyfnELJ7J4XqxMxIcfw= dependencies: text-mask-core "^5.0.0" angulartics2@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/angulartics2/-/angulartics2-5.2.0.tgz#5bac82d4b6acf798b7db906488861e70b49fe04c" + integrity sha512-6uRX6uTLEtgwFAfPAXYWexnpndWcHKoeymDrwnh95OI5KMKgdPvWC2u3amHtap5iHzpWOff5Z8322kKGTU5pgw== dependencies: tslib "^1.7.1" ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + integrity sha1-w2rsy6VjuJzrVW82kPCx2eNUf38= dependencies: string-width "^2.0.0" ansi-cyan@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" + integrity sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM= dependencies: ansi-wrap "0.1.0" ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= ansi-red@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" + integrity sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw= dependencies: ansi-wrap "0.1.0" ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= ansi-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= ansi-styles@^3.1.0, ansi-styles@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" + integrity sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug== dependencies: color-convert "^1.9.0" ansi-wrap@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" + integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= any-promise@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-0.1.0.tgz#830b680aa7e56f33451d4b049f3bd8044498ee27" + integrity sha1-gwtoCqflbzNFHUsEnzvYBESY7ic= anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== dependencies: micromatch "^2.1.5" normalize-path "^2.0.0" @@ -523,6 +620,7 @@ anymatch@^1.3.0: anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== dependencies: micromatch "^3.1.4" normalize-path "^2.1.1" @@ -530,14 +628,17 @@ anymatch@^2.0.0: app-root-path@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.0.1.tgz#cd62dcf8e4fd5a417efc664d2e5b10653c651b46" + integrity sha1-zWLc+OT9WkF+/GZNLlsQZTxlG0Y= aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== archiver-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-1.3.0.tgz#e50b4c09c70bf3d680e32ff1b7994e9f9d895174" + integrity sha1-5QtMCccL89aA4y/xt5lOn52JUXQ= dependencies: glob "^7.0.0" graceful-fs "^4.1.0" @@ -549,6 +650,7 @@ archiver-utils@^1.3.0: archiver@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/archiver/-/archiver-1.3.0.tgz#4f2194d6d8f99df3f531e6881f14f15d55faaf22" + integrity sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI= dependencies: archiver-utils "^1.3.0" async "^2.0.0" @@ -563,6 +665,7 @@ archiver@1.3.0: are-we-there-yet@~1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + integrity sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0= dependencies: delegates "^1.0.0" readable-stream "^2.0.6" @@ -570,12 +673,14 @@ are-we-there-yet@~1.1.2: argparse@^1.0.7: version "1.0.9" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + integrity sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY= dependencies: sprintf-js "~1.0.2" arr-diff@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" + integrity sha1-aHwydYFjWI/vfeezb6vklesaOZo= dependencies: arr-flatten "^1.0.1" array-slice "^0.2.3" @@ -583,48 +688,59 @@ arr-diff@^1.0.1: arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= dependencies: arr-flatten "^1.0.1" arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= arr-flatten@^1.0.1, arr-flatten@^1.0.3, arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== arr-union@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" + integrity sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0= arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= array-differ@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + integrity sha1-7/UuN1gknTO+QCuLuOVkuytdQDE= array-filter@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= array-flatten@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" + integrity sha1-Qmu52oQJDBg42BLIFQryCoMx4pY= array-includes@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= dependencies: define-properties "^1.1.2" es-abstract "^1.7.0" @@ -632,44 +748,54 @@ array-includes@^3.0.3: array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= array-slice@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" + integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU= array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= dependencies: array-uniq "^1.0.1" array-uniq@^1.0.1, array-uniq@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= arraybuffer.slice@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== arrify@^1.0.0, arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= asn1.js@^4.0.0: version "4.9.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" + integrity sha1-SLokC0WpKA6UdImQull9IWYX/UA= dependencies: bn.js "^4.0.0" inherits "^2.0.1" @@ -678,94 +804,114 @@ asn1.js@^4.0.0: asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + integrity sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y= assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assert-plus@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + integrity sha1-104bh+ev/A24qttwIfP+SBAasjQ= assert@^1.1.1, assert@^1.3.0, assert@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= dependencies: util "0.10.3" assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= ast-types@0.x.x: version "0.10.1" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd" + integrity sha512-UY7+9DPzlJ9VM8eY0b2TUZcZvF+1pO0hzMtAyjBYKhOmnvRlqYNYnWdtsMj0V16CGaMlpL0G1jnLbLo4AyotuQ== astw@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/astw/-/astw-2.2.0.tgz#7bd41784d32493987aeb239b6b4e1c57a873b917" + integrity sha1-e9QXhNMkk5h66yOba04cV6hzuRc= dependencies: acorn "^4.0.3" async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + integrity sha1-GdOGodntxufByF04iu28xW0zYC0= async-foreach@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" + integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== async@1.x, async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= async@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/async/-/async-2.0.1.tgz#b709cc0280a9c36f09f4536be823c838a9049e25" + integrity sha1-twnMAoCpw28J9FNr6CPIOKkEniU= dependencies: lodash "^4.8.0" async@^2.0.0, async@^2.1.2, async@^2.1.5: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" + integrity sha1-YqVrJ5yYoR0JhwlqAcw+6463u9c= dependencies: lodash "^4.14.0" async@^2.4.1, async@^2.5.0: version "2.6.0" resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + integrity sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw== dependencies: lodash "^4.14.0" async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= async@~2.1.2: version "2.1.5" resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" + integrity sha1-5YfGhYCZSsZ/xW/4bTrFa9voELw= dependencies: lodash "^4.14.0" asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= atob@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" + integrity sha1-GcenYEc3dEaPILLS0DNyrX1Mv10= atob@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/atob/-/atob-1.1.3.tgz#95f13629b12c3a51a5d215abdce2aa9f32f80773" + integrity sha1-lfE2KbEsOlGl0hWr3OKqnzL4B3M= autoprefixer@^6.3.1: version "6.7.7" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + integrity sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ= dependencies: browserslist "^1.7.6" caniuse-db "^1.0.30000634" @@ -777,6 +923,7 @@ autoprefixer@^6.3.1: autoprefixer@^7.1.1: version "7.1.5" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.1.5.tgz#d65d14b83c7cd1dd7bc801daa00557addf5a06b2" + integrity sha512-sMN453qIm8Z+tunzYWW+Y490wWkICHhCYm/VohLjjl+N7ARSFuF5au7E6tr7oEbeeXj8mNjpSw2kxjJaO6YCOw== dependencies: browserslist "^2.5.0" caniuse-lite "^1.0.30000744" @@ -788,6 +935,7 @@ autoprefixer@^7.1.1: autoprefixer@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.0.0.tgz#c19e480f061013127c373df0b01cf46919943f74" + integrity sha512-XBEqAoESCyGu3daYmWcTC37Dwmjvs0y40UtUO3MMX+Pd/w7jwNFfUKNtxoMFu0u0wcotP+arDpU3JVH54UV79Q== dependencies: browserslist "^3.0.0" caniuse-lite "^1.0.30000808" @@ -799,6 +947,7 @@ autoprefixer@^8.0.0: awesome-typescript-loader@3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/awesome-typescript-loader/-/awesome-typescript-loader-3.4.1.tgz#22fa49800f0619ec18ab15383aef93b95378dea9" + integrity sha512-fYxBtN6s4Dm6vtsROWi8IQ4I+KcmwRWePAVvhI06mFcHbtHfZopOs4qGNu9LyCPEw403LDROKFA+NVV6ig5yNw== dependencies: colors "^1.1.2" enhanced-resolve "3.3.0" @@ -812,24 +961,29 @@ awesome-typescript-loader@3.4.1: aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8= aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + integrity sha1-g+9cqGCysy5KDe7e6MdxudtXRx4= axios@^0.15.3: version "0.15.3" resolved "https://registry.yarnpkg.com/axios/-/axios-0.15.3.tgz#2c9d638b2e191a08ea1d6cc988eadd6ba5bdc053" + integrity sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM= dependencies: follow-redirects "1.0.0" babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= dependencies: chalk "^1.1.3" esutils "^2.0.2" @@ -838,6 +992,7 @@ babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: babel-generator@^6.18.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" + integrity sha1-rBriAHC3n248odMmlhMFN3TyDcU= dependencies: babel-messages "^6.23.0" babel-runtime "^6.26.0" @@ -851,12 +1006,14 @@ babel-generator@^6.18.0: babel-messages@^6.23.0: version "6.23.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= dependencies: babel-runtime "^6.22.0" babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= dependencies: core-js "^2.4.0" regenerator-runtime "^0.11.0" @@ -864,6 +1021,7 @@ babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: babel-template@^6.16.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= dependencies: babel-runtime "^6.26.0" babel-traverse "^6.26.0" @@ -874,6 +1032,7 @@ babel-template@^6.16.0: babel-traverse@^6.18.0, babel-traverse@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= dependencies: babel-code-frame "^6.26.0" babel-messages "^6.23.0" @@ -888,6 +1047,7 @@ babel-traverse@^6.18.0, babel-traverse@^6.26.0: babel-types@^6.18.0, babel-types@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= dependencies: babel-runtime "^6.26.0" esutils "^2.0.2" @@ -897,38 +1057,47 @@ babel-types@^6.18.0, babel-types@^6.26.0: babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== backo2@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= balanced-match@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" + integrity sha1-tQS9BYabOSWd0MXvw12EMXbczEo= balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= base64-arraybuffer@0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= base64-js@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" + integrity sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw== base64id@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" + integrity sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY= base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== dependencies: cache-base "^1.0.1" class-utils "^0.3.5" @@ -941,32 +1110,38 @@ base@^0.11.1: basic-auth@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.0.tgz#015db3f353e02e56377755f962742e8981e7bbba" + integrity sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o= dependencies: safe-buffer "5.1.1" batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= bcrypt-pbkdf@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + integrity sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40= dependencies: tweetnacl "^0.14.3" beeper@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" + integrity sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak= better-assert@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= dependencies: callsite "1.0.0" bfj-node4@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.2.1.tgz#3a6aa2730cf6911ba2afb836c2f88f015d718f3f" + integrity sha512-w+OTPD/R0AvDVR/sy/uVUVeoCpEgUoYj9/1P2zB6mR1yx7F/ADzLX4nlvZ/91WWzGgdZnuLxWP/J89D7ZDt0DA== dependencies: bluebird "^3.5.1" check-types "^7.3.0" @@ -975,56 +1150,67 @@ bfj-node4@^5.2.0: big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== binary-extensions@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" + integrity sha1-muuabF6IY4qtFx4Wf1kAq+JINdA= bitsyntax@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82" + integrity sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI= dependencies: buffer-more-ints "0.0.2" bl@^1.0.0: version "1.2.1" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" + integrity sha1-ysMo977kVzDUBLaSID/LWQ4XLV4= dependencies: readable-stream "^2.0.5" bl@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + integrity sha1-/cqHGplxOqANGeO7ukHER4emU5g= dependencies: readable-stream "~2.0.5" blob@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" + integrity sha1-vPEwUspURj8w+fx+lbmkdjCpSSE= block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= dependencies: inherits "~2.0.0" blocking-proxy@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz#81d6fd1fe13a4c0d6957df7f91b75e98dac40cb2" + integrity sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA== dependencies: minimist "^1.2.0" bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.4.7, bluebird@^3.5.0, bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== body-parser@1.18.2, body-parser@^1.16.1: version "1.18.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" + integrity sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ= dependencies: bytes "3.0.0" content-type "~1.0.4" @@ -1040,6 +1226,7 @@ body-parser@1.18.2, body-parser@^1.16.1: bonjour@^3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= dependencies: array-flatten "^2.1.0" deep-equal "^1.0.1" @@ -1051,32 +1238,38 @@ bonjour@^3.5.0: boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + integrity sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8= dependencies: hoek "2.x.x" boom@4.x.x: version "4.3.1" resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + integrity sha1-T4owBctKfjiJ90kDD9JbluAdLjE= dependencies: hoek "4.x.x" boom@5.x.x: version "5.2.0" resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + integrity sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw== dependencies: hoek "4.x.x" bootstrap@4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.1.3.tgz#0eb371af2c8448e8c210411d0cb824a6409a12be" + integrity sha512-rDFIzgXcof0jDyjNosjv4Sno77X4KuPeFxG2XZZv1/Kc8DRVGVADdoQyyOVDwPqL36DDmtCQbrpMCqvpPLJQ0w== boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + integrity sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw== dependencies: ansi-align "^2.0.0" camelcase "^4.0.0" @@ -1089,6 +1282,7 @@ boxen@^1.2.1: brace-expansion@^1.1.7: version "1.1.8" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + integrity sha1-wHshHHyVLsH479Uad+8NHTmQopI= dependencies: balanced-match "^1.0.0" concat-map "0.0.1" @@ -1096,12 +1290,14 @@ brace-expansion@^1.1.7: braces@^0.1.2: version "0.1.5" resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" + integrity sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY= dependencies: expand-range "^0.1.0" braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= dependencies: expand-range "^1.8.1" preserve "^0.2.0" @@ -1110,6 +1306,7 @@ braces@^1.8.2: braces@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.2.2.tgz#241f868c2b2690d9febeee5a7c83fbbf25d00b1b" + integrity sha1-JB+GjCsmkNn+vu5afIP7vyXQCxs= dependencies: arr-flatten "^1.0.3" array-unique "^0.3.2" @@ -1126,6 +1323,7 @@ braces@^2.2.2: braces@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.0.tgz#a46941cb5fb492156b3d6a656e06c35364e3e66e" + integrity sha512-P4O8UQRdGiMLWSizsApmXVQDBS6KCt7dSexgLKBmH5Hr1CZq7vsnscFh8oR1sP1ab1Zj0uCHCEzZeV6SfUf3rA== dependencies: arr-flatten "^1.1.0" array-unique "^0.3.2" @@ -1142,10 +1340,12 @@ braces@^2.3.0: brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= browser-pack@^6.0.1: version "6.0.3" resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.3.tgz#91ca96518583ef580ab063a309de62e407767a39" + integrity sha512-Jo+RYsn8X8OhyP9tMXXg0ueR2fW696HUu1Hf3/DeiwNean1oGiPtdgGRNuUHBpPHzBH3x4n1kzAlgOgHSIq88g== dependencies: JSONStream "^1.0.3" combine-source-map "~0.8.0" @@ -1157,12 +1357,14 @@ browser-pack@^6.0.1: browser-resolve@^1.11.0, browser-resolve@^1.7.0: version "1.11.2" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + integrity sha1-j/CbCixCFxihBRwmCzLkj0QpOM4= dependencies: resolve "1.1.7" browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.0.8" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.8.tgz#c8fa3b1b7585bb7ba77c5560b60996ddec6d5309" + integrity sha512-WYCMOT/PtGTlpOKFht0YJFYcPy6pLCR98CtWfzK13zoynLlBMvAdEMSRGmgnJCw2M2j/5qxBkinZQFobieM8dQ== dependencies: buffer-xor "^1.0.3" cipher-base "^1.0.0" @@ -1174,6 +1376,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4: browserify-cipher@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + integrity sha1-mYgkSHS/XtTijalWZtzWasj8Njo= dependencies: browserify-aes "^1.0.4" browserify-des "^1.0.0" @@ -1182,6 +1385,7 @@ browserify-cipher@^1.0.0: browserify-des@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" + integrity sha1-2qJ3cXRwki7S/hhZQRihdUOXId0= dependencies: cipher-base "^1.0.1" des.js "^1.0.0" @@ -1190,6 +1394,7 @@ browserify-des@^1.0.0: browserify-rsa@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= dependencies: bn.js "^4.1.0" randombytes "^2.0.1" @@ -1197,6 +1402,7 @@ browserify-rsa@^4.0.0: browserify-sign@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= dependencies: bn.js "^4.1.1" browserify-rsa "^4.0.0" @@ -1209,18 +1415,21 @@ browserify-sign@^4.0.0: browserify-zlib@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0= dependencies: pako "~0.2.0" browserify-zlib@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== dependencies: pako "~1.0.5" browserify@^14.5.0: version "14.5.0" resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.5.0.tgz#0bbbce521acd6e4d1d54d8e9365008efb85a9cc5" + integrity sha512-gKfOsNQv/toWz+60nSPfYzuwSEdzvV2WdxrVPUbPD/qui44rAkB3t3muNtmmGYHqrG56FGwX9SUEQmzNLAeS7g== dependencies: JSONStream "^1.0.3" assert "^1.4.0" @@ -1273,6 +1482,7 @@ browserify@^14.5.0: browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: version "1.7.7" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + integrity sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk= dependencies: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" @@ -1280,6 +1490,7 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: browserslist@^2.0.0, browserslist@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.5.1.tgz#68e4bc536bbcc6086d62843a2ffccea8396821c6" + integrity sha512-jAvM2ku7YDJ+leAq3bFH1DE0Ylw+F+EQDq4GkqZfgPEqpWYw9ofQH85uKSB9r3Tv7XDbfqVtE+sdvKJW7IlPJA== dependencies: caniuse-lite "^1.0.30000744" electron-to-chromium "^1.3.24" @@ -1287,6 +1498,7 @@ browserslist@^2.0.0, browserslist@^2.5.0: browserslist@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.1.0.tgz#6a1ccc302ddf48e70480e2ee1a9acc293eceb306" + integrity sha512-pyoJs5teqQWTdwOTG7F5IDKi7hMvifd9ri3EYLG2ElXlA2AwvqB1SZ6RIPMRHpmYb0RYN8N7GSERey5WgxSCUQ== dependencies: caniuse-lite "^1.0.30000808" electron-to-chromium "^1.3.33" @@ -1294,26 +1506,32 @@ browserslist@^3.0.0: buffer-crc32@^0.2.1: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= buffer-es6@^4.9.1: version "4.9.3" resolved "https://registry.yarnpkg.com/buffer-es6/-/buffer-es6-4.9.3.tgz#f26347b82df76fd37e18bcb5288c4970cfd5c404" + integrity sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ= buffer-indexof@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== buffer-more-ints@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c" + integrity sha1-JrOIXRD6E9t/wBquOquHAZngEkw= buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= buffer@^4.3.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1322,6 +1540,7 @@ buffer@^4.3.0: buffer@^5.0.2: version "5.0.8" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24" + integrity sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA== dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" @@ -1329,6 +1548,7 @@ buffer@^5.0.2: buildmail@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/buildmail/-/buildmail-4.0.1.tgz#877f7738b78729871c9a105e3b837d2be11a7a72" + integrity sha1-h393OLeHKYccmhBeO4N9K+EaenI= dependencies: addressparser "1.0.1" libbase64 "0.1.0" @@ -1341,18 +1561,22 @@ buildmail@4.0.1: builtin-modules@^1.0.0, builtin-modules@^1.1.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= cacache@^10.0.1: version "10.0.2" resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.2.tgz#105a93a162bbedf3a25da42e1939ed99ffb145f8" + integrity sha512-dljb7dk1jqO5ogE+dRpoR9tpHYv5xz9vPSNunh1+0wRuNdYxmzp9WmsyokgW/DUF1FDRVA/TMsmxt027R8djbQ== dependencies: bluebird "^3.5.0" chownr "^1.0.1" @@ -1371,6 +1595,7 @@ cacache@^10.0.1: cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== dependencies: collection-visit "^1.0.0" component-emitter "^1.2.1" @@ -1385,14 +1610,17 @@ cache-base@^1.0.1: cached-path-relative@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7" + integrity sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc= callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= camel-case@3.0.x: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M= dependencies: no-case "^2.2.0" upper-case "^1.1.1" @@ -1400,6 +1628,7 @@ camel-case@3.0.x: camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= dependencies: camelcase "^2.0.0" map-obj "^1.0.0" @@ -1407,22 +1636,27 @@ camelcase-keys@^2.0.0: camelcase@^1.0.2, camelcase@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + integrity sha1-tTTnxzTE+B7F++isoq0kNUuWLGw= dependencies: browserslist "^1.3.6" caniuse-db "^1.0.30000529" @@ -1432,6 +1666,7 @@ caniuse-api@^1.5.2: caniuse-api@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-2.0.0.tgz#b1ddb5a5966b16f48dc4998444d4bbc6c7d9d834" + integrity sha1-sd21pZZrFvSNxJmERNS7xsfZ2DQ= dependencies: browserslist "^2.0.0" caniuse-lite "^1.0.0" @@ -1441,30 +1676,37 @@ caniuse-api@^2.0.0: caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: version "1.0.30000740" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000740.tgz#03fcaaa176e3ed075895f72d46c1a12149bbeac9" + integrity sha1-A/yqoXbj7QdYlfctRsGhIUm76sk= caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000744: version "1.0.30000745" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000745.tgz#20d6fede1157a4935133502946fc7e0e6b880da5" + integrity sha1-INb+3hFXpJNRM1ApRvx+DmuIDaU= caniuse-lite@^1.0.30000697, caniuse-lite@^1.0.30000808: version "1.0.30000808" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000808.tgz#7d759b5518529ea08b6705a19e70dbf401628ffc" + integrity sha512-vT0JLmHdvq1UVbYXioxCXHYdNw55tyvi+IUWyX0Zeh1OFQi2IllYtm38IJnSgHWCv/zUnX1hdhy3vMJvuTNSqw== capture-stack-trace@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + integrity sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0= caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + integrity sha1-cVuW6phBWTzDMGeSP17GDr2k99c= caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= dependencies: align-text "^0.1.3" lazy-cache "^1.0.3" @@ -1472,12 +1714,14 @@ center-align@^0.1.1: cerialize@0.1.18: version "0.1.18" resolved "https://registry.yarnpkg.com/cerialize/-/cerialize-0.1.18.tgz#d0f4f1b61cec7e4ed16a3eda0cac2bc99787414d" + integrity sha512-C/hp4UoPrMK060251Pt/21axF9aL4ceJlg3+pThB68VghhRjOzBzy4f8R9AirXdNB4gpqgaV2deU3UaexInL5w== dependencies: typescript "^2.5.0" chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" @@ -1488,6 +1732,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: chalk@^2.0.1, chalk@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" + integrity sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ== dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -1496,6 +1741,7 @@ chalk@^2.0.1, chalk@^2.1.0: chalk@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" + integrity sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q== dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -1504,6 +1750,7 @@ chalk@^2.3.0: chalk@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796" + integrity sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g== dependencies: ansi-styles "^3.2.0" escape-string-regexp "^1.0.5" @@ -1512,6 +1759,7 @@ chalk@^2.3.1: chalk@~2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.2.2.tgz#4403f5cf18f35c05f51fbdf152bf588f956cf7cb" + integrity sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw== dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -1520,14 +1768,17 @@ chalk@~2.2.0: charenc@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= check-types@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d" + integrity sha1-Ro9XGkQ1wkJI9f0MsOjYfDw0Hn0= chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -1543,6 +1794,7 @@ chokidar@^1.4.1, chokidar@^1.4.2, chokidar@^1.7.0: chokidar@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.0.tgz#6686313c541d3274b2a5c01233342037948c911b" + integrity sha512-OgXCNv2U6TnG04D3tth0gsvdbV4zdbxFG3sYUqcoQMoEFVd1j1pZR6TZ8iknC45o9IJ6PeQI/J6wT/+cHcniAw== dependencies: anymatch "^2.0.0" async-each "^1.0.0" @@ -1560,6 +1812,7 @@ chokidar@^2.0.0: chokidar@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" + integrity sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw== dependencies: anymatch "^2.0.0" async-each "^1.0.0" @@ -1578,10 +1831,12 @@ chokidar@^2.0.2: chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + integrity sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE= cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -1589,16 +1844,19 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: circular-json@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f" + integrity sha512-UjgcRlTAhAkLeXmDe2wK7ktwy/tgAqxiSndTIPiFZuIPLZmzHzWMwUIe9h9m/OokypG7snxCDEuwJshGBdPvaw== clap@^1.0.9: version "1.2.3" resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" + integrity sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA== dependencies: chalk "^1.1.3" class-utils@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.5.tgz#17e793103750f9627b2176ea34cfd1b565903c80" + integrity sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA= dependencies: arr-union "^3.1.0" define-property "^0.2.5" @@ -1609,16 +1867,19 @@ class-utils@^0.3.5: clean-css@4.1.x: version "4.1.9" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.9.tgz#35cee8ae7687a49b98034f70de00c4edd3826301" + integrity sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE= dependencies: source-map "0.5.x" cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= dependencies: center-align "^0.1.1" right-align "^0.1.1" @@ -1627,6 +1888,7 @@ cliui@^2.1.0: cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -1635,6 +1897,7 @@ cliui@^3.2.0: cliui@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc" + integrity sha512-nY3W5Gu2racvdDk//ELReY+dHjb9PlIcVDFXP72nVIhq2Gy3LuVXYwJoPVudwQnv1shtohpgkdCKT2YaKY0CKw== dependencies: string-width "^2.1.1" strip-ansi "^4.0.0" @@ -1643,6 +1906,7 @@ cliui@^4.0.0: clone-deep@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.3.0.tgz#348c61ae9cdbe0edfe053d91ff4cc521d790ede8" + integrity sha1-NIxhrpzb4O3+BT2R/0zFIdeQ7eg= dependencies: for-own "^1.0.0" is-plain-object "^2.0.1" @@ -1652,32 +1916,39 @@ clone-deep@^0.3.0: clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" + integrity sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= clone@^1.0.0, clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + integrity sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk= co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= co@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/co/-/co-3.0.6.tgz#1445f226c5eb956138e68c9ac30167ea7d2e6bda" + integrity sha1-FEXyJsXrlWE45oyawwFn6n0ua9o= coa@~1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + integrity sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0= dependencies: q "^1.1.2" code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= codelyzer@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-4.1.0.tgz#3117754538d8f5ffa36dff91d340573a836cf373" + integrity sha512-a3FCIAS3FNQIACvj7KA4iKvH3c6r7X6t6zXsrtV797QGYPQyCwD1fIEd9yV+ZDamijF3YaZ5fbB7QbUMOJGC/g== dependencies: app-root-path "^2.0.1" css-selector-tokenizer "^0.7.0" @@ -1689,6 +1960,7 @@ codelyzer@^4.1.0: collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= dependencies: map-visit "^1.0.0" object-visit "^1.0.0" @@ -1696,22 +1968,26 @@ collection-visit@^1.0.0: color-convert@^1.3.0, color-convert@^1.8.2, color-convert@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" + integrity sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o= dependencies: color-name "^1.1.1" color-name@^1.0.0, color-name@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= color-string@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + integrity sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE= dependencies: color-name "^1.0.0" color-string@^1.4.0: version "1.5.2" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.2.tgz#26e45814bc3c9a7cbd6751648a41434514a773a9" + integrity sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k= dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -1719,6 +1995,7 @@ color-string@^1.4.0: color@^0.11.0: version "0.11.4" resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + integrity sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q= dependencies: clone "^1.0.2" color-convert "^1.3.0" @@ -1727,6 +2004,7 @@ color@^0.11.0: color@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/color/-/color-1.0.3.tgz#e48e832d85f14ef694fb468811c2d5cfe729b55d" + integrity sha1-5I6DLYXxTvaU+0aIEcLVz+cptV0= dependencies: color-convert "^1.8.2" color-string "^1.4.0" @@ -1734,6 +2012,7 @@ color@^1.0.3: colormin@^1.0.5: version "1.1.2" resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + integrity sha1-6i90IKcrlogaOKrlnsEkpvcpgTM= dependencies: color "^0.11.0" css-color-names "0.0.4" @@ -1742,20 +2021,24 @@ colormin@^1.0.5: colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= colors@1.1.2, colors@^1.1.0, colors@^1.1.2, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM= combine-lists@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6" + integrity sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y= dependencies: lodash "^4.5.0" combine-source-map@~0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.7.2.tgz#0870312856b307a87cc4ac486f3a9a62aeccc09e" + integrity sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4= dependencies: convert-source-map "~1.1.0" inline-source-map "~0.6.0" @@ -1765,6 +2048,7 @@ combine-source-map@~0.7.1: combine-source-map@~0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b" + integrity sha1-pY0N8ELBhvz4IqjoAV9UUNLXmos= dependencies: convert-source-map "~1.1.0" inline-source-map "~0.6.0" @@ -1774,40 +2058,49 @@ combine-source-map@~0.8.0: combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + integrity sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk= dependencies: delayed-stream "~1.0.0" commander@2.11.x, commander@^2.9.0, commander@~2.11.0: version "2.11.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + integrity sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ== commander@^2.12.1, commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA== commander@^2.13.0: version "2.14.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + integrity sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw== commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= component-bind@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= component-emitter@1.2.1, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= compress-commons@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.0.tgz#58587092ef20d37cb58baf000112c9278ff73b9f" + integrity sha1-WFhwku8g03y1i68AARLJJ4/3O58= dependencies: buffer-crc32 "^0.2.1" crc32-stream "^2.0.0" @@ -1817,12 +2110,14 @@ compress-commons@^1.2.0: compressible@~2.0.11: version "2.0.11" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a" + integrity sha1-FnGKdd4oPtjmBAQWJaIGRYZ5fYo= dependencies: mime-db ">= 1.29.0 < 2" compression-webpack-plugin@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.1.6.tgz#450808fe143b4c5216a14f0c315c47bec3d83cec" + integrity sha512-oNao3kC1JiQC781akjCgm7zu7K1pxDw9sd2oUUbW128cp2OpvOD4xm1s/WDuAdRsOl4m6rsTGAzcdILvA/7nFg== dependencies: async "^2.4.1" cacache "^10.0.1" @@ -1833,6 +2128,7 @@ compression-webpack-plugin@^1.1.6: compression@1.7.1, compression@^1.5.2: version "1.7.1" resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.1.tgz#eff2603efc2e22cf86f35d2eb93589f9875373db" + integrity sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s= dependencies: accepts "~1.3.4" bytes "3.0.0" @@ -1845,10 +2141,12 @@ compression@1.7.1, compression@^1.5.2: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= concat-stream@1.6.0, concat-stream@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= dependencies: inherits "^2.0.3" readable-stream "^2.2.2" @@ -1857,6 +2155,7 @@ concat-stream@1.6.0, concat-stream@^1.5.0: concat-stream@~1.5.0, concat-stream@~1.5.1: version "1.5.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" + integrity sha1-cIl4Yk2FavQaWnQd790mHadSwmY= dependencies: inherits "~2.0.1" readable-stream "~2.0.0" @@ -1865,6 +2164,7 @@ concat-stream@~1.5.0, concat-stream@~1.5.1: configstore@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" + integrity sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw== dependencies: dot-prop "^4.1.0" graceful-fs "^4.1.2" @@ -1876,10 +2176,12 @@ configstore@^3.0.0: connect-history-api-fallback@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169" + integrity sha1-5R0X+PDvDbkKZP20feMFFVbp8Wk= connect@^3.6.0: version "3.6.5" resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.5.tgz#fb8dde7ba0763877d0ec9df9dac0b4b40e72c7da" + integrity sha1-+43ee6B2OHfQ7J352sC0tA5yx9o= dependencies: debug "2.6.9" finalhandler "1.0.6" @@ -1889,40 +2191,49 @@ connect@^3.6.0: console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= dependencies: date-now "^0.1.4" console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= constants-browserify@^1.0.0, constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= content-disposition@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== convert-source-map@^0.3.3: version "0.3.5" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" + integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= convert-source-map@^1.1.1, convert-source-map@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" + integrity sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU= convert-source-map@~1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.1.3.tgz#4829c877e9fe49b3161f3bf3673888e204699860" + integrity sha1-SCnId+n+SbMWHzvzZziI4gRpmGA= cookie-parser@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5" + integrity sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU= dependencies: cookie "0.3.1" cookie-signature "1.0.6" @@ -1930,14 +2241,17 @@ cookie-parser@1.4.3: cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== dependencies: aproba "^1.1.1" fs-write-stream-atomic "^1.0.8" @@ -1949,10 +2263,12 @@ copy-concurrently@^1.0.0: copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= copy-webpack-plugin@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.4.1.tgz#1e8c366211db6dc2ddee40e5a3e4fc661dd149e8" + integrity sha512-ojaz8MpS3zoLJT/JbYMusYM+dCEArhW24hGAUPYPydTCS+87NFh2TWr85sywG3So4Q4E68QoerqQ+Ns1g0fhDg== dependencies: cacache "^10.0.1" find-cache-dir "^1.0.0" @@ -1966,26 +2282,32 @@ copy-webpack-plugin@^4.4.1: core-js@^2.2.0, core-js@^2.4.0: version "2.5.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" + integrity sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs= core-js@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw== core-js@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU= core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= corser@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: version "2.2.2" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892" + integrity sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A== dependencies: is-directory "^0.3.1" js-yaml "^3.4.3" @@ -1998,6 +2320,7 @@ cosmiconfig@^2.1.0, cosmiconfig@^2.1.1: coveralls@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.0.0.tgz#22ef730330538080d29b8c151dc9146afde88a99" + integrity sha512-ZppXR9y5PraUOrf/DzHJY6gzNUhXYE3b9D43xEXs4QYZ7/Oe0Gy0CS+IPKWFfvQFXB3RG9QduaQUFehzSpGAFw== dependencies: js-yaml "^3.6.1" lcov-parse "^0.0.10" @@ -2008,6 +2331,7 @@ coveralls@3.0.0: crc32-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4" + integrity sha1-483TtN8xaN10494/u8t7KX/pCPQ= dependencies: crc "^3.4.4" readable-stream "^2.0.0" @@ -2015,10 +2339,12 @@ crc32-stream@^2.0.0: crc@3.4.4, crc@^3.4.4: version "3.4.4" resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" + integrity sha1-naHpgOO9RPxck79as9ozeNheRms= create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + integrity sha1-iIxyNZbN92EvZJgjPuvXo1MBc30= dependencies: bn.js "^4.1.0" elliptic "^6.0.0" @@ -2026,12 +2352,14 @@ create-ecdh@^4.0.0: create-error-class@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + integrity sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y= dependencies: capture-stack-trace "^1.0.0" create-hash@^1.1.0, create-hash@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + integrity sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0= dependencies: cipher-base "^1.0.1" inherits "^2.0.1" @@ -2041,6 +2369,7 @@ create-hash@^1.1.0, create-hash@^1.1.2: create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: version "1.1.6" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + integrity sha1-rLniIaThe9sHbpBlfEK5PjcmzwY= dependencies: cipher-base "^1.0.3" create-hash "^1.1.0" @@ -2052,6 +2381,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: cross-spawn@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= dependencies: lru-cache "^4.0.1" which "^1.2.9" @@ -2059,6 +2389,7 @@ cross-spawn@^3.0.0: cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= dependencies: lru-cache "^4.0.1" shebang-command "^1.2.0" @@ -2067,22 +2398,26 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0: crypt@~0.0.1: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + integrity sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g= dependencies: boom "2.x.x" cryptiles@3.x.x: version "3.1.2" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + integrity sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4= dependencies: boom "5.x.x" crypto-browserify@^3.0.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" @@ -2099,6 +2434,7 @@ crypto-browserify@^3.0.0: crypto-browserify@^3.11.0: version "3.11.1" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.1.tgz#948945efc6757a400d6e5e5af47194d10064279f" + integrity sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ== dependencies: browserify-cipher "^1.0.0" browserify-sign "^4.0.0" @@ -2114,10 +2450,12 @@ crypto-browserify@^3.11.0: crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= css-color-function@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/css-color-function/-/css-color-function-1.3.0.tgz#72c767baf978f01b8a8a94f42f17ba5d22a776fc" + integrity sha1-csdnuvl48BuKipT0Lxe6XSKndvw= dependencies: balanced-match "0.1.0" color "^0.11.0" @@ -2127,10 +2465,12 @@ css-color-function@^1.3.0: css-color-names@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= css-loader@0.28.9: version "0.28.9" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.9.tgz#68064b85f4e271d7ce4c48a58300928e535d1c95" + integrity sha512-r3dgelMm/mkPz5Y7m9SeiGE46i2VsEU/OYbez+1llfxtv8b2y5/b5StaeEvPK3S5tlNQI+tDW/xDIhKJoZgDtw== dependencies: babel-code-frame "^6.26.0" css-selector-tokenizer "^0.7.0" @@ -2150,6 +2490,7 @@ css-loader@0.28.9: css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= dependencies: boolbase "~1.0.0" css-what "2.1" @@ -2159,6 +2500,7 @@ css-select@^1.1.0: css-selector-tokenizer@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + integrity sha1-5piEdK6MlTR3v15+/s/OzNnPTIY= dependencies: cssesc "^0.1.0" fastparse "^1.1.1" @@ -2167,14 +2509,17 @@ css-selector-tokenizer@^0.7.0: css-unit-converter@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= css-what@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + integrity sha1-lGfQMsOM+u+58teVASUwYvh/ob0= css@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/css/-/css-2.2.1.tgz#73a4c81de85db664d4ee674f7d47085e3b2d55dc" + integrity sha1-c6TIHehdtmTU7mdPfUcIXjstVdw= dependencies: inherits "^2.0.1" source-map "^0.1.38" @@ -2184,16 +2529,19 @@ css@^2.0.0: cssauron@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" + integrity sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg= dependencies: through X.X.X cssesc@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + integrity sha1-yBSQPkViM3GgR3tAEJqq++6t27Q= cssnano@^3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + integrity sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg= dependencies: autoprefixer "^6.3.1" decamelize "^1.1.2" @@ -2231,6 +2579,7 @@ cssnano@^3.10.0: csso@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + integrity sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U= dependencies: clap "^1.0.9" source-map "^0.5.3" @@ -2238,44 +2587,53 @@ csso@~2.3.1: currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= dependencies: array-find-index "^1.0.1" custom-event@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU= cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + integrity sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8= dependencies: es5-ext "^0.10.9" dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= dependencies: assert-plus "^1.0.0" data-uri-to-buffer@1: version "1.2.0" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835" + integrity sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ== date-format@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8" + integrity sha1-YV6CjiM90aubua4JUODOzPpuytg= date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= dateformat@^1.0.11, dateformat@^1.0.6: version "1.0.12" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.12.tgz#9f124b67594c937ff706932e4a642cca8dbbfee9" + integrity sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk= dependencies: get-stdin "^4.0.1" meow "^3.3.0" @@ -2283,52 +2641,63 @@ dateformat@^1.0.11, dateformat@^1.0.6: debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@~2.6.4, debug@~2.6.6, debug@~2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + integrity sha1-+HBX6ZWxofauaklgZkE3vFbwOdo= dependencies: ms "0.7.1" debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" debug@~0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + integrity sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk= decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= deep-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + integrity sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8= deep-freeze-strict@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-freeze-strict/-/deep-freeze-strict-1.1.1.tgz#77d0583ca24a69be4bbd9ac2fae415d55523e5b0" + integrity sha1-d9BYPKJKab5LvZrC+uQV1VUj5bA= deep-freeze@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/deep-freeze/-/deep-freeze-0.0.1.tgz#3a0b0005de18672819dfd38cd31f91179c893e84" + integrity sha1-OgsABd4YZygZ39OM0x+RF5yJPoQ= deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= define-properties@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + integrity sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ= dependencies: foreach "^2.0.5" object-keys "^1.0.8" @@ -2336,22 +2705,26 @@ define-properties@^1.1.2: define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= dependencies: is-descriptor "^0.1.0" define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= dependencies: is-descriptor "^1.0.0" defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= degenerator@~1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-1.0.4.tgz#fcf490a37ece266464d9cc431ab98c5819ced095" + integrity sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU= dependencies: ast-types "0.x.x" escodegen "1.x.x" @@ -2360,6 +2733,7 @@ degenerator@~1.0.2: del@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag= dependencies: globby "^5.0.0" is-path-cwd "^1.0.0" @@ -2372,6 +2746,7 @@ del@^2.2.0: del@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" + integrity sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU= dependencies: globby "^6.1.0" is-path-cwd "^1.0.0" @@ -2383,22 +2758,27 @@ del@^3.0.0: delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= depd@1.1.1, depd@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" + integrity sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k= dependency-graph@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.7.0.tgz#8fc7991ad236e47f0d5742701b5e307b83d7c0d0" + integrity sha512-QzVBbA603vbxF1SMvYmGbE9ZXl+ggb+2SbHvIeOw0w753lgbXC2bZOnCmvG9qr+zlrKK/E0rqtCXOfrRGKH4/Q== deps-sort@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" + integrity sha1-CRckkC6EZYJg65EHSMzNGvbiH7U= dependencies: JSONStream "^1.0.3" shasum "^1.0.0" @@ -2408,6 +2788,7 @@ deps-sort@^2.0.0: des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -2415,20 +2796,24 @@ des.js@^1.0.0: destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= dependencies: repeating "^2.0.0" detect-node@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" + integrity sha1-ogM8CcyOFY03dI+951B4Mr1s4Sc= detective@^4.0.0: version "4.7.1" resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" + integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== dependencies: acorn "^5.2.1" defined "^1.0.0" @@ -2436,14 +2821,17 @@ detective@^4.0.0: di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= diff@^3.1.0, diff@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.1.tgz#aa8567a6eed03c531fc89d3f711cd0e5259dec75" + integrity sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww== diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + integrity sha1-tYNXOScM/ias9jIJn97SoH8gnl4= dependencies: bn.js "^4.1.0" miller-rabin "^4.0.0" @@ -2452,6 +2840,7 @@ diffie-hellman@^5.0.0: dir-glob@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + integrity sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag== dependencies: arrify "^1.0.1" path-type "^3.0.0" @@ -2459,10 +2848,12 @@ dir-glob@^2.0.0: dns-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= dns-packet@^1.0.1: version "1.2.2" resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.2.2.tgz#a8a26bec7646438963fc86e06f8f8b16d6c8bf7a" + integrity sha512-kN+DjfGF7dJGUL7nWRktL9Z18t1rWP3aQlyZdY8XlpvU3Nc6GeFTQApftcjtWKxAZfiggZSGrCEoszNgvnpwDg== dependencies: ip "^1.1.0" safe-buffer "^5.0.1" @@ -2470,18 +2861,21 @@ dns-packet@^1.0.1: dns-txt@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= dependencies: buffer-indexof "^1.0.0" dom-converter@~0.1: version "0.1.4" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" + integrity sha1-pF71cnuJDJv/5tfIduexnLDhfzs= dependencies: utila "~0.3" dom-serialize@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs= dependencies: custom-event "~1.0.0" ent "~2.2.0" @@ -2491,6 +2885,7 @@ dom-serialize@^2.2.0: dom-serializer@0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= dependencies: domelementtype "~1.1.1" entities "~1.1.1" @@ -2498,34 +2893,41 @@ dom-serializer@0: domain-browser@^1.1.1, domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + integrity sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw= domelementtype@1: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + integrity sha1-sXrtguirWeUt2cGbF1bg/BhyBMI= domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= domhandler@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" + integrity sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ= dependencies: domelementtype "1" domino@^1.0.29: version "1.0.30" resolved "https://registry.yarnpkg.com/domino/-/domino-1.0.30.tgz#54a4154ecae968616680f8feba3cedff355c71f4" + integrity sha512-ikq8WiDSkICdkElud317F2Sigc6A3EDpWsxWBwIZqOl95km4p/Vc9Rj98id7qKgsjDmExj0AVM7JOd4bb647Xg== domutils@1.1: version "1.1.6" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.1.6.tgz#bddc3de099b9a2efacc51c623f28f416ecc57485" + integrity sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU= dependencies: domelementtype "1" domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= dependencies: dom-serializer "0" domelementtype "1" @@ -2533,36 +2935,43 @@ domutils@1.5.1: dot-prop@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ== dependencies: is-obj "^1.0.0" double-ended-queue@^2.1.0-0: version "2.1.0-0" resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c" + integrity sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw= duplexer2@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + integrity sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds= dependencies: readable-stream "~1.1.9" duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME= dependencies: readable-stream "^2.0.2" duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= duplexer@^0.1.1, duplexer@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= duplexify@^3.4.2, duplexify@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e" + integrity sha512-g8ID9OroF9hKt2POf8YLayy+9594PzmM3scI00/uBXocX3TWNgoB67hjzkFe9ITAbQOne/lLdBxHXvYUM4ZgGA== dependencies: end-of-stream "^1.0.0" inherits "^2.0.1" @@ -2572,12 +2981,14 @@ duplexify@^3.4.2, duplexify@^3.5.3: ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + integrity sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU= dependencies: jsbn "~0.1.0" ecstatic@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/ecstatic/-/ecstatic-3.1.1.tgz#2564aa9dde84179dcaf926a9e6d12df13a0b666d" + integrity sha512-D9UcjcxDMMqjaQxC0mSsFh/IjJSdiZVPnHrhjHuKXlhLByk5QGGPX1GUIDIjRzhTq4UDCPYwWblw79VBEh3r1w== dependencies: he "^1.1.1" mime "^1.4.1" @@ -2587,22 +2998,27 @@ ecstatic@^3.0.0: ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= ejs@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" + integrity sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo= electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.24: version "1.3.24" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.24.tgz#9b7b88bb05ceb9fa016a177833cc2dde388f21b6" + integrity sha1-m3uIuwXOufoBahd4M8wt3jiPIbY= electron-to-chromium@^1.3.33: version "1.3.33" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.33.tgz#bf00703d62a7c65238136578c352d6c5c042a545" + integrity sha1-vwBwPWKnxlI4E2V4w1LWxcBCpUU= elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + integrity sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8= dependencies: bn.js "^4.4.0" brorand "^1.0.1" @@ -2615,26 +3031,31 @@ elliptic@^6.0.0: emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= encodeurl@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + integrity sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA= end-of-stream@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.0.tgz#7a90d833efda6cfa6eac0f4949dbb0fad3a63206" + integrity sha1-epDYM+/abPpurA9JSduw+tOmMgY= dependencies: once "^1.4.0" end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== dependencies: once "^1.4.0" engine.io-client@~3.1.0: version "3.1.4" resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.4.tgz#4fcf1370b47163bd2ce9be2733972430350d4ea1" + integrity sha1-T88TcLRxY70s6b4nM5ckMDUNTqE= dependencies: component-emitter "1.2.1" component-inherit "0.0.3" @@ -2651,6 +3072,7 @@ engine.io-client@~3.1.0: engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.1.2.tgz#4c0f4cff79aaeecbbdcfdea66a823c6085409196" + integrity sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw== dependencies: after "0.8.2" arraybuffer.slice "~0.0.7" @@ -2661,6 +3083,7 @@ engine.io-parser@~2.1.0, engine.io-parser@~2.1.1: engine.io@~3.1.0: version "3.1.4" resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.4.tgz#3d0211b70a552ce841ffc7da8627b301a9a4162e" + integrity sha1-PQIRtwpVLOhB/8fahiezAamkFi4= dependencies: accepts "1.3.3" base64id "1.0.0" @@ -2674,6 +3097,7 @@ engine.io@~3.1.0: enhanced-resolve@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.3.0.tgz#950964ecc7f0332a42321b673b38dc8ff15535b3" + integrity sha512-2qbxE7ek3YxPJ1ML6V+satHkzHpJQKWkRHmRx6mfAoW59yP8YH8BFplbegSP+u2hBd6B6KCOpvJQ3dZAP+hkpg== dependencies: graceful-fs "^4.1.2" memory-fs "^0.4.0" @@ -2683,6 +3107,7 @@ enhanced-resolve@3.3.0: enhanced-resolve@^3.1.0, enhanced-resolve@^3.4.0: version "3.4.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" + integrity sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24= dependencies: graceful-fs "^4.1.2" memory-fs "^0.4.0" @@ -2692,26 +3117,31 @@ enhanced-resolve@^3.1.0, enhanced-resolve@^3.4.0: ent@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= errno@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + integrity sha1-uJbiOp5ei6M4cfyZar02NfyaHH0= dependencies: prr "~0.0.0" error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + integrity sha1-+FWobOYa3E6GIcPNoh56dhLDqNw= dependencies: is-arrayish "^0.2.1" es-abstract@^1.4.3, es-abstract@^1.7.0: version "1.8.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.2.tgz#25103263dc4decbda60e0c737ca32313518027ee" + integrity sha512-dvhwFL3yjQxNNsOWx6exMlaDrRHCRGMQlnx5lsXDCZ/J7G/frgIIl94zhZSp/galVAYp7VzPi1OrAHta89/yGQ== dependencies: es-to-primitive "^1.1.1" function-bind "^1.1.1" @@ -2722,6 +3152,7 @@ es-abstract@^1.4.3, es-abstract@^1.7.0: es-to-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + integrity sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0= dependencies: is-callable "^1.1.1" is-date-object "^1.0.1" @@ -2730,6 +3161,7 @@ es-to-primitive@^1.1.1: es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.30" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.30.tgz#7141a16836697dbabfaaaeee41495ce29f52c939" + integrity sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk= dependencies: es6-iterator "2" es6-symbol "~3.1" @@ -2737,6 +3169,7 @@ es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512" + integrity sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI= dependencies: d "1" es5-ext "^0.10.14" @@ -2745,6 +3178,7 @@ es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1: es6-map@^0.1.3: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" + integrity sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA= dependencies: d "1" es5-ext "~0.10.14" @@ -2756,14 +3190,17 @@ es6-map@^0.1.3: es6-promise@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y= es6-promise@~4.0.3: version "4.0.5" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.0.5.tgz#7882f30adde5b240ccfa7f7d78c548330951ae42" + integrity sha1-eILzCt3lskDM+n99eMVIMwlRrkI= es6-set@~0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" + integrity sha1-0rPsXU2ADO2BjbU40ol02wpzzLE= dependencies: d "1" es5-ext "~0.10.14" @@ -2774,6 +3211,7 @@ es6-set@~0.1.5: es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + integrity sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc= dependencies: d "1" es5-ext "~0.10.14" @@ -2781,6 +3219,7 @@ es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbo es6-weak-map@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" + integrity sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8= dependencies: d "1" es5-ext "^0.10.14" @@ -2790,14 +3229,17 @@ es6-weak-map@^2.0.1: escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + integrity sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg= dependencies: esprima "^2.7.1" estraverse "^1.9.1" @@ -2809,6 +3251,7 @@ escodegen@1.8.x: escodegen@1.x.x: version "1.9.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.0.tgz#9811a2f265dc1cd3894420ee3717064b632b8852" + integrity sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw== dependencies: esprima "^3.1.3" estraverse "^4.2.0" @@ -2820,6 +3263,7 @@ escodegen@1.x.x: escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + integrity sha1-4Bl16BJ4GhY6ba392AOY3GTIicM= dependencies: es6-map "^0.1.3" es6-weak-map "^2.0.1" @@ -2829,18 +3273,22 @@ escope@^3.6.0: esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= esprima@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + integrity sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw== esrecurse@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.0.tgz#fa9568d98d3823f9a41d91e902dcab9ea6e5b163" + integrity sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM= dependencies: estraverse "^4.1.0" object-assign "^4.0.1" @@ -2848,34 +3296,42 @@ esrecurse@^4.1.0: estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q= estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= estree-walker@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.2.1.tgz#bdafe8095383d8414d5dc2ecf4c9173b6db9412e" + integrity sha1-va/oCVOD2EFNXcLs9MkXO225QS4= estree-walker@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.3.1.tgz#e6b1a51cf7292524e7237c312e5fe6660c1ce1aa" + integrity sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao= estree-walker@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.0.tgz#aae3b57c42deb8010e349c892462f0e71c5dd1aa" + integrity sha512-/bEAy+yKAZQrEWUhGmS3H9XpGqSDBtRzX0I2PgMw9kA2n1jN22uV5B5p7MFdZdvWdXCRJztXAfx6ZeRfgkEETg== esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + integrity sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs= etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= event-emitter@~0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk= dependencies: d "1" es5-ext "~0.10.14" @@ -2883,6 +3339,7 @@ event-emitter@~0.3.5: event-stream@~3.3.0: version "3.3.4" resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= dependencies: duplexer "~0.1.1" from "~0" @@ -2895,20 +3352,24 @@ event-stream@~3.3.0: eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg= events@^1.0.0, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= eventsource@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + integrity sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI= dependencies: original ">=0.0.5" evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== dependencies: md5.js "^1.3.4" safe-buffer "^5.1.1" @@ -2916,6 +3377,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + integrity sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c= dependencies: cross-spawn "^5.0.1" get-stream "^3.0.0" @@ -2928,10 +3390,12 @@ execa@^0.7.0: exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= expand-braces@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" + integrity sha1-SIsdHSRRyz06axks/AMPRMWFX+o= dependencies: array-slice "^0.2.3" array-unique "^0.2.1" @@ -2940,12 +3404,14 @@ expand-braces@^0.1.1: expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= dependencies: is-posix-bracket "^0.1.0" expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= dependencies: debug "^2.3.3" define-property "^0.2.5" @@ -2958,6 +3424,7 @@ expand-brackets@^2.1.4: expand-range@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" + integrity sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ= dependencies: is-number "^0.1.1" repeat-string "^0.2.2" @@ -2965,12 +3432,14 @@ expand-range@^0.1.0: expand-range@^1.8.1: version "1.8.2" resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= dependencies: fill-range "^2.1.0" exports-loader@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5" + integrity sha512-RKwCrO4A6IiKm0pG3c9V46JxIHcDplwwGJn6+JJ1RcVnh/WSGJa0xkmk5cRVtgOPzCAtTMGj2F7nluh9L0vpSA== dependencies: loader-utils "^1.1.0" source-map "0.5.0" @@ -2978,6 +3447,7 @@ exports-loader@^0.7.0: express-session@1.15.6: version "1.15.6" resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" + integrity sha512-r0nrHTCYtAMrFwZ0kBzZEXa1vtPVrw0dKvGSrKP4dahwBQ1BJpF2/y1Pp4sCD/0kvxV4zZeclyvfmw0B4RMJQA== dependencies: cookie "0.3.1" cookie-signature "1.0.6" @@ -2992,6 +3462,7 @@ express-session@1.15.6: express@4.16.2, express@^4.16.2: version "4.16.2" resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" + integrity sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w= dependencies: accepts "~1.3.4" array-flatten "1.1.1" @@ -3027,18 +3498,21 @@ express@4.16.2, express@^4.16.2: extend-shallow@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" + integrity sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE= dependencies: kind-of "^1.1.0" extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= dependencies: is-extendable "^0.1.0" extend-shallow@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= dependencies: assign-symbols "^1.0.0" is-extendable "^1.0.1" @@ -3046,16 +3520,19 @@ extend-shallow@^3.0.0: extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + integrity sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ= extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= dependencies: is-extglob "^1.0.0" extglob@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.2.tgz#3290f46208db1b2e8eb8be0c94ed9e6ad80edbe2" + integrity sha512-I0+eZBH+jFGL8F5BnIz2ON2nKCjTS3AS3H/5PeSmCp7UVC70Ym8IhdRiQly2juKYQ//f7z1aj1BRpQniFJoU1w== dependencies: array-unique "^0.3.2" define-property "^1.0.0" @@ -3069,6 +3546,7 @@ extglob@^2.0.2: extract-zip@~1.6.5: version "1.6.5" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.5.tgz#99a06735b6ea20ea9b705d779acffcc87cff0440" + integrity sha1-maBnNbbqIOqbcF13ms/8yHz/BEA= dependencies: concat-stream "1.6.0" debug "2.2.0" @@ -3078,10 +3556,12 @@ extract-zip@~1.6.5: extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= fancy-log@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.0.tgz#45be17d02bb9917d60ccffd4995c999e6c8c9948" + integrity sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg= dependencies: chalk "^1.1.1" time-stamp "^1.0.0" @@ -3089,52 +3569,63 @@ fancy-log@^1.1.0: fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + integrity sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8= fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + integrity sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg= faye-websocket@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= dependencies: websocket-driver ">=0.5.1" faye-websocket@~0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" + integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg= dependencies: websocket-driver ">=0.5.1" fd-slicer@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= dependencies: pend "~1.2.0" file-uri-to-path@1: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= filesize@^3.5.11: version "3.6.0" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa" + integrity sha512-g5OWtoZWcPI56js1DFhIEqyG9tnu/7sG3foHwgS9KGYFMfsYguI3E+PRVCmtmE96VajQIEMRU2OhN+ME589Gdw== fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + integrity sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM= dependencies: is-number "^2.1.0" isobject "^2.0.0" @@ -3145,6 +3636,7 @@ fill-range@^2.1.0: fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= dependencies: extend-shallow "^2.0.1" is-number "^3.0.0" @@ -3154,6 +3646,7 @@ fill-range@^4.0.0: finalhandler@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.6.tgz#007aea33d1a4d3e42017f624848ad58d212f814f" + integrity sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8= dependencies: debug "2.6.9" encodeurl "~1.0.1" @@ -3166,6 +3659,7 @@ finalhandler@1.0.6: finalhandler@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" + integrity sha1-zgtoVbRYU+eRsvzGgARtiCU91/U= dependencies: debug "2.6.9" encodeurl "~1.0.1" @@ -3178,6 +3672,7 @@ finalhandler@1.1.0: find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= dependencies: commondir "^1.0.1" make-dir "^1.0.0" @@ -3186,6 +3681,7 @@ find-cache-dir@^1.0.0: find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= dependencies: path-exists "^2.0.0" pinkie-promise "^2.0.0" @@ -3193,16 +3689,19 @@ find-up@^1.0.0: find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= dependencies: locate-path "^2.0.0" flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= flush-write-stream@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" + integrity sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc= dependencies: inherits "^2.0.1" readable-stream "^2.0.4" @@ -3210,44 +3709,53 @@ flush-write-stream@^1.0.0: follow-redirects@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37" + integrity sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc= dependencies: debug "^2.2.0" font-awesome@4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" + integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM= for-in@^0.1.3: version "0.1.8" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= dependencies: for-in "^1.0.1" for-own@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" + integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= dependencies: for-in "^1.0.1" foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25" + integrity sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU= dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -3256,6 +3764,7 @@ form-data@~2.0.0: form-data@~2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + integrity sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE= dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -3264,6 +3773,7 @@ form-data@~2.1.1: form-data@~2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + integrity sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8= dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -3272,20 +3782,24 @@ form-data@~2.3.1: forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= dependencies: map-cache "^0.2.2" fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= dependencies: inherits "^2.0.1" readable-stream "^2.0.0" @@ -3293,16 +3807,19 @@ from2@^2.1.0: from@~0: version "0.1.7" resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= fs-access@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" + integrity sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o= dependencies: null-check "^1.0.0" fs-extra@^0.22.1: version "0.22.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.22.1.tgz#5fd6f8049dc976ca19eb2355d658173cabcce056" + integrity sha1-X9b4BJ3JdsoZ6yNV1lgXPKvM4FY= dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" @@ -3311,6 +3828,7 @@ fs-extra@^0.22.1: fs-extra@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -3319,6 +3837,7 @@ fs-extra@^4.0.0: fs-extra@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== dependencies: graceful-fs "^4.1.2" jsonfile "^4.0.0" @@ -3327,6 +3846,7 @@ fs-extra@^5.0.0: fs-extra@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA= dependencies: graceful-fs "^4.1.2" jsonfile "^2.1.0" @@ -3335,6 +3855,7 @@ fs-extra@~1.0.0: fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= dependencies: graceful-fs "^4.1.2" iferr "^0.1.5" @@ -3344,10 +3865,12 @@ fs-write-stream-atomic@^1.0.8: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= fsevents@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" + integrity sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw== dependencies: nan "^2.3.0" node-pre-gyp "^0.6.36" @@ -3355,6 +3878,7 @@ fsevents@^1.0.0: fstream-ignore@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + integrity sha1-nDHa40dnAY/h0kmyTa2mfQktoQU= dependencies: fstream "^1.0.0" inherits "2" @@ -3363,6 +3887,7 @@ fstream-ignore@^1.0.5: fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE= dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" @@ -3372,6 +3897,7 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: ftp@~0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" + integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= dependencies: readable-stream "1.1.x" xregexp "2.0.0" @@ -3379,10 +3905,12 @@ ftp@~0.3.10: function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -3396,38 +3924,46 @@ gauge@~2.7.3: gaze@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + integrity sha1-hHIkZ3rbiHDWeSV+0ziP22HkAQU= dependencies: globule "^1.0.0" generate-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + integrity sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ= generate-object-property@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + integrity sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA= dependencies: is-property "^1.0.0" get-caller-file@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U= get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + integrity sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= get-uri@2: version "2.0.1" resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-2.0.1.tgz#dbdcacacd8c608a38316869368117697a1631c59" + integrity sha512-7aelVrYqCLuVjq2kEKRTH8fXPTC0xKTkM+G7UlFkEwCXY3sFbSxvY375JoFowOAYbkaU47SrBvOefUlLZZ+6QA== dependencies: data-uri-to-buffer "1" debug "2" @@ -3439,16 +3975,19 @@ get-uri@2: get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: assert-plus "^1.0.0" glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= dependencies: glob-parent "^2.0.0" is-glob "^2.0.0" @@ -3456,12 +3995,14 @@ glob-base@^0.3.0: glob-parent@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= dependencies: is-glob "^2.0.0" glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= dependencies: is-glob "^3.1.0" path-dirname "^1.0.0" @@ -3469,6 +4010,7 @@ glob-parent@^3.1.0: glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= dependencies: inflight "^1.0.4" inherits "2" @@ -3479,6 +4021,7 @@ glob@^5.0.15: glob@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + integrity sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI= dependencies: inflight "^1.0.4" inherits "2" @@ -3489,6 +4032,7 @@ glob@^6.0.4: glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3500,16 +4044,19 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.0.6, glob@^7.1.0, glob@^7.1.1, gl global-dirs@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + integrity sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU= dependencies: ini "^1.3.4" globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== globby@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0= dependencies: array-union "^1.0.1" arrify "^1.0.0" @@ -3521,6 +4068,7 @@ globby@^5.0.0: globby@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= dependencies: array-union "^1.0.1" glob "^7.0.3" @@ -3531,6 +4079,7 @@ globby@^6.1.0: globby@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= dependencies: array-union "^1.0.1" dir-glob "^2.0.0" @@ -3542,6 +4091,7 @@ globby@^7.1.1: globule@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" + integrity sha1-HcScaCLdnoovoAuiopUAboZkvQk= dependencies: glob "~7.1.1" lodash "~4.17.4" @@ -3550,18 +4100,21 @@ globule@^1.0.0: glogg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.0.tgz#7fe0f199f57ac906cf512feead8f90ee4a284fc5" + integrity sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U= dependencies: sparkles "^1.0.0" gonzales-pe@^4.0.3: version "4.2.2" resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.2.tgz#f50a8c17842f13a9007909b7cb32188266e4d74c" + integrity sha512-jbQFnd6CD3iEuGtSKVhsh37tQIkkx+/eil3tufyYOHMouG89uqtkWGP03P4vxY+XGeJnCi3ewIY+BnBogyC61Q== dependencies: minimist "1.1.x" got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + integrity sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA= dependencies: create-error-class "^3.0.0" duplexer3 "^0.1.4" @@ -3578,10 +4131,12 @@ got@^6.7.1: graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= gulp-util@3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.7.tgz#78925c4b8f8b49005ac01a011c557e6218941cbb" + integrity sha1-eJJcS4+LSQBawBoBHFV+YhiUHLs= dependencies: array-differ "^1.0.0" array-uniq "^1.0.2" @@ -3605,12 +4160,14 @@ gulp-util@3.0.7: gulplog@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" + integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= dependencies: glogg "^1.0.0" gzip-size@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c" + integrity sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw= dependencies: duplexer "^0.1.1" pify "^3.0.0" @@ -3618,10 +4175,12 @@ gzip-size@^4.1.0: handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" + integrity sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ= handlebars@^4.0.1, handlebars@^4.0.6: version "4.0.10" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f" + integrity sha1-PTDHGLCaPZbyPqTMH0A8TTup/08= dependencies: async "^1.4.0" optimist "^0.6.1" @@ -3632,14 +4191,17 @@ handlebars@^4.0.1, handlebars@^4.0.6: har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + integrity sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4= har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= har-validator@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + integrity sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0= dependencies: chalk "^1.1.1" commander "^2.9.0" @@ -3649,6 +4211,7 @@ har-validator@~2.0.6: har-validator@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + integrity sha1-M0gdDxu/9gDdID11gSpqX7oALio= dependencies: ajv "^4.9.1" har-schema "^1.0.5" @@ -3656,6 +4219,7 @@ har-validator@~4.2.1: har-validator@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + integrity sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0= dependencies: ajv "^5.1.0" har-schema "^2.0.0" @@ -3663,44 +4227,53 @@ har-validator@~5.0.3: has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= dependencies: ansi-regex "^2.0.0" has-binary2@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.2.tgz#e83dba49f0b9be4d026d27365350d9f03f54be98" + integrity sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg= dependencies: isarray "2.0.1" has-cors@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + integrity sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo= has-flag@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= has-gulplog@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" + integrity sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4= dependencies: sparkles "^1.0.0" has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= dependencies: get-value "^2.0.3" has-values "^0.1.4" @@ -3709,6 +4282,7 @@ has-value@^0.3.1: has-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= dependencies: get-value "^2.0.6" has-values "^1.0.0" @@ -3717,10 +4291,12 @@ has-value@^1.0.0: has-values@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= has-values@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -3728,18 +4304,21 @@ has-values@^1.0.0: has@^1.0.0, has@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + integrity sha1-hGFzP1OLCDfJNh45qauelwTcLyg= dependencies: function-bind "^1.0.2" hash-base@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + integrity sha1-ZuodhW206KVHDK32/OI65SRO8uE= dependencies: inherits "^2.0.1" hash-base@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -3747,6 +4326,7 @@ hash-base@^3.0.0: hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.3" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + integrity sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA== dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.0" @@ -3754,6 +4334,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: hasha@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" + integrity sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE= dependencies: is-stream "^1.0.1" pinkie-promise "^2.0.0" @@ -3761,6 +4342,7 @@ hasha@~2.2.0: hawk@3.1.3, hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + integrity sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ= dependencies: boom "2.x.x" cryptiles "2.x.x" @@ -3770,6 +4352,7 @@ hawk@3.1.3, hawk@~3.1.3: hawk@~6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + integrity sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ== dependencies: boom "4.x.x" cryptiles "3.x.x" @@ -3779,14 +4362,17 @@ hawk@~6.0.2: he@1.1.x, he@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= highlight.js@^9.0.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" + integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= hipchat-notifier@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e" + integrity sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4= dependencies: lodash "^4.0.0" request "^2.0.0" @@ -3794,6 +4380,7 @@ hipchat-notifier@^1.1.0: hmac-drbg@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= dependencies: hash.js "^1.0.3" minimalistic-assert "^1.0.0" @@ -3802,24 +4389,29 @@ hmac-drbg@^1.0.0: hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + integrity sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0= hoek@4.x.x: version "4.2.0" resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + integrity sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ== homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + integrity sha1-TCu8inWJmP7r9e1oWA921GdotLw= dependencies: parse-passwd "^1.0.0" hosted-git-info@^2.1.4: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + integrity sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg== hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= dependencies: inherits "^2.0.1" obuf "^1.0.0" @@ -3829,14 +4421,17 @@ hpack.js@^2.1.6: html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + integrity sha1-ZouTd26q5V696POtRkswekljYl4= html-entities@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= html-minifier@^3.2.3: version "3.5.5" resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.5.tgz#3bdc9427e638bbe3dbde96c0eb988b044f02739e" + integrity sha512-g+1+NBycQI0fGnggd52JM8TRUweG7+9W2wrtjGitMAqc4G7maweAHvVAAjz9veHseIH3tYKE2lk2USGSoewIrQ== dependencies: camel-case "3.0.x" clean-css "4.1.x" @@ -3850,6 +4445,7 @@ html-minifier@^3.2.3: html-webpack-plugin@2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-2.30.1.tgz#7f9c421b7ea91ec460f56527d78df484ee7537d5" + integrity sha1-f5xCG36pHsRg9WUn1430hO51N9U= dependencies: bluebird "^3.4.7" html-minifier "^3.2.3" @@ -3861,10 +4457,12 @@ html-webpack-plugin@2.30.1: htmlescape@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" + integrity sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E= htmlparser2@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.3.0.tgz#cc70d05a59f6542e43f0e685c982e14c924a9efe" + integrity sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4= dependencies: domelementtype "1" domhandler "2.1" @@ -3874,10 +4472,12 @@ htmlparser2@~3.3.0: http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= http-errors@1.6.2, http-errors@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" + integrity sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY= dependencies: depd "1.1.1" inherits "2.0.3" @@ -3887,10 +4487,12 @@ http-errors@1.6.2, http-errors@~1.6.2: http-parser-js@>=0.4.0: version "0.4.8" resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.8.tgz#763f75c4b771a0bb44653b07070bff6ca7bc5561" + integrity sha512-jmHp99g6/fLx0pRNJqzsQgjsclCHAY7NhIeA3/U+bsGNvgbvUCQFQY9m5AYpqpAxY/2VcikfbKpjQozSTiz0jA== http-proxy-agent@1: version "1.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a" + integrity sha1-zBzjjkU7+YSg93AtLdWcc9CBKEo= dependencies: agent-base "2" debug "2" @@ -3899,6 +4501,7 @@ http-proxy-agent@1: http-proxy-middleware@~0.17.4: version "0.17.4" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" + integrity sha1-ZC6ISIUdZvCdTxJJEoRtuutBuDM= dependencies: http-proxy "^1.16.2" is-glob "^3.1.0" @@ -3908,6 +4511,7 @@ http-proxy-middleware@~0.17.4: http-proxy@^1.13.0, http-proxy@^1.16.2, http-proxy@^1.8.1: version "1.16.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" + integrity sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I= dependencies: eventemitter3 "1.x.x" requires-port "1.x.x" @@ -3915,6 +4519,7 @@ http-proxy@^1.13.0, http-proxy@^1.16.2, http-proxy@^1.8.1: http-server@0.11.1: version "0.11.1" resolved "https://registry.yarnpkg.com/http-server/-/http-server-0.11.1.tgz#2302a56a6ffef7f9abea0147d838a5e9b6b6a79b" + integrity sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w== dependencies: colors "1.0.3" corser "~2.0.0" @@ -3928,6 +4533,7 @@ http-server@0.11.1: http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + integrity sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8= dependencies: assert-plus "^0.2.0" jsprim "^1.2.2" @@ -3936,6 +4542,7 @@ http-signature@~1.1.0: http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= dependencies: assert-plus "^1.0.0" jsprim "^1.2.2" @@ -3944,6 +4551,7 @@ http-signature@~1.2.0: httpntlm@1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/httpntlm/-/httpntlm-1.6.1.tgz#ad01527143a2e8773cfae6a96f58656bb52a34b2" + integrity sha1-rQFScUOi6Hc8+uapb1hla7UqNLI= dependencies: httpreq ">=0.4.22" underscore "~1.7.0" @@ -3951,18 +4559,22 @@ httpntlm@1.6.1: httpreq@>=0.4.22: version "0.4.24" resolved "https://registry.yarnpkg.com/httpreq/-/httpreq-0.4.24.tgz#4335ffd82cd969668a39465c929ac61d6393627f" + integrity sha1-QzX/2CzZaWaKOUZckprGHWOTYn8= https-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" + integrity sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI= https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= https-proxy-agent@1, https-proxy-agent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6" + integrity sha1-NffabEjOTdv6JkiRrFk+5f+GceY= dependencies: agent-base "2" debug "2" @@ -3971,52 +4583,64 @@ https-proxy-agent@1, https-proxy-agent@^1.0.0: https@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https/-/https-1.0.0.tgz#3c37c7ae1a8eeb966904a2ad1e975a194b7ed3a4" + integrity sha1-PDfHrhqO65ZpBKKtHpdaGUt+06Q= iconv-lite@0.4.15: version "0.4.15" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + integrity sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es= iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== icss-replace-symbols@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= icss-utils@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + integrity sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI= dependencies: postcss "^6.0.1" ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + integrity sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q= iferr@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" + integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk= ignore@^3.3.5: version "3.3.7" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" + integrity sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA== immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= import-local@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ== dependencies: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" @@ -4024,6 +4648,7 @@ import-local@^1.0.0: imports-loader@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.7.1.tgz#f204b5f34702a32c1db7d48d89d5e867a0441253" + integrity sha1-8gS180cCoywdt9SNidXoZ6BEElM= dependencies: loader-utils "^1.0.2" source-map "^0.5.6" @@ -4031,36 +4656,44 @@ imports-loader@0.7.1: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= in-publish@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + integrity sha1-4g/146KvwmkDILbcVSaCqcf631E= indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= dependencies: repeating "^2.0.0" indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= inflection@~1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f" + integrity sha1-W//LEZetPoEFD44X4hZoCH7p6y8= inflection@~1.3.0: version "1.3.8" resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.3.8.tgz#cbd160da9f75b14c3cc63578d4f396784bf3014e" + integrity sha1-y9Fg2p91sUw8xjV41POWeEvzAU4= inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= dependencies: once "^1.3.0" wrappy "1" @@ -4068,24 +4701,29 @@ inflight@^1.0.4: inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + integrity sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4= inline-source-map@~0.6.0: version "0.6.2" resolved "https://registry.yarnpkg.com/inline-source-map/-/inline-source-map-0.6.2.tgz#f9393471c18a79d1724f863fa38b586370ade2a5" + integrity sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU= dependencies: source-map "~0.5.3" insert-module-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/insert-module-globals/-/insert-module-globals-7.0.1.tgz#c03bf4e01cb086d5b5e5ace8ad0afe7889d638c3" + integrity sha1-wDv04BywhtW15azorQr+eInWOMM= dependencies: JSONStream "^1.0.3" combine-source-map "~0.7.1" @@ -4099,90 +4737,109 @@ insert-module-globals@^7.0.0: internal-ip@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" + integrity sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w= dependencies: meow "^3.3.0" interpret@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.4.tgz#820cdd588b868ffb191a809506d6c9c8f212b1b0" + integrity sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA= invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A= dependencies: loose-envify "^1.0.0" invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= ip@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" + integrity sha1-x+NWzeoiWucbNtcPLnGpK6TkJZA= ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" + integrity sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A= is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= dependencies: kind-of "^3.0.2" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= is-arrayish@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.1.tgz#c2dfc386abaa0c3e33c48db3fe87059e69065efd" + integrity sha1-wt/DhquqDD4zxI2z/ocFnmkGXv0= is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= dependencies: binary-extensions "^1.0.0" is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.1: version "1.1.5" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + integrity sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw= is-buffer@^1.1.0: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= dependencies: builtin-modules "^1.0.0" is-callable@^1.1.1, is-callable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + integrity sha1-hut1OSgF3cM69xySoO7fdO52BLI= is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= dependencies: kind-of "^3.0.2" is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -4191,6 +4848,7 @@ is-descriptor@^0.1.0: is-descriptor@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.1.tgz#2c6023599bde2de9d5d2c8b9a9d94082036b6ef2" + integrity sha512-G3fFVFTqfaqu7r4YuSBHKBAuOaLz8Sy7ekklUpFEliaLMP1Y2ZjoN9jS62YWCAPQrQpMUQSitRlrzibbuCZjdA== dependencies: is-accessor-descriptor "^0.1.6" is-data-descriptor "^0.1.4" @@ -4199,72 +4857,86 @@ is-descriptor@^1.0.0: is-directory@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= is-equal-shallow@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= dependencies: is-primitive "^2.0.0" is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= is-extglob@^2.1.0, is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= dependencies: number-is-nan "^1.0.0" is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= is-glob@^2.0.0, is-glob@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= dependencies: is-extglob "^1.0.0" is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= dependencies: is-extglob "^2.1.0" is-glob@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= dependencies: is-extglob "^2.1.1" is-installed-globally@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + integrity sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA= dependencies: global-dirs "^0.1.0" is-path-inside "^1.0.0" @@ -4272,10 +4944,12 @@ is-installed-globally@^0.1.0: is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= is-my-json-valid@^2.12.4: version "2.16.1" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" + integrity sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ== dependencies: generate-function "^2.0.0" generate-object-property "^1.1.0" @@ -4285,152 +4959,185 @@ is-my-json-valid@^2.12.4: is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + integrity sha1-8vtjpl5JBbQGyGBydloaTceTufQ= is-number@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" + integrity sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY= is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= dependencies: kind-of "^3.0.2" is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= dependencies: kind-of "^3.0.2" is-obj@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= is-odd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-1.0.0.tgz#3b8a932eb028b3775c39bb09e91767accdb69088" + integrity sha1-O4qTLrAos3dcObsJ6RdnrM22kIg= dependencies: is-number "^3.0.0" is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= is-path-in-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" + integrity sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw= dependencies: is-path-inside "^1.0.0" is-path-inside@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f" + integrity sha1-/AbloWg/vaE95mev9xe7wQpI838= dependencies: path-is-inside "^1.0.1" is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ= is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= dependencies: has "^1.0.1" is-retry-allowed@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= is-svg@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + integrity sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk= dependencies: html-comment-regex "^1.1.0" is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + integrity sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI= is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= isarray@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= isbinaryfile@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" + integrity sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE= isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= isnumeric@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/isnumeric/-/isnumeric-0.2.0.tgz#a2347ba360de19e33d0ffd590fddf7755cbf2e64" + integrity sha1-ojR7o2DeGeM9D/1ZD933dVy/LmQ= isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= dependencies: isarray "1.0.0" isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= istanbul-instrumenter-loader@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-instrumenter-loader/-/istanbul-instrumenter-loader-3.0.0.tgz#9f553923b22360bac95e617aaba01add1f7db0b2" + integrity sha512-alLSEFX06ApU75sm5oWcaVNaiss/bgMRiWTct3g0P0ZZTKjR+6QiCcuVOKDI1kWJgwHEnIXsv/dWm783kPpmtw== dependencies: convert-source-map "^1.5.0" istanbul-lib-instrument "^1.7.3" @@ -4440,10 +5147,12 @@ istanbul-instrumenter-loader@3.0.0: istanbul-lib-coverage@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" + integrity sha512-0+1vDkmzxqJIn5rcoEqapSB4DmPxE31EtI2dF2aCkV5esN9EWHxZ0dwgDClivMXJqE7zaYQxq30hj5L0nlTN5Q== istanbul-lib-instrument@^1.7.3: version "1.8.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532" + integrity sha1-ZvbJQhzJ7EcE928tsIS6kHiitTI= dependencies: babel-generator "^6.18.0" babel-template "^6.16.0" @@ -4456,6 +5165,7 @@ istanbul-lib-instrument@^1.7.3: istanbul@0.4.5, istanbul@^0.4.0, istanbul@^0.4.3: version "0.4.5" resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + integrity sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs= dependencies: abbrev "1.0.x" async "1.x" @@ -4475,26 +5185,31 @@ istanbul@0.4.5, istanbul@^0.4.0, istanbul@^0.4.3: jasmine-core@^2.99.1: version "2.99.1" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz#e6400df1e6b56e130b61c4bcd093daa7f6e8ca15" + integrity sha1-5kAN8ea1bhMLYcS80JPap/boyhU= jasmine-core@~2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" + integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4= jasmine-marbles@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/jasmine-marbles/-/jasmine-marbles-0.2.0.tgz#b893d8508b75790b634876d3a1bea1345d65c156" + integrity sha1-uJPYUIt1eQtjSHbTob6hNF1lwVY= dependencies: lodash "^4.5.0" jasmine-spec-reporter@4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-4.2.1.tgz#1d632aec0341670ad324f92ba84b4b32b35e9e22" + integrity sha512-FZBoZu7VE5nR7Nilzy+Np8KuVIOxF4oXDPDknehCYBDE080EnlPu0afdZNmpGDBRCUBv3mj5qgqCRmk6W/K8vg== dependencies: colors "1.1.2" jasmine@2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e" + integrity sha1-awicChFXax8W3xG4AUbZHU6Lij4= dependencies: exit "^0.1.2" glob "^7.0.6" @@ -4503,22 +5218,27 @@ jasmine@2.8.0: jasminewd2@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.1.0.tgz#da595275d1ae631de736ac0a7c7d85c9f73ef652" + integrity sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI= js-base64@^2.1.8, js-base64@^2.1.9: version "2.3.2" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.3.2.tgz#a79a923666372b580f8e27f51845c6f7e8fbfbaf" + integrity sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw== js-cookie@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.0.tgz#1b2c279a6eece380a12168b92485265b35b1effb" + integrity sha1-Gywnmm7s44ChIWi5JIUmWzWx7/s= js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.6.1, js-yaml@^3.7.0: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" + integrity sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -4526,6 +5246,7 @@ js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.6.1, js-yaml@^3.7.0: js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + integrity sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A= dependencies: argparse "^1.0.7" esprima "^2.6.0" @@ -4533,90 +5254,110 @@ js-yaml@~3.7.0: js.clone@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/js.clone/-/js.clone-0.0.3.tgz#f378d2bf501fcf648074fd91893f4718236bb79c" + integrity sha1-83jSv1Afz2SAdP2RiT9HGCNrt5w= jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= json-loader@0.5.7, json-loader@^0.5.4: version "0.5.7" resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" + integrity sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w== json-parse-better-errors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" + integrity sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw== json-schema-traverse@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= dependencies: jsonify "~0.0.0" json-stable-stringify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz#611c23e814db375527df851193db59dd2af27f45" + integrity sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U= dependencies: jsonify "~0.0.0" json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= optionalDependencies: graceful-fs "^4.1.6" jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= optionalDependencies: graceful-fs "^4.1.6" jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= jsonpointer@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= jsonschema@1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.2.tgz#83ab9c63d65bf4d596f91d81195e78772f6452bc" + integrity sha512-iX5OFQ6yx9NgbHCwse51ohhKgLuLL7Z5cNOeZOPIlDUtAMrxlruHLzVZxbltdHE5mEDXN+75oFOwq6Gn0MZwsA== jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: assert-plus "1.0.0" extsprintf "1.3.0" @@ -4626,6 +5367,7 @@ jsprim@^1.2.2: jszip@^3.1.3: version "3.1.5" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37" + integrity sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ== dependencies: core-js "~2.3.0" es6-promise "~3.0.2" @@ -4636,10 +5378,12 @@ jszip@^3.1.3: jwt-decode@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.2.0.tgz#7d86bd56679f58ce6a84704a657dd392bba81a79" + integrity sha1-fYa9VmefWM5qhHBKZX3TkruoGnk= karma-chrome-launcher@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" + integrity sha512-uf/ZVpAabDBPvdPdveyk1EPgbnloPvFFGgmRhYLTDH7gEB4nZdSBk8yTU47w1g/drLSx5uMOkjKk7IWKfWg/+w== dependencies: fs-access "^1.0.0" which "^1.2.1" @@ -4647,12 +5391,14 @@ karma-chrome-launcher@2.2.0: karma-cli@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/karma-cli/-/karma-cli-1.0.1.tgz#ae6c3c58a313a1d00b45164c455b9b86ce17f960" + integrity sha1-rmw8WKMTodALRRZMRVubhs4X+WA= dependencies: resolve "^1.1.6" karma-coverage@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/karma-coverage/-/karma-coverage-1.1.1.tgz#5aff8b39cf6994dc22de4c84362c76001b637cf6" + integrity sha1-Wv+LOc9plNwi3kyENix2ABtjfPY= dependencies: dateformat "^1.0.6" istanbul "^0.4.0" @@ -4663,16 +5409,19 @@ karma-coverage@1.1.1: karma-istanbul-preprocessor@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/karma-istanbul-preprocessor/-/karma-istanbul-preprocessor-0.0.2.tgz#6965116c3b4c0b9d4ff62cb8b924349372be1eff" + integrity sha1-aWURbDtMC51P9iy4uSQ0k3K+Hv8= dependencies: istanbul "^0.4.3" karma-jasmine@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.1.tgz#6fe840e75a11600c9d91e84b33c458e1c46a3529" + integrity sha1-b+hA51oRYAydkehLM8RY4cRqNSk= karma-mocha-reporter@2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/karma-mocha-reporter/-/karma-mocha-reporter-2.2.5.tgz#15120095e8ed819186e47a0b012f3cd741895560" + integrity sha1-FRIAlejtgZGG5HoLAS8810GJVWA= dependencies: chalk "^2.1.0" log-symbols "^2.1.0" @@ -4681,6 +5430,7 @@ karma-mocha-reporter@2.2.5: karma-phantomjs-launcher@1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz#d23ca34801bda9863ad318e3bb4bd4062b13acd2" + integrity sha1-0jyjSAG9qYY60xjju0vUBisTrNI= dependencies: lodash "^4.0.1" phantomjs-prebuilt "^2.1.7" @@ -4688,12 +5438,14 @@ karma-phantomjs-launcher@1.0.4: karma-remap-coverage@^0.1.5: version "0.1.5" resolved "https://registry.yarnpkg.com/karma-remap-coverage/-/karma-remap-coverage-0.1.5.tgz#d2e3bb2dd00adcd256603a702b08c371370fbc12" + integrity sha512-FM5h8eHcHbMMR+2INBUxD+4+wUbkCnobfn5uWprkLyj6Xcm2MRFQOuAJn9h2H13nNso6rk+QoNpHd5xCevlPOw== dependencies: remap-istanbul "^0.10" karma-remap-istanbul@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/karma-remap-istanbul/-/karma-remap-istanbul-0.6.0.tgz#97f3b770065254f9b4724f2d9be4a3a2e1baf6fc" + integrity sha1-l/O3cAZSVPm0ck8tm+SjouG69vw= dependencies: istanbul "^0.4.3" remap-istanbul "^0.9.0" @@ -4701,18 +5453,21 @@ karma-remap-istanbul@0.6.0: karma-sourcemap-loader@0.3.7: version "0.3.7" resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.3.7.tgz#91322c77f8f13d46fed062b042e1009d4c4505d8" + integrity sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg= dependencies: graceful-fs "^4.1.2" karma-webdriver-launcher@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/karma-webdriver-launcher/-/karma-webdriver-launcher-1.0.5.tgz#b1c3cb347f26e786039c15abf7f19a7791e8ddd7" + integrity sha1-scPLNH8m54YDnBWr9/Gad5Ho3dc= dependencies: wd "^1.0.0" karma-webpack@2.0.9: version "2.0.9" resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-2.0.9.tgz#61c88091f7dd910635134c032b266a465affb57f" + integrity sha512-F1j3IG/XhiMzcunAXbWXH95uizjzr3WdTzmVWlta8xqxcCtAu9FByCb4sccIMxaVFAefpgnUW9KlCo0oLvIX6A== dependencies: async "~0.9.0" loader-utils "^0.2.5" @@ -4723,6 +5478,7 @@ karma-webpack@2.0.9: karma@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/karma/-/karma-2.0.0.tgz#a02698dd7f0f05ff5eb66ab8f65582490b512e58" + integrity sha512-K9Kjp8CldLyL9ANSUctDyxC7zH3hpqXj/K09qVf06K3T/kXaHtFZ5tQciK7OzQu68FLvI89Na510kqQ2LCbpIw== dependencies: bluebird "^3.3.0" body-parser "^1.16.1" @@ -4756,50 +5512,60 @@ karma@2.0.0: kew@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" + integrity sha1-edk9LTM2PW/dKXCzNdkUGtWR15s= killable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" + integrity sha1-2ouEvUfeU5WHj5XWTQLyRJ/gXms= kind-of@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" + integrity sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ= kind-of@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= dependencies: is-buffer "^1.0.2" kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0, kind-of@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= dependencies: is-buffer "^1.1.5" kind-of@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= dependencies: is-buffer "^1.1.5" kind-of@^5.0.0, kind-of@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.0.2.tgz#f57bec933d9a2209ffa96c5c08343607b7035fda" + integrity sha512-ru8+TQHbN8956c7ZlkgK5Imjx0GMat3jN45GNIthpPeb+SzLrqSg/NG7llQtIqUTbrdu5Oi0lSnIoJmDTwwSzw== kind-of@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== klaw@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= optionalDependencies: graceful-fs "^4.1.9" labeled-stream-splicer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz#a52e1d138024c00b86b1c0c91f677918b8ae0a59" + integrity sha1-pS4dE4AkwAuGscDJH2d5GLiuClk= dependencies: inherits "^2.0.1" isarray "~0.0.1" @@ -4808,42 +5574,50 @@ labeled-stream-splicer@^2.0.0: latest-version@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + integrity sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU= dependencies: package-json "^4.0.0" lazy-cache@^0.2.3: version "0.2.7" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= lazy-cache@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" + integrity sha1-uRkKT5EzVGlIQIWfio9whNiCImQ= dependencies: set-getter "^0.1.0" lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" + integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= dependencies: readable-stream "^2.0.5" lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= dependencies: invert-kv "^1.0.0" lcov-parse@^0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" + integrity sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM= levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -4851,16 +5625,19 @@ levn@~0.3.0: lexical-scope@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" + integrity sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ= dependencies: astw "^2.0.0" libbase64@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6" + integrity sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY= libmime@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/libmime/-/libmime-3.0.0.tgz#51a1a9e7448ecbd32cda54421675bb21bc093da6" + integrity sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY= dependencies: iconv-lite "0.4.15" libbase64 "0.1.0" @@ -4869,16 +5646,19 @@ libmime@3.0.0: libqp@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/libqp/-/libqp-1.1.0.tgz#f5e6e06ad74b794fb5b5b66988bf728ef1dedbe8" + integrity sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g= lie@~3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" + integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4= dependencies: immediate "~3.0.5" load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -4889,6 +5669,7 @@ load-json-file@^1.0.0: load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= dependencies: graceful-fs "^4.1.2" parse-json "^2.2.0" @@ -4898,6 +5679,7 @@ load-json-file@^2.0.0: load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= dependencies: graceful-fs "^4.1.2" parse-json "^4.0.0" @@ -4907,10 +5689,12 @@ load-json-file@^4.0.0: loader-runner@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + integrity sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI= loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@~0.2.2: version "0.2.17" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" + integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g= dependencies: big.js "^3.1.3" emojis-list "^2.0.0" @@ -4920,6 +5704,7 @@ loader-utils@^0.2.15, loader-utils@^0.2.16, loader-utils@^0.2.5, loader-utils@~0 loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + integrity sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0= dependencies: big.js "^3.1.3" emojis-list "^2.0.0" @@ -4928,6 +5713,7 @@ loader-utils@^1.0.0, loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1 locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= dependencies: p-locate "^2.0.0" path-exists "^3.0.0" @@ -4935,6 +5721,7 @@ locate-path@^2.0.0: lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + integrity sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4= dependencies: lodash._basecopy "^3.0.0" lodash.keys "^3.0.0" @@ -4942,22 +5729,27 @@ lodash._baseassign@^3.0.0: lodash._basecopy@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" + integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= lodash._basetostring@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" + integrity sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U= lodash._basevalues@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" + integrity sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc= lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" + integrity sha1-g4pbri/aymOsIt7o4Z+k5taXCxE= dependencies: lodash._bindcallback "^3.0.0" lodash._isiterateecall "^3.0.0" @@ -4966,30 +5758,37 @@ lodash._createassigner@^3.0.0: lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= lodash._isiterateecall@^3.0.0: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + integrity sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw= lodash._reescape@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" + integrity sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo= lodash._reevaluate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" + integrity sha1-WLx0xAZklTrgsSTYBpltrKQx4u0= lodash._reinterpolate@^3.0.0, lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0= lodash._root@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + integrity sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= lodash.assign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" + integrity sha1-POnwI0tLIiPilrj6CsH+6OvKZPo= dependencies: lodash._baseassign "^3.0.0" lodash._createassigner "^3.0.0" @@ -4998,18 +5797,22 @@ lodash.assign@^3.0.0: lodash.assign@^4.0.1, lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= lodash.clonedeep@^4.3.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= lodash.defaults@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c" + integrity sha1-xzCLGNv4vJNy1wGnNJPGEZK9Liw= dependencies: lodash.assign "^3.0.0" lodash.restparam "^3.0.0" @@ -5017,36 +5820,44 @@ lodash.defaults@^3.1.2: lodash.defaults@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= lodash.endswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09" + integrity sha1-/tWawXOO0+I27dcGTsRWRIs3vAk= lodash.escape@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" + integrity sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= dependencies: lodash._root "^3.0.0" lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo= lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + integrity sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U= lodash.isfunction@^3.0.8: version "3.0.9" resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + integrity sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo= dependencies: lodash._getnative "^3.0.0" lodash.isarguments "^3.0.0" @@ -5055,30 +5866,37 @@ lodash.keys@^3.0.0: lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= lodash.memoize@~3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8= lodash.mergewith@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" + integrity sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU= lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= lodash.startswith@^4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c" + integrity sha1-xZjErc4YiiflMUVzHNxsDnF3YAw= lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" + integrity sha1-0jM6NtnncXyK0vfKyv7HwytERmQ= lodash.template@^3.0.0: version "3.6.2" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" + integrity sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8= dependencies: lodash._basecopy "^3.0.0" lodash._basetostring "^3.0.0" @@ -5093,6 +5911,7 @@ lodash.template@^3.0.0: lodash.template@^4.2.4: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + integrity sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A= dependencies: lodash._reinterpolate "~3.0.0" lodash.templatesettings "^4.0.0" @@ -5100,6 +5919,7 @@ lodash.template@^4.2.4: lodash.templatesettings@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" + integrity sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU= dependencies: lodash._reinterpolate "^3.0.0" lodash.escape "^3.0.0" @@ -5107,44 +5927,53 @@ lodash.templatesettings@^3.0.0: lodash.templatesettings@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + integrity sha1-K01OlbpEDZFf8IvImeRVNmZxMxY= dependencies: lodash._reinterpolate "~3.0.0" lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= lodash@4.16.2: version "4.16.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.2.tgz#3e626db827048a699281a8a125226326cfc0e652" + integrity sha1-PmJtuCcEimmSgaihJSJjJs/A5lI= lodash@^3.8.0: version "3.10.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.5.0, lodash@^4.8.0, lodash@~4.17.4: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + integrity sha1-eCA6TRwyiuHYbcpkYONptX9AVa4= log-driver@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" + integrity sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY= log-symbols@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6" + integrity sha512-zLeLrzMA1A2vRF1e/0Mo+LNINzi6jzBylHj5WqvQ/WK/5WCZt8si9SyN4p9llr/HRYvVR1AoXHRHl4WTHyQAzQ== dependencies: chalk "^2.0.1" log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" log4js@^2.3.9: version "2.5.2" resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.2.tgz#234e9c688bc4aab3999bd4b149c85851a4e62faa" + integrity sha512-MmZhzQCfCV5+nQgOqy34V9EV3k+Z/rPCdxyq+25EePKpwdUQxCb19BTmL5iX3iOCSAV/tWh7KVYqchwrx3+S2Q== dependencies: circular-json "^0.5.1" date-format "^1.2.0" @@ -5164,6 +5993,7 @@ log4js@^2.3.9: loggly@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/loggly/-/loggly-1.1.1.tgz#0a0fc1d3fa3a5ec44fdc7b897beba2a4695cebee" + integrity sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4= dependencies: json-stringify-safe "5.0.x" request "2.75.x" @@ -5172,24 +6002,29 @@ loggly@^1.1.0: loglevel@^1.4.1: version "1.5.0" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.5.0.tgz#3863984a2c326b986fbb965f378758a6dc8a4324" + integrity sha512-OQ2jhWI5G2qsvO0UFNyCQWgKl/tFiwuPIXxELzACeUO2FqstN/R7mmL09+nhv6xOWVPPojQO1A90sCEoJSgBcQ== loglevelnext@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e" + integrity sha512-OCxd/b78TijTB4b6zVqLbMrxhebyvdZKwqpL0VHUZ0pYhavXaPD4l6Xrr4n5xqTYWiqtb0i7ikSoJY/myQ/Org== longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= loose-envify@^1.0.0: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + integrity sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg= dependencies: js-tokens "^3.0.0" loud-rejection@^1.0.0, loud-rejection@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= dependencies: currently-unhandled "^0.4.1" signal-exit "^3.0.0" @@ -5197,18 +6032,22 @@ loud-rejection@^1.0.0, loud-rejection@^1.6.0: lower-case@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY= lru-cache@2.2.x: version "2.2.4" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" + integrity sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0= lru-cache@^4.0.1, lru-cache@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + integrity sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew== dependencies: pseudomap "^1.0.2" yallist "^2.1.2" @@ -5216,26 +6055,31 @@ lru-cache@^4.0.1, lru-cache@^4.1.1: lru-cache@~2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" + integrity sha1-5W1jVBSO3o13B7WNFDIg/QjfD9U= macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + integrity sha1-WQTcU3w57G2+/q6QIycTX6hRHxI= magic-string@^0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.16.0.tgz#970ebb0da7193301285fb1aa650f39bdd81eb45a" + integrity sha1-lw67DacZMwEoX7GqZQ85vdgetFo= dependencies: vlq "^0.2.1" magic-string@^0.22.3, magic-string@^0.22.4: version "0.22.4" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.4.tgz#31039b4e40366395618c1d6cf8193c53917475ff" + integrity sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw== dependencies: vlq "^0.2.1" mailcomposer@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/mailcomposer/-/mailcomposer-4.0.1.tgz#0e1c44b2a07cf740ee17dc149ba009f19cadfeb4" + integrity sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ= dependencies: buildmail "4.0.1" libmime "3.0.0" @@ -5243,6 +6087,7 @@ mailcomposer@4.0.1: mailgun-js@^0.7.0: version "0.7.15" resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.7.15.tgz#ee366a20dac64c3c15c03d6c1b3e0ed795252abb" + integrity sha1-7jZqINrGTDwVwD1sGz4O15UlKrs= dependencies: async "~2.1.2" debug "~2.2.0" @@ -5257,42 +6102,51 @@ mailgun-js@^0.7.0: make-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.0.0.tgz#97a011751e91dd87cfadef58832ebb04936de978" + integrity sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg= dependencies: pify "^2.3.0" make-error@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.0.tgz#52ad3a339ccf10ce62b4040b708fe707244b8b96" + integrity sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y= map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= map-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= dependencies: object-visit "^1.0.0" marked@^0.3.5: version "0.3.12" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519" + integrity sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA== math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + integrity sha1-3oGf282E3M2PrlnGrreWFbnSZqw= md5.js@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + integrity sha1-6b296UogpawYsENA/Fdk1bCdkB0= dependencies: hash-base "^3.0.0" inherits "^2.0.1" @@ -5300,6 +6154,7 @@ md5.js@^1.3.4: md5@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= dependencies: charenc "~0.0.1" crypt "~0.0.1" @@ -5308,16 +6163,19 @@ md5@^2.2.1: media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= mem@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + integrity sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y= dependencies: mimic-fn "^1.0.0" memory-fs@^0.4.0, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= dependencies: errno "^0.1.3" readable-stream "^2.0.1" @@ -5325,10 +6183,12 @@ memory-fs@^0.4.0, memory-fs@~0.4.1: memorystream@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= meow@^3.3.0, meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= dependencies: camelcase-keys "^2.0.0" decamelize "^1.1.2" @@ -5344,18 +6204,22 @@ meow@^3.3.0, meow@^3.7.0: merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= merge@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + integrity sha1-dTHjnUlJwoGma4xabgJl6LBYlNo= methods@1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= dependencies: arr-diff "^2.0.0" array-unique "^0.2.1" @@ -5374,6 +6238,7 @@ micromatch@^2.1.5, micromatch@^2.3.11: micromatch@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.0.tgz#5102d4eaf20b6997d6008e3acfe1c44a3fa815e2" + integrity sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -5392,6 +6257,7 @@ micromatch@^3.0.3: micromatch@^3.1.4: version "3.1.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.5.tgz#d05e168c206472dfbca985bfef4f57797b4cd4ba" + integrity sha512-ykttrLPQrz1PUJcXjwsTUjGoPJ64StIGNE2lGVD1c9CuguJ+L7/navsE8IcDNndOoCMvYV0qc/exfVbMHkUhvA== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -5410,6 +6276,7 @@ micromatch@^3.1.4: miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== dependencies: bn.js "^4.0.0" brorand "^1.0.1" @@ -5417,62 +6284,76 @@ miller-rabin@^4.0.0: "mime-db@>= 1.29.0 < 2", mime-db@~1.30.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + integrity sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE= mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7: version "2.1.17" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + integrity sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo= dependencies: mime-db "~1.30.0" mime@1.4.1, mime@^1.3.4: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== mime@^1.4.1, mime@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== mime@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.2.0.tgz#161e541965551d3b549fa1114391e3a3d55b923b" + integrity sha512-0Qz9uF1ATtl8RKJG4VRfOymh7PyEor6NbrI/61lRfuRe4vx9SNATrvAeTj2EWVRKjEQGskrzWkJBBY5NbaVHIA== mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" + integrity sha1-5md4PZLonb00KBi1IwudYqZyrRg= minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M= minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= "minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= minimist@1.1.x: version "1.1.3" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" + integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag= minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= mississippi@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-1.3.0.tgz#d201583eb12327e3c5c1642a404a9cacf94e34f5" + integrity sha1-0gFYPrEjJ+PFwWQqQEqcrPlONPU= dependencies: concat-stream "^1.5.0" duplexify "^3.4.2" @@ -5488,6 +6369,7 @@ mississippi@^1.3.0: mixin-deep@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.2.0.tgz#d02b8c6f8b6d4b8f5982d3fd009c4919851c3fe2" + integrity sha1-0CuMb4ttS49ZgtP9AJxJGYUcP+I= dependencies: for-in "^1.0.2" is-extendable "^0.1.1" @@ -5495,6 +6377,7 @@ mixin-deep@^1.2.0: mixin-object@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= dependencies: for-in "^0.1.3" is-extendable "^0.1.1" @@ -5502,18 +6385,21 @@ mixin-object@^2.0.1: mkdirp@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + integrity sha1-HXMHam35hs2TROFecfzAWkyavxI= dependencies: minimist "0.0.8" mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" module-deps@^4.0.8: version "4.1.1" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.1.1.tgz#23215833f1da13fd606ccb8087b44852dcb821fd" + integrity sha1-IyFYM/HaE/1gbMuAh7RIUty4If0= dependencies: JSONStream "^1.0.3" browser-resolve "^1.7.0" @@ -5534,10 +6420,12 @@ module-deps@^4.0.8: moment@^2.19.3, moment@^2.22.1: version "2.22.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.1.tgz#529a2e9bf973f259c9643d237fda84de3a26e8ad" + integrity sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ== morgan@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.0.tgz#d01fa6c65859b76fcf31b3cb53a3821a311d8051" + integrity sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE= dependencies: basic-auth "~2.0.0" debug "2.6.9" @@ -5548,6 +6436,7 @@ morgan@1.9.0: move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= dependencies: aproba "^1.1.1" copy-concurrently "^1.0.0" @@ -5559,18 +6448,22 @@ move-concurrently@^1.0.1: ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + integrity sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg= ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= multicast-dns@^6.0.1: version "6.1.1" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.1.1.tgz#6e7de86a570872ab17058adea7160bbeca814dde" + integrity sha1-bn3oalcIcqsXBYrepxYLvsqBTd4= dependencies: dns-packet "^1.0.1" thunky "^0.1.0" @@ -5578,16 +6471,19 @@ multicast-dns@^6.0.1: multipipe@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" + integrity sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s= dependencies: duplexer2 "0.0.2" nan@^2.3.0, nan@^2.3.2: version "2.7.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46" + integrity sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY= nanomatch@^1.2.1: version "1.2.3" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.3.tgz#15e1c02dcf990c27a283b08c0ba1801ce249a6a6" + integrity sha512-HqDMQWJlwpXbfKDpAnkc6AJQh5PFqVlrjYbruDjYVAS+05TQUb1qhIde4G9jMzHbs/u6bgEok1jMAV4yJzoh+w== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -5604,6 +6500,7 @@ nanomatch@^1.2.1: nanomatch@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.7.tgz#53cd4aa109ff68b7f869591fdc9d10daeeea3e79" + integrity sha512-/5ldsnyurvEw7wNpxLFgjVvBLMta43niEYOy0CJ4ntcYSbx6bugRUTQeFb4BR/WanEL1o3aQgHuVLHQaB6tOqg== dependencies: arr-diff "^4.0.0" array-unique "^0.3.2" @@ -5620,52 +6517,63 @@ nanomatch@^1.2.5: ncname@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" + integrity sha1-W1etGLHKCShk72Kwse2BlPODtxw= dependencies: xml-char-classes "^1.0.0" negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= netmask@~1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" + integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= ng2-file-upload@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ng2-file-upload/-/ng2-file-upload-1.2.1.tgz#5563c5dfd6f43fbfbe815c206e343464a0a6a197" + integrity sha1-VWPF39b0P7++gVwgbjQ0ZKCmoZc= ng2-nouislider@^1.7.11: version "1.7.11" resolved "https://registry.yarnpkg.com/ng2-nouislider/-/ng2-nouislider-1.7.11.tgz#b8ba5e3d2ffc23e1e32dfe54dd1726e2b4be316b" + integrity sha1-uLpePS/8I+HjLf5U3Rcm4rS+MWs= ngrx-store-freeze@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ngrx-store-freeze/-/ngrx-store-freeze-0.2.1.tgz#04fb29db33cafda0f2d6ea32adeaac4891b1b27b" + integrity sha512-nQZJoyR03OqGR0dWWqIJgzkMj+99xnFgIY35Z5UXVhLEIfvk4HjuQ/iIeQU1dLHmZ8NnUDRFcrEthCSvE4eAWQ== dependencies: deep-freeze-strict "^1.1.1" ngx-infinite-scroll@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/ngx-infinite-scroll/-/ngx-infinite-scroll-0.8.2.tgz#9cc615c01fbb6307599453c9d9cfb5c1db4fd3e8" + integrity sha512-MtkfH/JL9f+nc+2Pv4QVYJwWNKCaW6ERSP0ZHtP2DfdJxDwWeHQA/nMrslBtOHkBy50fiBwuf1cDblXVk/H2Jw== ngx-pagination@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/ngx-pagination/-/ngx-pagination-3.0.3.tgz#314145263613738d8c544da36cd8dacc5aa89a6f" + integrity sha1-MUFFJjYTc42MVE2jbNjazFqomm8= no-case@^2.2.0: version "2.3.2" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== dependencies: lower-case "^1.1.1" node-forge@0.6.33: version "0.6.33" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc" + integrity sha1-RjgRh59XPUUVWtap9D3ClujoXrw= node-gyp@^3.3.1: version "3.6.2" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + integrity sha1-m/vlRWIoYoSDjnUOrAUpWFP6HGA= dependencies: fstream "^1.0.0" glob "^7.0.3" @@ -5684,6 +6592,7 @@ node-gyp@^3.3.1: node-libs-browser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646" + integrity sha1-o6WeyXAkmFtG6Vg3lkb5bEthZkY= dependencies: assert "^1.1.1" browserify-zlib "^0.1.4" @@ -5712,6 +6621,7 @@ node-libs-browser@^2.0.0: node-pre-gyp@^0.6.36: version "0.6.38" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz#e92a20f83416415bb4086f6d1fb78b3da73d113d" + integrity sha1-6Sog+DQWQVu0CG9tH7eLPac9ET0= dependencies: hawk "3.1.3" mkdirp "^0.5.1" @@ -5727,6 +6637,7 @@ node-pre-gyp@^0.6.36: node-sass@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" + integrity sha512-CaV+wLqZ7//Jdom5aUFCpGNoECd7BbNhjuwdsX/LkXBrHl8eb1Wjw4HvWqcFvhr5KuNgAk8i/myf/MQ1YYeroA== dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -5751,10 +6662,12 @@ node-sass@^4.7.2: node-uuid@~1.4.7: version "1.4.8" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + integrity sha1-sEDrCSOWivq/jTL7HxfxFn/auQc= nodemailer-direct-transport@3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz#e96fafb90358560947e569017d97e60738a50a86" + integrity sha1-6W+vuQNYVglH5WkBfZfmBzilCoY= dependencies: nodemailer-shared "1.1.0" smtp-connection "2.12.0" @@ -5762,16 +6675,19 @@ nodemailer-direct-transport@3.3.2: nodemailer-fetch@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz#79c4908a1c0f5f375b73fe888da9828f6dc963a4" + integrity sha1-ecSQihwPXzdbc/6IjamCj23JY6Q= nodemailer-shared@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz#cf5994e2fd268d00f5cf0fa767a08169edb07ec0" + integrity sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA= dependencies: nodemailer-fetch "1.6.0" nodemailer-smtp-pool@2.8.2: version "2.8.2" resolved "https://registry.yarnpkg.com/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz#2eb94d6cf85780b1b4725ce853b9cbd5e8da8c72" + integrity sha1-LrlNbPhXgLG0clzoU7nL1ejajHI= dependencies: nodemailer-shared "1.1.0" nodemailer-wellknown "0.1.10" @@ -5780,6 +6696,7 @@ nodemailer-smtp-pool@2.8.2: nodemailer-smtp-transport@2.7.2: version "2.7.2" resolved "https://registry.yarnpkg.com/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz#03d71c76314f14ac7dbc7bf033a6a6d16d67fb77" + integrity sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c= dependencies: nodemailer-shared "1.1.0" nodemailer-wellknown "0.1.10" @@ -5788,10 +6705,12 @@ nodemailer-smtp-transport@2.7.2: nodemailer-wellknown@0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz#586db8101db30cb4438eb546737a41aad0cf13d5" + integrity sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U= nodemailer@^2.5.0: version "2.7.2" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-2.7.2.tgz#f242e649aeeae39b6c7ed740ef7b061c404d30f9" + integrity sha1-8kLmSa7q45tsftdA73sGHEBNMPk= dependencies: libmime "3.0.0" mailcomposer "4.0.1" @@ -5804,6 +6723,7 @@ nodemailer@^2.5.0: nodemon@^1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.15.0.tgz#ddec01eeb9c33d53dfbf7eddb2fa32f723474c1e" + integrity sha512-0cXNs7jnvQJuxASvFCzrsqwpig1SQDqeDsjddlQLCU6h1KQiF6QBOX99tPs1YdMLXUMZkRuBSioPhe/zCOR75A== dependencies: chokidar "^2.0.2" debug "^3.1.0" @@ -5818,12 +6738,14 @@ nodemon@^1.15.0: "nopt@2 || 3", nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= dependencies: abbrev "1" nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= dependencies: abbrev "1" osenv "^0.1.4" @@ -5831,12 +6753,14 @@ nopt@^4.0.1: nopt@~1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= dependencies: abbrev "1" normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -5846,16 +6770,19 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= dependencies: remove-trailing-separator "^1.0.1" normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= normalize-url@^1.4.0: version "1.9.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= dependencies: object-assign "^4.0.1" prepend-http "^1.0.0" @@ -5865,10 +6792,12 @@ normalize-url@^1.4.0: nouislider@^11.0.0: version "11.1.0" resolved "https://registry.yarnpkg.com/nouislider/-/nouislider-11.1.0.tgz#1768eb5b854917325d41b96f2dc4eb3757d73381" + integrity sha512-nD+Fgc8A8j6hnGvR5AaV+OBuLF446z4H2fmcEJ/6U6CJr6rAnFnionMXu7dmdghZ+bhgePvL3wrDRbu+0ux7Jg== npm-run-all@4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.2.tgz#90d62d078792d20669139e718621186656cea056" + integrity sha512-Z2aRlajMK4SQ8u19ZA75NZZu7wupfCNQWdYosIi8S6FgBdGf/8Y6Hgyjdc8zU2cYmIRVCx1nM80tJPkdEd+UYg== dependencies: ansi-styles "^3.2.0" chalk "^2.1.0" @@ -5883,12 +6812,14 @@ npm-run-all@4.1.2: npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= dependencies: path-key "^2.0.0" "npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -5898,40 +6829,49 @@ npm-run-path@^2.0.0: nth-check@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + integrity sha1-mSms32KPwsQQmN6rgqxYDPFJquQ= dependencies: boolbase "~1.0.0" null-check@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" + integrity sha1-l33/1xdgErnsMNKjnbXPcqBDnt0= num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + integrity sha1-Rqarfwrq2N6unsBWV4C31O/rnUM= object-assign@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" + integrity sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I= object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-component@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= dependencies: copy-descriptor "^0.1.0" define-property "^0.2.5" @@ -5940,20 +6880,24 @@ object-copy@^0.1.0: object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + integrity sha1-xUYBd4rVYPEULODgG8yotW0TQm0= object-path@^0.9.2: version "0.9.2" resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.9.2.tgz#0fd9a74fc5fad1ae3968b586bda5c632bd6c05a5" + integrity sha1-D9mnT8X60a45aLWGvaXGMr1sBaU= object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= dependencies: isobject "^3.0.0" object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= dependencies: for-own "^0.1.4" is-extendable "^0.1.1" @@ -5961,46 +6905,55 @@ object.omit@^2.0.0: object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" obuf@^1.0.0, obuf@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" + integrity sha1-EEEktsYCxnlogaBCVB0220OlJk4= on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= dependencies: ee-first "1.1.1" on-headers@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + integrity sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c= once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" onecolor@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.4.tgz#75a46f80da6c7aaa5b4daae17a47198bd9652494" + integrity sha1-daRvgNpseqpbTarhekcZi9llJJQ= opener@^1.4.3, opener@~1.4.0: version "1.4.3" resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + integrity sha1-XG2ixdflgx6P+jlklQ+NZnSskLg= opn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519" + integrity sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg== dependencies: is-wsl "^1.1.0" optimist@0.6.x, optimist@^0.6.1, optimist@~0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" @@ -6008,6 +6961,7 @@ optimist@0.6.x, optimist@^0.6.1, optimist@~0.6.0: optionator@^0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.4" @@ -6019,34 +6973,41 @@ optionator@^0.8.1: options@>=0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" + integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= original@>=0.0.5: version "1.0.0" resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" + integrity sha1-kUf5P6FpbQS+YeAb1QuurKZWvTs= dependencies: url-parse "1.0.x" os-browserify@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" + integrity sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8= os-browserify@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= os-locale@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= dependencies: lcid "^1.0.0" os-locale@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + integrity sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA== dependencies: execa "^0.7.0" lcid "^1.0.0" @@ -6055,10 +7016,12 @@ os-locale@^2.0.0: os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= osenv@0, osenv@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + integrity sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ= dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" @@ -6066,34 +7029,41 @@ osenv@0, osenv@^0.1.4: p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" + integrity sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng== dependencies: p-try "^1.0.0" p-limit@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc" + integrity sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw= p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= dependencies: p-limit "^1.1.0" p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" + integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= pac-proxy-agent@1: version "1.1.0" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d" + integrity sha512-QBELCWyLYPgE2Gj+4wUEiMscHrQ8nRPBzYItQNOHWavwBt25ohZHQC4qnd5IszdVVrFbLsQ+dPkm6eqdjJAmwQ== dependencies: agent-base "2" debug "2" @@ -6108,6 +7078,7 @@ pac-proxy-agent@1: pac-resolver@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-2.0.0.tgz#99b88d2f193fbdeefc1c9a529c1f3260ab5277cd" + integrity sha1-mbiNLxk/ve78HJpSnB8yYKtSd80= dependencies: co "~3.0.6" degenerator "~1.0.2" @@ -6118,6 +7089,7 @@ pac-resolver@~2.0.0: package-json@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" + integrity sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0= dependencies: got "^6.7.1" registry-auth-token "^3.0.1" @@ -6127,14 +7099,17 @@ package-json@^4.0.0: pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= pako@~1.0.2, pako@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== parallel-transform@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= dependencies: cyclist "~0.2.2" inherits "^2.0.3" @@ -6143,18 +7118,21 @@ parallel-transform@^1.1.0: param-case@2.1.x: version "2.1.1" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc= dependencies: no-case "^2.2.0" parents@^1.0.0, parents@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751" + integrity sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E= dependencies: path-platform "~0.11.15" parse-asn1@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" + integrity sha1-N8T5t+06tlx0gXtfJICTf7+XxxI= dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -6165,6 +7143,7 @@ parse-asn1@^5.0.0: parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= dependencies: glob-base "^0.3.0" is-dotfile "^1.0.0" @@ -6174,12 +7153,14 @@ parse-glob@^3.0.4: parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= dependencies: error-ex "^1.2.0" parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" @@ -6187,78 +7168,95 @@ parse-json@^4.0.0: parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= parseqs@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= dependencies: better-assert "~1.0.0" parseuri@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= dependencies: better-assert "~1.0.0" parseurl@~1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= path-browserify@0.0.0, path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= dependencies: pinkie-promise "^2.0.0" path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-is-inside@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= path-key@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= path-parse@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + integrity sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME= path-platform@~0.11.15: version "0.11.15" resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + integrity sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I= path-proxy@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/path-proxy/-/path-proxy-1.0.0.tgz#18e8a36859fc9d2f1a53b48dee138543c020de5e" + integrity sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4= dependencies: inflection "~1.3.0" path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= dependencies: graceful-fs "^4.1.2" pify "^2.0.0" @@ -6267,24 +7265,28 @@ path-type@^1.0.0: path-type@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= dependencies: pify "^2.0.0" path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== dependencies: pify "^3.0.0" pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= dependencies: through "~2.3" pbkdf2@^3.0.3: version "3.0.14" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade" + integrity sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA== dependencies: create-hash "^1.1.2" create-hmac "^1.1.4" @@ -6295,6 +7297,7 @@ pbkdf2@^3.0.3: pem@1.12.3: version "1.12.3" resolved "https://registry.yarnpkg.com/pem/-/pem-1.12.3.tgz#b1fb5c8b79da8d18146c27fee79b0d4ddf9905b3" + integrity sha512-hT7GwvQL35+0iqgYUl8vn5I5pAVR0HcJas07TXL8bNaR4c5kAFRquk4ZqQk1F9YMcQOr6WjGdY5OnDC0RBnzig== dependencies: md5 "^2.2.1" os-tmpdir "^1.0.1" @@ -6304,18 +7307,22 @@ pem@1.12.3: pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= phantomjs-prebuilt@^2.1.7: version "2.1.15" resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.15.tgz#20f86e82d3349c505917527745b7a411e08b3903" + integrity sha1-IPhugtM0nFBZF1J3RbekEeCLOQM= dependencies: es6-promise "~4.0.3" extract-zip "~1.6.5" @@ -6330,24 +7337,29 @@ phantomjs-prebuilt@^2.1.7: pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pixrem@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/pixrem/-/pixrem-4.0.1.tgz#2da4a1de6ec4423c5fc3794e930b81d4490ec686" + integrity sha1-LaSh3m7EQjxfw3lOkwuB1EkOxoY= dependencies: browserslist "^2.0.0" postcss "^6.0.0" @@ -6356,12 +7368,14 @@ pixrem@^4.0.0: pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= dependencies: find-up "^2.1.0" pleeease-filters@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pleeease-filters/-/pleeease-filters-4.0.0.tgz#6632b2fb05648d2758d865384fbced79e1ccaec7" + integrity sha1-ZjKy+wVkjSdY2GU4T7zteeHMrsc= dependencies: onecolor "^3.0.4" postcss "^6.0.1" @@ -6369,6 +7383,7 @@ pleeease-filters@^4.0.0: plugin-error@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" + integrity sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4= dependencies: ansi-cyan "^0.1.1" ansi-red "^0.1.1" @@ -6379,6 +7394,7 @@ plugin-error@^0.1.2: portfinder@^1.0.13, portfinder@^1.0.9: version "1.0.13" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" + integrity sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek= dependencies: async "^1.5.2" debug "^2.2.0" @@ -6387,10 +7403,12 @@ portfinder@^1.0.13, portfinder@^1.0.9: posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= postcss-apply@0.8.0, postcss-apply@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/postcss-apply/-/postcss-apply-0.8.0.tgz#14e544bbb5cb6f1c1e048857965d79ae066b1343" + integrity sha1-FOVEu7XLbxweBIhXll15rgZrE0M= dependencies: babel-runtime "^6.23.0" balanced-match "^0.4.2" @@ -6399,6 +7417,7 @@ postcss-apply@0.8.0, postcss-apply@^0.8.0: postcss-attribute-case-insensitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-2.0.0.tgz#94dc422c8f90997f16bd33a3654bbbec084963b4" + integrity sha1-lNxCLI+QmX8WvTOjZUu77AhJY7Q= dependencies: postcss "^6.0.0" postcss-selector-parser "^2.2.3" @@ -6406,6 +7425,7 @@ postcss-attribute-case-insensitive@^2.0.0: postcss-calc@^5.2.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + integrity sha1-d7rnypKK2FcW4v2kLyYb98HWW14= dependencies: postcss "^5.0.2" postcss-message-helpers "^2.0.0" @@ -6414,6 +7434,7 @@ postcss-calc@^5.2.0: postcss-calc@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-6.0.0.tgz#b681b279c6d24fbe0e33ed9045803705445d613b" + integrity sha1-toGyecbST74OM+2QRYA3BURdYTs= dependencies: css-unit-converter "^1.1.1" postcss "^6.0.0" @@ -6423,6 +7444,7 @@ postcss-calc@^6.0.0: postcss-cli@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/postcss-cli/-/postcss-cli-5.0.0.tgz#3d6aee7652b8dc6566f0189dd90d11bb0a535a20" + integrity sha512-LbSZQUu1fh7hiIU/JCitSWd+V3MO60DWi9ETUKiO7i9fDZN/9XqI3ACbAOnymd9a+8v5x5XI5SjeEv4/R+l/Ow== dependencies: chalk "^2.1.0" chokidar "^2.0.0" @@ -6440,6 +7462,7 @@ postcss-cli@^5.0.0: postcss-color-function@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-color-function/-/postcss-color-function-4.0.0.tgz#7e0106f4f6a1ecb1ad5b3a8553ace5e828aae187" + integrity sha1-fgEG9Pah7LGtWzqFU6zl6Ciq4Yc= dependencies: css-color-function "^1.3.0" postcss "^6.0.1" @@ -6449,6 +7472,7 @@ postcss-color-function@^4.0.0: postcss-color-gray@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-4.0.0.tgz#681bf305097dd66bfef0e1e6282d5d99b5acc95d" + integrity sha1-aBvzBQl91mv+8OHmKC1dmbWsyV0= dependencies: color "^1.0.3" postcss "^6.0.1" @@ -6458,6 +7482,7 @@ postcss-color-gray@^4.0.0: postcss-color-hex-alpha@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-3.0.0.tgz#1e53e6c8acb237955e8fd08b7ecdb1b8b8309f95" + integrity sha1-HlPmyKyyN5Vej9CLfs2xuLgwn5U= dependencies: color "^1.0.3" postcss "^6.0.1" @@ -6466,6 +7491,7 @@ postcss-color-hex-alpha@^3.0.0: postcss-color-hsl@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-color-hsl/-/postcss-color-hsl-2.0.0.tgz#12703666fa310430e3f30a454dac1386317d5844" + integrity sha1-EnA2ZvoxBDDj8wpFTawThjF9WEQ= dependencies: postcss "^6.0.1" postcss-value-parser "^3.3.0" @@ -6474,6 +7500,7 @@ postcss-color-hsl@^2.0.0: postcss-color-hwb@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-color-hwb/-/postcss-color-hwb-3.0.0.tgz#3402b19ef4d8497540c1fb5072be9863ca95571e" + integrity sha1-NAKxnvTYSXVAwftQcr6YY8qVVx4= dependencies: color "^1.0.3" postcss "^6.0.1" @@ -6483,6 +7510,7 @@ postcss-color-hwb@^3.0.0: postcss-color-rebeccapurple@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-3.0.0.tgz#eebaf03d363b4300b96792bd3081c19ed66513d3" + integrity sha1-7rrwPTY7QwC5Z5K9MIHBntZlE9M= dependencies: postcss "^6.0.1" postcss-value-parser "^3.3.0" @@ -6490,6 +7518,7 @@ postcss-color-rebeccapurple@^3.0.0: postcss-color-rgb@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-color-rgb/-/postcss-color-rgb-2.0.0.tgz#14539c8a7131494b482e0dd1cc265ff6514b5263" + integrity sha1-FFOcinExSUtILg3RzCZf9lFLUmM= dependencies: postcss "^6.0.1" postcss-value-parser "^3.3.0" @@ -6497,6 +7526,7 @@ postcss-color-rgb@^2.0.0: postcss-color-rgba-fallback@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-color-rgba-fallback/-/postcss-color-rgba-fallback-3.0.0.tgz#37d5c9353a07a09270912a82606bb42a0d702c04" + integrity sha1-N9XJNToHoJJwkSqCYGu0Kg1wLAQ= dependencies: postcss "^6.0.6" postcss-value-parser "^3.3.0" @@ -6505,6 +7535,7 @@ postcss-color-rgba-fallback@^3.0.0: postcss-colormin@^2.1.8: version "2.2.2" resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + integrity sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks= dependencies: colormin "^1.0.5" postcss "^5.0.13" @@ -6513,6 +7544,7 @@ postcss-colormin@^2.1.8: postcss-convert-values@^2.3.4: version "2.6.1" resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + integrity sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0= dependencies: postcss "^5.0.11" postcss-value-parser "^3.1.2" @@ -6520,6 +7552,7 @@ postcss-convert-values@^2.3.4: postcss-cssnext@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-3.1.0.tgz#927dc29341a938254cde38ea60a923b9dfedead9" + integrity sha512-awPDhI4OKetcHCr560iVCoDuP6e/vn0r6EAqdWPpAavJMvkBSZ6kDpSN4b3mB3Ti57hQMunHHM8Wvx9PeuYXtA== dependencies: autoprefixer "^7.1.1" caniuse-api "^2.0.0" @@ -6556,12 +7589,14 @@ postcss-cssnext@3.1.0: postcss-custom-media@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-6.0.0.tgz#be532784110ecb295044fb5395a18006eb21a737" + integrity sha1-vlMnhBEOyylQRPtTlaGABushpzc= dependencies: postcss "^6.0.1" postcss-custom-properties@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-6.1.0.tgz#9caf1151ac41b1e9e64d3a2ff9ece996ca18977d" + integrity sha1-nK8RUaxBsenmTTov+ezplsoYl30= dependencies: balanced-match "^1.0.0" postcss "^6.0.3" @@ -6569,6 +7604,7 @@ postcss-custom-properties@^6.1.0: postcss-custom-selectors@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-4.0.1.tgz#781382f94c52e727ef5ca4776ea2adf49a611382" + integrity sha1-eBOC+UxS5yfvXKR3bqKt9JphE4I= dependencies: postcss "^6.0.1" postcss-selector-matches "^3.0.0" @@ -6576,30 +7612,35 @@ postcss-custom-selectors@^4.0.1: postcss-discard-comments@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + integrity sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0= dependencies: postcss "^5.0.14" postcss-discard-duplicates@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + integrity sha1-uavye4isGIFYpesSq8riAmO5GTI= dependencies: postcss "^5.0.4" postcss-discard-empty@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + integrity sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU= dependencies: postcss "^5.0.14" postcss-discard-overridden@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + integrity sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg= dependencies: postcss "^5.0.16" postcss-discard-unused@^2.2.1: version "2.2.3" resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + integrity sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM= dependencies: postcss "^5.0.14" uniqs "^2.0.0" @@ -6607,6 +7648,7 @@ postcss-discard-unused@^2.2.1: postcss-filter-plugins@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" + integrity sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew= dependencies: postcss "^5.0.4" uniqid "^4.0.0" @@ -6614,18 +7656,21 @@ postcss-filter-plugins@^2.0.0: postcss-font-family-system-ui@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-font-family-system-ui/-/postcss-font-family-system-ui-3.0.0.tgz#675fe7a9e029669f05f8dba2e44c2225ede80623" + integrity sha512-58G/hTxMSSKlIRpcPUjlyo6hV2MEzvcVO2m4L/T7Bb2fJTG4DYYfQjQeRvuimKQh1V1sOzCIz99g+H2aFNtlQw== dependencies: postcss "^6.0" postcss-font-variant@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-3.0.0.tgz#08ccc88f6050ba82ed8ef2cc76c0c6a6b41f183e" + integrity sha1-CMzIj2BQuoLtjvLMdsDGprQfGD4= dependencies: postcss "^6.0.1" postcss-image-set-polyfill@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/postcss-image-set-polyfill/-/postcss-image-set-polyfill-0.3.5.tgz#0f193413700cf1f82bd39066ef016d65a4a18181" + integrity sha1-Dxk0E3AM8fgr05Bm7wFtZaShgYE= dependencies: postcss "^6.0.1" postcss-media-query-parser "^0.2.3" @@ -6633,6 +7678,7 @@ postcss-image-set-polyfill@^0.3.5: postcss-initial@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-2.0.0.tgz#72715f7336e0bb79351d99ee65c4a253a8441ba4" + integrity sha1-cnFfczbgu3k1HZnuZcSiU6hEG6Q= dependencies: lodash.template "^4.2.4" postcss "^6.0.1" @@ -6640,6 +7686,7 @@ postcss-initial@^2.0.0: postcss-load-config@^1.1.0, postcss-load-config@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a" + integrity sha1-U56a/J3chiASHr+djDZz4M5Q0oo= dependencies: cosmiconfig "^2.1.0" object-assign "^4.1.0" @@ -6649,6 +7696,7 @@ postcss-load-config@^1.1.0, postcss-load-config@^1.2.0: postcss-load-options@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c" + integrity sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw= dependencies: cosmiconfig "^2.1.0" object-assign "^4.1.0" @@ -6656,6 +7704,7 @@ postcss-load-options@^1.2.0: postcss-load-plugins@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92" + integrity sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI= dependencies: cosmiconfig "^2.1.1" object-assign "^4.1.0" @@ -6663,6 +7712,7 @@ postcss-load-plugins@^2.3.0: postcss-loader@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.0.tgz#038c2d6d59753fef4667827fd3ae03f5dc5e6a7a" + integrity sha512-S/dKzpDwGFmP9g8eyCu9sUIV+/+3UooeTpYlsKf23qKDdrhHuA4pTSfytVu0rEJ0iDqUavXrgtOPq5KhNyNMOw== dependencies: loader-utils "^1.1.0" postcss "^6.0.0" @@ -6672,16 +7722,19 @@ postcss-loader@^2.1.0: postcss-media-minmax@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-3.0.0.tgz#675256037a43ef40bc4f0760bfd06d4dc69d48d2" + integrity sha1-Z1JWA3pD70C8Twdgv9BtTcadSNI= dependencies: postcss "^6.0.1" postcss-media-query-parser@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ= postcss-merge-idents@^2.1.5: version "2.1.7" resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + integrity sha1-TFUwMTwI4dWzu/PSu8dH4njuonA= dependencies: has "^1.0.1" postcss "^5.0.10" @@ -6690,12 +7743,14 @@ postcss-merge-idents@^2.1.5: postcss-merge-longhand@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + integrity sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg= dependencies: postcss "^5.0.4" postcss-merge-rules@^2.0.3: version "2.1.2" resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + integrity sha1-0d9d+qexrMO+VT8OnhDofGG19yE= dependencies: browserslist "^1.5.2" caniuse-api "^1.5.2" @@ -6706,10 +7761,12 @@ postcss-merge-rules@^2.0.3: postcss-message-helpers@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + integrity sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4= postcss-minify-font-values@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + integrity sha1-S1jttWZB66fIR0qzUmyv17vey2k= dependencies: object-assign "^4.0.1" postcss "^5.0.4" @@ -6718,6 +7775,7 @@ postcss-minify-font-values@^1.0.2: postcss-minify-gradients@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + integrity sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE= dependencies: postcss "^5.0.12" postcss-value-parser "^3.3.0" @@ -6725,6 +7783,7 @@ postcss-minify-gradients@^1.0.1: postcss-minify-params@^1.0.4: version "1.2.2" resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + integrity sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM= dependencies: alphanum-sort "^1.0.1" postcss "^5.0.2" @@ -6734,6 +7793,7 @@ postcss-minify-params@^1.0.4: postcss-minify-selectors@^2.0.4: version "2.1.1" resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + integrity sha1-ssapjAByz5G5MtGkllCBFDEXNb8= dependencies: alphanum-sort "^1.0.2" has "^1.0.1" @@ -6743,12 +7803,14 @@ postcss-minify-selectors@^2.0.4: postcss-modules-extract-imports@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" + integrity sha1-ZhQOzs447wa/DT41XWm/WdFB6oU= dependencies: postcss "^6.0.1" postcss-modules-local-by-default@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + integrity sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk= dependencies: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" @@ -6756,6 +7818,7 @@ postcss-modules-local-by-default@^1.2.0: postcss-modules-scope@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + integrity sha1-1upkmUx5+XtipytCb75gVqGUu5A= dependencies: css-selector-tokenizer "^0.7.0" postcss "^6.0.1" @@ -6763,6 +7826,7 @@ postcss-modules-scope@^1.1.0: postcss-modules-values@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + integrity sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA= dependencies: icss-replace-symbols "^1.1.0" postcss "^6.0.1" @@ -6770,18 +7834,21 @@ postcss-modules-values@^1.3.0: postcss-nesting@^4.0.1: version "4.2.1" resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-4.2.1.tgz#0483bce338b3f0828ced90ff530b29b98b00300d" + integrity sha512-IkyWXICwagCnlaviRexi7qOdwPw3+xVVjgFfGsxmztvRVaNxAlrypOIKqDE5mxY+BVxnId1rnUKBRQoNE2VDaA== dependencies: postcss "^6.0.11" postcss-normalize-charset@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + integrity sha1-757nEhLX/nWceO0WL2HtYrXLk/E= dependencies: postcss "^5.0.5" postcss-normalize-url@^3.0.7: version "3.0.8" resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + integrity sha1-EI90s/L82viRov+j6kWSJ5/HgiI= dependencies: is-absolute-url "^2.0.0" normalize-url "^1.4.0" @@ -6791,6 +7858,7 @@ postcss-normalize-url@^3.0.7: postcss-ordered-values@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + integrity sha1-7sbCpntsQSqNsgQud/6NpD+VwR0= dependencies: postcss "^5.0.4" postcss-value-parser "^3.0.1" @@ -6798,6 +7866,7 @@ postcss-ordered-values@^2.1.0: postcss-pseudo-class-any-link@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-4.0.0.tgz#9152a0613d3450720513e8892854bae42d0ee68e" + integrity sha1-kVKgYT00UHIFE+iJKFS65C0O5o4= dependencies: postcss "^6.0.1" postcss-selector-parser "^2.2.3" @@ -6805,12 +7874,14 @@ postcss-pseudo-class-any-link@^4.0.0: postcss-pseudoelements@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/postcss-pseudoelements/-/postcss-pseudoelements-5.0.0.tgz#eef194e8d524645ca520a949e95e518e812402cb" + integrity sha1-7vGU6NUkZFylIKlJ6V5RjoEkAss= dependencies: postcss "^6.0.0" postcss-reduce-idents@^2.2.2: version "2.4.0" resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + integrity sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM= dependencies: postcss "^5.0.4" postcss-value-parser "^3.0.2" @@ -6818,12 +7889,14 @@ postcss-reduce-idents@^2.2.2: postcss-reduce-initial@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + integrity sha1-aPgGlfBF0IJjqHmtJA343WT2ROo= dependencies: postcss "^5.0.4" postcss-reduce-transforms@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + integrity sha1-/3b02CEkN7McKYpC0uFEQCV3GuE= dependencies: has "^1.0.1" postcss "^5.0.8" @@ -6832,12 +7905,14 @@ postcss-reduce-transforms@^1.0.3: postcss-replace-overflow-wrap@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-2.0.0.tgz#794db6faa54f8db100854392a93af45768b4e25b" + integrity sha1-eU22+qVPjbEAhUOSqTr0V2i04ls= dependencies: postcss "^6.0.1" postcss-reporter@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-5.0.0.tgz#a14177fd1342829d291653f2786efd67110332c3" + integrity sha512-rBkDbaHAu5uywbCR2XE8a25tats3xSOsGNx6mppK6Q9kSFGKc/FyAzfci+fWM2l+K402p1D0pNcfDGxeje5IKg== dependencies: chalk "^2.0.1" lodash "^4.17.4" @@ -6847,12 +7922,14 @@ postcss-reporter@^5.0.0: postcss-responsive-type@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/postcss-responsive-type/-/postcss-responsive-type-1.0.0.tgz#bb2d57d830beb9586ec4fda7994f07e37953aad8" + integrity sha1-uy1X2DC+uVhuxP2nmU8H43lTqtg= dependencies: postcss "^6.0.6" postcss-sass@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.2.0.tgz#e55516441e9526ba4b380a730d3a02e9eaa78c7a" + integrity sha512-cUmYzkP747fPCQE6d+CH2l1L4VSyIlAzZsok3HPjb5Gzsq3jE+VjpAdGlPsnQ310WKWI42sw+ar0UNN59/f3hg== dependencies: gonzales-pe "^4.0.3" postcss "^6.0.6" @@ -6860,12 +7937,14 @@ postcss-sass@^0.2.0: postcss-scss@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-1.0.2.tgz#ff45cf3354b879ee89a4eb68680f46ac9bb14f94" + integrity sha1-/0XPM1S4ee6JpOtoaA9GrJuxT5Q= dependencies: postcss "^6.0.3" postcss-selector-matches@^3.0.0, postcss-selector-matches@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-3.0.1.tgz#e5634011e13950881861bbdd58c2d0111ffc96ab" + integrity sha1-5WNAEeE5UIgYYbvdWMLQER/8lqs= dependencies: balanced-match "^0.4.2" postcss "^6.0.1" @@ -6873,6 +7952,7 @@ postcss-selector-matches@^3.0.0, postcss-selector-matches@^3.0.1: postcss-selector-not@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-3.0.1.tgz#2e4db2f0965336c01e7cec7db6c60dff767335d9" + integrity sha1-Lk2y8JZTNsAefOx9tsYN/3ZzNdk= dependencies: balanced-match "^0.4.2" postcss "^6.0.1" @@ -6880,6 +7960,7 @@ postcss-selector-not@^3.0.1: postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2, postcss-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + integrity sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A= dependencies: flatten "^1.0.2" indexes-of "^1.0.1" @@ -6888,6 +7969,7 @@ postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2, postcss-selector postcss-smart-import@0.7.6: version "0.7.6" resolved "https://registry.yarnpkg.com/postcss-smart-import/-/postcss-smart-import-0.7.6.tgz#259deb84aa28f138458218ecc0e9a84c61ada6a4" + integrity sha512-9OpXaQ1uMMHWafUh0RWIpAKa3xxUDC2yyxicUPpGffH33nzbZG4/z+nk5Ocw5gGZ+3qkXV91iDV23Cmxf2Jhew== dependencies: babel-runtime "^6.26.0" lodash "^4.17.4" @@ -6904,6 +7986,7 @@ postcss-smart-import@0.7.6: postcss-svgo@^2.1.1: version "2.1.6" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + integrity sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0= dependencies: is-svg "^2.0.0" postcss "^5.0.14" @@ -6913,6 +7996,7 @@ postcss-svgo@^2.1.1: postcss-unique-selectors@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + integrity sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0= dependencies: alphanum-sort "^1.0.1" postcss "^5.0.4" @@ -6921,10 +8005,12 @@ postcss-unique-selectors@^2.0.2: postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + integrity sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU= postcss-zindex@^2.0.1: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + integrity sha1-0hCd3AVbka9n/EyzsCWUZjnSryI= dependencies: has "^1.0.1" postcss "^5.0.4" @@ -6933,6 +8019,7 @@ postcss-zindex@^2.0.1: postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: version "5.2.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.17.tgz#cf4f597b864d65c8a492b2eabe9d706c879c388b" + integrity sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs= dependencies: chalk "^1.1.3" js-base64 "^2.1.9" @@ -6942,6 +8029,7 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0 postcss@^6.0, postcss@^6.0.14: version "6.0.16" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.16.tgz#112e2fe2a6d2109be0957687243170ea5589e146" + integrity sha512-m758RWPmSjFH/2MyyG3UOW1fgYbR9rtdzz5UNJnlm7OLtu4B2h9C6gi+bE4qFKghsBRFfZT8NzoQBs6JhLotoA== dependencies: chalk "^2.3.0" source-map "^0.6.1" @@ -6950,6 +8038,7 @@ postcss@^6.0, postcss@^6.0.14: postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.13, postcss@^6.0.3, postcss@^6.0.5, postcss@^6.0.6, postcss@^6.0.8: version "6.0.13" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.13.tgz#b9ecab4ee00c89db3ec931145bd9590bbf3f125f" + integrity sha512-nHsrD1PPTMSJDfU+osVsLtPkSP9YGeoOz4FDLN4r1DW4N5vqL1J+gACzTQHsfwIiWG/0/nV4yCzjTMo1zD8U1g== dependencies: chalk "^2.1.0" source-map "^0.6.1" @@ -6958,6 +8047,7 @@ postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.13, postcss@^6.0.3 postcss@^6.0.17, postcss@^6.0.18: version "6.0.18" resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.18.tgz#370f5f44d47f3a205f0eb2f6262bbf202df2a80e" + integrity sha512-X8MyLi3OYI1o71u0SsefWLpGBo5xnGiK1Pn+nrZFplc671Ts7L8aPwEbPIO8AWpulK5wuaVzyM9Rw6R8o7hYBw== dependencies: chalk "^2.3.1" source-map "^0.6.1" @@ -6966,18 +8056,22 @@ postcss@^6.0.17, postcss@^6.0.18: prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prepend-http@^1.0.0, prepend-http@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= pretty-error@^2.0.2: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" + integrity sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM= dependencies: renderkid "^2.0.1" utila "~0.4" @@ -6985,40 +8079,49 @@ pretty-error@^2.0.2: pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= process-es6@^0.11.3: version "0.11.6" resolved "https://registry.yarnpkg.com/process-es6/-/process-es6-0.11.6.tgz#c6bb389f9a951f82bd4eb169600105bd2ff9c778" + integrity sha1-xrs4n5qVH4K9TrFpYAEFvS/5x3g= process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= process@^0.11.0, process@~0.11.0: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8= progress@~1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= promise-each@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/promise-each/-/promise-each-2.2.0.tgz#3353174eff2694481037e04e01f77aa0fb6d1b60" + integrity sha1-M1MXTv8mlEgQN+BOAfd6oPttG2A= dependencies: any-promise "^0.1.0" promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= protractor-istanbul-plugin@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/protractor-istanbul-plugin/-/protractor-istanbul-plugin-2.0.0.tgz#f6271d2a5d6382488e86ff9fb7770f46a8b2c5e2" + integrity sha1-9icdKl1jgkiOhv+ft3cPRqiyxeI= dependencies: fs-extra "^0.22.1" merge "^1.2.0" @@ -7028,6 +8131,7 @@ protractor-istanbul-plugin@2.0.0: protractor@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/protractor/-/protractor-5.3.0.tgz#5df98201cbdaeb50826af6d05630ef1945bf9c32" + integrity sha512-8z1TWtc/I9Kn4fkfg87DhkSAi0arul7DHBEeJ70sy66teQAeffjQED1s0Gduigme7hxHRYdYEKbhHYz28fpv5w== dependencies: "@types/node" "^6.0.46" "@types/q" "^0.0.32" @@ -7048,6 +8152,7 @@ protractor@^5.3.0: proxy-addr@~2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec" + integrity sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew= dependencies: forwarded "~0.1.2" ipaddr.js "1.5.2" @@ -7055,6 +8160,7 @@ proxy-addr@~2.0.2: proxy-agent@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" + integrity sha1-V+tTR6qAXXTsaByyVknbo5yTNJk= dependencies: agent-base "2" debug "2" @@ -7068,26 +8174,31 @@ proxy-agent@~2.0.0: prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + integrity sha1-GoS4WQgyVQFBGFPQCB7j+obikmo= ps-tree@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" + integrity sha1-tCGyQUDWID8e08dplrRCewjowBQ= dependencies: event-stream "~3.3.0" pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= pstree.remy@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.0.tgz#f2af27265bd3e5b32bbfcc10e80bac55ba78688b" + integrity sha512-q5I5vLRMVtdWa8n/3UEzZX7Lfghzrg9eG2IKk2ENLSofKRCXVqMvMUHxCKgXNaqH/8ebhBxrqftHWnyTFweJ5Q== dependencies: ps-tree "^1.1.0" public-encrypt@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + integrity sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY= dependencies: bn.js "^4.1.0" browserify-rsa "^4.0.0" @@ -7098,6 +8209,7 @@ public-encrypt@^4.0.0: pump@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -7105,6 +8217,7 @@ pump@^1.0.0: pump@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.0.tgz#7946da1c8d622b098e2ceb2d3476582470829c9d" + integrity sha512-6MYypjOvtiXhBSTOD0Zs5eNjCGfnqi5mPsCsW+dgKTxrZzQMZQNpBo3XRkLx7id753f3EeyHLBqzqqUymIolgw== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -7112,6 +8225,7 @@ pump@^2.0.0: pumpify@^1.3.3: version "1.4.0" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb" + integrity sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA== dependencies: duplexify "^3.5.3" inherits "^2.0.3" @@ -7120,42 +8234,52 @@ pumpify@^1.3.3: punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= punycode@1.4.1, punycode@^1.2.4, punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= q@1.4.1, q@^1.1.2, q@^1.4.1, q@~1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + integrity sha1-VXBbzZPF82c1MMLCy8DCs63cKG4= qjobs@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73" + integrity sha1-ZZ3p8s+NzCehSBJ28gU3cnI4LnM= qs@6.5.1, qs@~6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + integrity sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A== qs@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/qs/-/qs-2.3.3.tgz#e9e85adbe75da0bbe4c8e0476a086290f863b404" + integrity sha1-6eha2+ddoLvkyOBHaghikPhjtAQ= qs@~6.2.0: version "6.2.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + integrity sha1-HPyyXBCpsrSDBT/zn138kjOQjP4= qs@~6.3.0: version "6.3.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + integrity sha1-51vV9uJoEioqDgvaYwslUMFmUCw= qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + integrity sha1-E+JtKK1rD/qpExLNO/cI7TUecjM= query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= dependencies: object-assign "^4.1.0" strict-uri-encode "^1.0.0" @@ -7163,26 +8287,32 @@ query-string@^4.1.0: querystring-es3@^0.2.0, querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= querystringify@0.0.x: version "0.0.4" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" + integrity sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw= querystringify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" + integrity sha1-YoYkIRLFtxL6ZU5SZlK/ahP/Bcs= random-bytes@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + integrity sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how== dependencies: is-number "^3.0.0" kind-of "^4.0.0" @@ -7190,18 +8320,21 @@ randomatic@^1.1.3: randombytes@^2.0.0, randombytes@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.5.tgz#dc009a246b8d09a177b4b7a0ae77bc570f4b1b79" + integrity sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg== dependencies: safe-buffer "^5.1.0" randombytes@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== dependencies: safe-buffer "^5.1.0" randomfill@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62" + integrity sha512-YL6GrhrWoic0Eq8rXVbMptH7dAxCs0J+mh5Y0euNekPPYaxEmdVGim6GdoxoRzKW2yJoU8tueifS7mYxvcFDEQ== dependencies: randombytes "^2.0.5" safe-buffer "^5.1.0" @@ -7209,10 +8342,12 @@ randomfill@^1.0.3: range-parser@^1.0.3, range-parser@^1.2.0, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= raw-body@2, raw-body@2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" + integrity sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k= dependencies: bytes "3.0.0" http-errors "1.6.2" @@ -7222,10 +8357,12 @@ raw-body@2, raw-body@2.3.2: raw-loader@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + integrity sha1-DD0L6u2KAclm2Xh793goElKpeao= rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: version "1.2.1" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + integrity sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU= dependencies: deep-extend "~0.4.0" ini "~1.3.0" @@ -7235,18 +8372,21 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: read-cache@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= dependencies: pify "^2.3.0" read-only-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0" + integrity sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A= dependencies: readable-stream "^2.0.2" read-pkg-up@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= dependencies: find-up "^1.0.0" read-pkg "^1.0.0" @@ -7254,6 +8394,7 @@ read-pkg-up@^1.0.1: read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= dependencies: find-up "^2.0.0" read-pkg "^2.0.0" @@ -7261,6 +8402,7 @@ read-pkg-up@^2.0.0: read-pkg@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= dependencies: load-json-file "^1.0.0" normalize-package-data "^2.3.2" @@ -7269,6 +8411,7 @@ read-pkg@^1.0.0: read-pkg@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= dependencies: load-json-file "^2.0.0" normalize-package-data "^2.3.2" @@ -7277,6 +8420,7 @@ read-pkg@^2.0.0: read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= dependencies: load-json-file "^4.0.0" normalize-package-data "^2.3.2" @@ -7285,6 +8429,7 @@ read-pkg@^3.0.0: "readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + integrity sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -7297,6 +8442,7 @@ read-pkg@^3.0.0: readable-stream@1.0: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -7306,6 +8452,7 @@ readable-stream@1.0: readable-stream@1.1.x, "readable-stream@1.x >=1.1.9", readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -7315,6 +8462,7 @@ readable-stream@1.1.x, "readable-stream@1.x >=1.1.9", readable-stream@~1.1.9: readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + integrity sha1-j5A0HmilPMySh4jaz80Rs265t44= dependencies: core-util-is "~1.0.0" inherits "~2.0.1" @@ -7326,6 +8474,7 @@ readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6: readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + integrity sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg= dependencies: graceful-fs "^4.1.2" minimatch "^3.0.2" @@ -7335,12 +8484,14 @@ readdirp@^2.0.0: rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" + integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= dependencies: resolve "^1.1.6" redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= dependencies: indent-string "^2.1.0" strip-indent "^1.0.1" @@ -7348,14 +8499,17 @@ redent@^1.0.0: redis-commands@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.3.1.tgz#81d826f45fa9c8b2011f4cd7a0fe597d241d442b" + integrity sha1-gdgm9F+pyLIBH0zXoP5ZfSQdRCs= redis-parser@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-2.6.0.tgz#52ed09dacac108f1a631c07e9b69941e7a19504b" + integrity sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs= redis@^2.7.1: version "2.8.0" resolved "https://registry.yarnpkg.com/redis/-/redis-2.8.0.tgz#202288e3f58c49f6079d97af7a10e1303ae14b02" + integrity sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A== dependencies: double-ended-queue "^2.1.0-0" redis-commands "^1.2.0" @@ -7364,6 +8518,7 @@ redis@^2.7.1: reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY= dependencies: balanced-match "^0.4.2" math-expression-evaluator "^1.2.14" @@ -7372,6 +8527,7 @@ reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7: reduce-css-calc@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.0.5.tgz#33c97838c5d4c711a5c14ef85ce4fde41483f7bd" + integrity sha1-M8l4OMXUxxGlwU74XOT95BSD970= dependencies: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" @@ -7379,44 +8535,53 @@ reduce-css-calc@^2.0.0: reduce-function-call@^1.0.1, reduce-function-call@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + integrity sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk= dependencies: balanced-match "^0.4.2" reflect-metadata@0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.12.tgz#311bf0c6b63cd782f228a81abe146a2bfa9c56f2" + integrity sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A== reflect-metadata@^0.1.2: version "0.1.10" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a" + integrity sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo= regenerate@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" + integrity sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg== regenerator-runtime@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" + integrity sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A== regex-cache@^0.4.2: version "0.4.4" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== dependencies: is-equal-shallow "^0.1.3" regex-not@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9" + integrity sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k= dependencies: extend-shallow "^2.0.1" regex-parser@^2.2.1: version "2.2.8" resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.8.tgz#da4c0cda5a828559094168930f455f532b6ffbac" + integrity sha1-2kwM2lqChVkJQWiTD0VfUytv+6w= regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + integrity sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs= dependencies: regenerate "^1.2.1" regjsgen "^0.2.0" @@ -7425,6 +8590,7 @@ regexpu-core@^1.0.0: registry-auth-token@^3.0.1: version "3.3.1" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.1.tgz#fb0d3289ee0d9ada2cbb52af5dfe66cb070d3006" + integrity sha1-+w0yie4Nmtosu1KvXf5mywcNMAY= dependencies: rc "^1.1.6" safe-buffer "^5.0.1" @@ -7432,26 +8598,31 @@ registry-auth-token@^3.0.1: registry-url@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + integrity sha1-PU74cPc93h138M+aOBQyRE4XSUI= dependencies: rc "^1.0.1" regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= regjsparser@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= dependencies: jsesc "~0.5.0" relateurl@0.2.x: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= remap-istanbul@^0.10: version "0.10.1" resolved "https://registry.yarnpkg.com/remap-istanbul/-/remap-istanbul-0.10.1.tgz#3aa58dd5021d499f336d3ba5bf3bbb91c1b88e37" + integrity sha512-gsNQXs5kJLhErICSyYhzVZ++C8LBW8dgwr874Y2QvzAUS75zBlD/juZgXs39nbYJ09fZDlX2AVLVJAY2jbFJoQ== dependencies: amdefine "^1.0.0" istanbul "0.4.5" @@ -7463,6 +8634,7 @@ remap-istanbul@^0.10: remap-istanbul@^0.9.0: version "0.9.5" resolved "https://registry.yarnpkg.com/remap-istanbul/-/remap-istanbul-0.9.5.tgz#a18617b1f31eec5a7dbee77538298b775606aaa8" + integrity sha1-oYYXsfMe7Fp9vud1OCmLd1YGqqg= dependencies: amdefine "^1.0.0" gulp-util "3.0.7" @@ -7474,10 +8646,12 @@ remap-istanbul@^0.9.0: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= renderkid@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.1.tgz#898cabfc8bede4b7b91135a3ffd323e58c0db319" + integrity sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk= dependencies: css-select "^1.1.0" dom-converter "~0.1" @@ -7488,34 +8662,41 @@ renderkid@^2.0.1: repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= repeat-string@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" + integrity sha1-x6jTI2BoNiBZp+RlH8aITosftK4= repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= dependencies: is-finite "^1.0.0" replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" + integrity sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ= request-progress@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" + integrity sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg= dependencies: throttleit "^1.0.0" request@2, request@^2.0.0, request@^2.74.0, request@^2.78.0, request@^2.79.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + integrity sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw== dependencies: aws-sign2 "~0.7.0" aws4 "^1.6.0" @@ -7543,6 +8724,7 @@ request@2, request@^2.0.0, request@^2.74.0, request@^2.78.0, request@^2.79.0: request@2.75.x: version "2.75.0" resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" + integrity sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM= dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" @@ -7569,6 +8751,7 @@ request@2.75.x: request@2.79.0, request@~2.79.0: version "2.79.0" resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + integrity sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4= dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" @@ -7594,6 +8777,7 @@ request@2.79.0, request@~2.79.0: request@2.81.0, request@~2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + integrity sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA= dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" @@ -7621,6 +8805,7 @@ request@2.81.0, request@~2.81.0: requestretry@^1.2.2: version "1.13.0" resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94" + integrity sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg== dependencies: extend "^3.0.0" lodash "^4.15.0" @@ -7630,32 +8815,39 @@ requestretry@^1.2.2: require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" + integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= requires-port@1.0.x, requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= dependencies: resolve-from "^3.0.0" resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + integrity sha1-six699nWiBvItuZTM17rywoYh0g= resolve-url-loader@2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-2.2.1.tgz#13a1396fb773edf959550e400e688f5ed32548bf" + integrity sha512-ywToZt/yttp4qG/SiiGMLAgaGuSaWSujAaf3WCadXehvQLxIgKFmMOSegaoH9Laa70Ayl4kti0zCAqLR48H/Mw== dependencies: adjust-sourcemap-loader "^1.1.0" camelcase "^4.0.0" @@ -7670,30 +8862,36 @@ resolve-url-loader@2.2.1: resolve-url@^0.2.1, resolve-url@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= resolve@^1.1.3, resolve@^1.1.4, resolve@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + integrity sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw== dependencies: path-parse "^1.0.5" resolve@^1.1.6, resolve@^1.3.2, resolve@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86" + integrity sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q== dependencies: path-parse "^1.0.5" rework-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" + integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= rework@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" + integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= dependencies: convert-source-map "^0.3.3" css "^2.0.0" @@ -7701,26 +8899,31 @@ rework@^1.0.1: rgb-hex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-2.1.0.tgz#c773c5fe2268a25578d92539a82a7a5ce53beda6" + integrity sha1-x3PF/iJoolV42SU5qCp6XOU77aY= rgb@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" + integrity sha1-vieykej+/+rBvZlylyG/pA/AN7U= right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= dependencies: align-text "^0.1.1" rimraf@2, rimraf@2.6.2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + integrity sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w== dependencies: glob "^7.0.5" ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + integrity sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc= dependencies: hash-base "^2.0.0" inherits "^2.0.1" @@ -7728,6 +8931,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: rollup-plugin-commonjs@^8.3.0: version "8.3.0" resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-8.3.0.tgz#91b4ba18f340951e39ed7b1901f377a80ab3f9c3" + integrity sha512-PYs3OiYgENFYEmI3vOEm5nrp3eY90YZqd5vGmQqeXmhJsAWFIrFdROCvOasqJ1HgeTvqyYo9IGXnFDyoboNcgQ== dependencies: acorn "^5.2.1" estree-walker "^0.5.0" @@ -7738,6 +8942,7 @@ rollup-plugin-commonjs@^8.3.0: rollup-plugin-node-globals@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/rollup-plugin-node-globals/-/rollup-plugin-node-globals-1.1.0.tgz#7efd8d611d132737829e804e9f51f50962af451f" + integrity sha1-fv2NYR0TJzeCnoBOn1H1CWKvRR8= dependencies: acorn "^4.0.1" buffer-es6 "^4.9.1" @@ -7749,6 +8954,7 @@ rollup-plugin-node-globals@1.1.0: rollup-plugin-node-resolve@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-3.0.3.tgz#8f57b253edd00e5b0ad0aed7b7e9cf5982e98fa4" + integrity sha512-qJLXJ1aASV6p8SrEfRdQdHmb5OQmqXyIWIdVGcju8QFzftSsHcuL554Vy+n8mr0fZCC+ksO6aWJ7TAVl2F+Qwg== dependencies: builtin-modules "^1.1.0" is-module "^1.0.0" @@ -7757,12 +8963,14 @@ rollup-plugin-node-resolve@^3.0.3: rollup-plugin-uglify@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/rollup-plugin-uglify/-/rollup-plugin-uglify-3.0.0.tgz#a34eca24617709c6bf1778e9653baafa06099b86" + integrity sha512-dehLu9eRRoV4l09aC+ySntRw1OAfoyKdbk8Nelblj03tHoynkSybqyEpgavemi1LBOH6S1vzI58/mpxkZIe1iQ== dependencies: uglify-es "^3.3.7" rollup-pluginutils@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-1.5.2.tgz#1e156e778f94b7255bfa1b3d0178be8f5c552408" + integrity sha1-HhVud4+UtyVb+hs9AXi+j1xVJAg= dependencies: estree-walker "^0.2.1" minimatch "^3.0.2" @@ -7770,6 +8978,7 @@ rollup-pluginutils@^1.5.2: rollup-pluginutils@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz#7ec95b3573f6543a46a6461bd9a7c544525d0fc0" + integrity sha1-fslbNXP2VDpGpkYb2afFRFJdD8A= dependencies: estree-walker "^0.3.0" micromatch "^2.3.11" @@ -7777,26 +8986,31 @@ rollup-pluginutils@^2.0.1: rollup@^0.56.0: version "0.56.0" resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.56.0.tgz#4aefe9cef84a01ac3245e3fbba155e38dc2cc2e8" + integrity sha512-/VSCYZl0Tn4f7e+Jlwzmx9cEMrDcQbHTHowPVnunzhmLW0fisG1LYovuR2sSVGOO5/+Esb1KUzJlyS4n1HcOsA== run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= dependencies: aproba "^1.1.1" rxjs@5.5.6: version "5.5.6" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.6.tgz#e31fb96d6fd2ff1fd84bcea8ae9c02d007179c02" + integrity sha512-v4Q5HDC0FHAQ7zcBX7T2IL6O5ltl1a2GX4ENjPXg6SjDY69Cmx9v4113C99a4wGF16ClPv5Z8mghuYorVkg/kg== dependencies: symbol-observable "1.0.1" safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k= dependencies: glob "^7.0.0" lodash "^4.0.0" @@ -7806,6 +9020,7 @@ sass-graph@^2.2.4: sass-loader@6.0.6: version "6.0.6" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-6.0.6.tgz#e9d5e6c1f155faa32a4b26d7a9b7107c225e40f9" + integrity sha512-c3/Zc+iW+qqDip6kXPYLEgsAu2lf4xz0EZDplB7EmSUMda12U1sGJPetH55B/j9eu0bTtKzKlNPWWyYC7wFNyQ== dependencies: async "^2.1.5" clone-deep "^0.3.0" @@ -7816,26 +9031,31 @@ sass-loader@6.0.6: saucelabs@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.3.0.tgz#d240e8009df7fa87306ec4578a69ba3b5c424fee" + integrity sha1-0kDoAJ33+ocwbsRXimm6O1xCT+4= dependencies: https-proxy-agent "^1.0.0" sax@0.6.x: version "0.6.1" resolved "https://registry.yarnpkg.com/sax/-/sax-0.6.1.tgz#563b19c7c1de892e09bfc4f2fc30e3c27f0952b9" + integrity sha1-VjsZx8HeiS4Jv8Ty/DDjwn8JUrk= sax@>=0.6.0, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== schema-utils@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" + integrity sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8= dependencies: ajv "^5.0.0" schema-utils@^0.4.0: version "0.4.5" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + integrity sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA== dependencies: ajv "^6.1.0" ajv-keywords "^3.1.0" @@ -7843,12 +9063,14 @@ schema-utils@^0.4.0: script-ext-html-webpack-plugin@1.8.8: version "1.8.8" resolved "https://registry.yarnpkg.com/script-ext-html-webpack-plugin/-/script-ext-html-webpack-plugin-1.8.8.tgz#faa888a286ce746fcd06a5e0a9e39ed7b9d24f66" + integrity sha512-9mxSrvfX8on97tu4pUfLXQ9StKGxfHKSy3NXsYBi+4EpyhI4oUUhE3KEWUViDiTQHmY7u2ztLT5OfOjQRzmJaQ== dependencies: debug "^3.1.0" scss-tokenizer@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= dependencies: js-base64 "^2.1.8" source-map "^0.4.2" @@ -7856,10 +9078,12 @@ scss-tokenizer@^0.2.3: select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= selenium-webdriver@3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz#2ba87a1662c020b8988c981ae62cb2a01298eafc" + integrity sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q== dependencies: jszip "^3.1.3" rimraf "^2.5.4" @@ -7869,6 +9093,7 @@ selenium-webdriver@3.6.0: selenium-webdriver@^2.53.2: version "2.53.3" resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-2.53.3.tgz#d29ff5a957dff1a1b49dc457756e4e4bfbdce085" + integrity sha1-0p/1qVff8aG0ncRXdW5OS/vc4IU= dependencies: adm-zip "0.4.4" rimraf "^2.2.8" @@ -7879,40 +9104,48 @@ selenium-webdriver@^2.53.2: selfsigned@^1.9.1: version "1.10.1" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.1.tgz#bf8cb7b83256c4551e31347c6311778db99eec52" + integrity sha1-v4y3uDJWxFUeMTR8YxF3jbme7FI= dependencies: node-forge "0.6.33" semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + integrity sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY= dependencies: semver "^5.0.3" semver-dsl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0" + integrity sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA= dependencies: semver "^5.3.0" "semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== semver@^5.4.1: version "5.5.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== semver@~5.0.1: version "5.0.3" resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a" + integrity sha1-d0Zt5YnNXTyV8TiqeLxWmjy10no= semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= send@0.16.1: version "0.16.1" resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" + integrity sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A== dependencies: debug "2.6.9" depd "~1.1.1" @@ -7931,10 +9164,12 @@ send@0.16.1: serialize-javascript@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005" + integrity sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU= serve-index@^1.7.2: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= dependencies: accepts "~1.3.4" batch "0.6.1" @@ -7947,6 +9182,7 @@ serve-index@^1.7.2: serve-static@1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" + integrity sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ== dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" @@ -7956,20 +9192,24 @@ serve-static@1.13.1: set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= set-getter@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" + integrity sha1-12nBgsnVpR9AkUXy+6guXoboA3Y= dependencies: to-object-path "^0.3.0" set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= set-value@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + integrity sha1-fbCPnT0i3H945Trzw79GZuzfzPE= dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -7979,6 +9219,7 @@ set-value@^0.4.3: set-value@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + integrity sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg== dependencies: extend-shallow "^2.0.1" is-extendable "^0.1.1" @@ -7988,18 +9229,22 @@ set-value@^2.0.0: setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= setprototypeof@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" + integrity sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ= setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== sha.js@^2.4.0, sha.js@^2.4.8: version "2.4.9" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d" + integrity sha512-G8zektVqbiPHrylgew9Zg1VRB1L/DtXNUVAM6q4QLy8NE3qtHlFXTf8VLL4k1Yl6c7NMjtZUTdXV+X44nFaT6A== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -8007,6 +9252,7 @@ sha.js@^2.4.0, sha.js@^2.4.8: sha.js@~2.4.4: version "2.4.10" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" + integrity sha512-vnwmrFDlOExK4Nm16J2KMWHLrp14lBrjxMxBJpu++EnsuBmpiYaM/MEs46Vxxm/4FvdP5yTwuCTO9it5FSjrqA== dependencies: inherits "^2.0.1" safe-buffer "^5.0.1" @@ -8014,6 +9260,7 @@ sha.js@~2.4.4: shallow-clone@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= dependencies: is-extendable "^0.1.1" kind-of "^2.0.1" @@ -8023,6 +9270,7 @@ shallow-clone@^0.1.2: shasum@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + integrity sha1-5wEjENj0F/TetXEhUOVni4euVl8= dependencies: json-stable-stringify "~0.0.0" sha.js "~2.4.4" @@ -8030,16 +9278,19 @@ shasum@^1.0.0: shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= dependencies: shebang-regex "^1.0.0" shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= shell-quote@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= dependencies: array-filter "~0.0.0" array-map "~0.0.0" @@ -8049,6 +9300,7 @@ shell-quote@^1.6.1: shelljs@^0.7.0: version "0.7.8" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" + integrity sha1-3svPh0sNHl+3LhSxZKloMEjprLM= dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -8057,30 +9309,36 @@ shelljs@^0.7.0: signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= dependencies: is-arrayish "^0.3.1" slack-node@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/slack-node/-/slack-node-0.2.0.tgz#de4b8dddaa8b793f61dbd2938104fdabf37dfa30" + integrity sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA= dependencies: requestretry "^1.2.2" slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= smart-buffer@^1.0.13, smart-buffer@^1.0.4: version "1.1.15" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + integrity sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY= smtp-connection@2.12.0: version "2.12.0" resolved "https://registry.yarnpkg.com/smtp-connection/-/smtp-connection-2.12.0.tgz#d76ef9127cb23c2259edb1e8349c2e8d5e2d74c1" + integrity sha1-1275EnyyPCJZ7bHoNJwujV4tdME= dependencies: httpntlm "1.6.1" nodemailer-shared "1.1.0" @@ -8088,6 +9346,7 @@ smtp-connection@2.12.0: snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== dependencies: define-property "^1.0.0" isobject "^3.0.0" @@ -8096,12 +9355,14 @@ snapdragon-node@^2.0.1: snapdragon-util@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== dependencies: kind-of "^3.2.0" snapdragon@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" + integrity sha1-4StUh/re0+PeoKyR6UAL91tAE3A= dependencies: base "^0.11.1" debug "^2.2.0" @@ -8115,22 +9376,26 @@ snapdragon@^0.8.1: sntp@1.x.x: version "1.0.9" resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + integrity sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg= dependencies: hoek "2.x.x" sntp@2.x.x: version "2.0.2" resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.0.2.tgz#5064110f0af85f7cfdb7d6b67a40028ce52b4b2b" + integrity sha1-UGQRDwr4X3z9t9a2ekACjOUrSys= dependencies: hoek "4.x.x" socket.io-adapter@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz#2a805e8a14d6372124dd9159ad4502f8cb07f06b" + integrity sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs= socket.io-client@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.0.4.tgz#0918a552406dc5e540b380dcd97afc4a64332f8e" + integrity sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44= dependencies: backo2 "1.0.2" base64-arraybuffer "0.1.5" @@ -8149,6 +9414,7 @@ socket.io-client@2.0.4: socket.io-parser@~3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.1.2.tgz#dbc2282151fc4faebbe40aeedc0772eba619f7f2" + integrity sha1-28IoIVH8T6675Aru3Ady66YZ9/I= dependencies: component-emitter "1.2.1" debug "~2.6.4" @@ -8158,6 +9424,7 @@ socket.io-parser@~3.1.1: socket.io@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.0.4.tgz#c1a4590ceff87ecf13c72652f046f716b29e6014" + integrity sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ= dependencies: debug "~2.6.6" engine.io "~3.1.0" @@ -8168,6 +9435,7 @@ socket.io@2.0.4: sockjs-client@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" + integrity sha1-W6vjhrd15M8U51IJEUUmVAFsixI= dependencies: debug "^2.6.6" eventsource "0.1.6" @@ -8179,6 +9447,7 @@ sockjs-client@1.1.4: sockjs@0.3.19: version "0.3.19" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== dependencies: faye-websocket "^0.10.0" uuid "^3.0.1" @@ -8186,6 +9455,7 @@ sockjs@0.3.19: socks-proxy-agent@2: version "2.1.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-2.1.1.tgz#86ebb07193258637870e13b7bd99f26c663df3d3" + integrity sha512-sFtmYqdUK5dAMh85H0LEVFUCO7OhJJe1/z2x/Z6mxp3s7/QPf1RkZmpZy+BpuU0bEjcV9npqKjq9Y3kwFUjnxw== dependencies: agent-base "2" extend "3" @@ -8194,6 +9464,7 @@ socks-proxy-agent@2: socks@1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691" + integrity sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE= dependencies: ip "^1.1.2" smart-buffer "^1.0.4" @@ -8201,6 +9472,7 @@ socks@1.1.9: socks@~1.1.5: version "1.1.10" resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" + integrity sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o= dependencies: ip "^1.1.4" smart-buffer "^1.0.13" @@ -8208,20 +9480,24 @@ socks@~1.1.5: sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= dependencies: is-plain-obj "^1.0.0" sortablejs@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.7.0.tgz#80a2b2370abd568e1cec8c271131ef30a904fa28" + integrity sha1-gKKyNwq9Vo4c7IwnETHvMKkE+ig= source-list-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + integrity sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A== source-map-loader@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-0.2.3.tgz#d4b0c8cd47d54edce3e6bfa0f523f452b5b0e521" + integrity sha512-MYbFX9DYxmTQFfy2v8FC1XZwpwHKYxg3SK8Wb7VPBKuhDjz8gi9re2819MsG4p49HDyiOSUKlmZ+nQBArW5CGw== dependencies: async "^2.5.0" loader-utils "~0.2.2" @@ -8230,6 +9506,7 @@ source-map-loader@0.2.3: source-map-resolve@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.3.1.tgz#610f6122a445b8dd51535a2a71b783dfc1248761" + integrity sha1-YQ9hIqRFuN1RU1oqcbeD38Ekh2E= dependencies: atob "~1.1.0" resolve-url "~0.2.1" @@ -8239,6 +9516,7 @@ source-map-resolve@^0.3.0: source-map-resolve@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.0.tgz#fcad0b64b70afb27699e425950cb5ebcd410bc20" + integrity sha1-/K0LZLcK+ydpnkJZUMtevNQQvCA= dependencies: atob "^2.0.0" resolve-url "^0.2.1" @@ -8248,74 +9526,89 @@ source-map-resolve@^0.5.0: source-map-support@^0.4.15, source-map-support@^0.4.2, source-map-support@~0.4.0: version "0.4.18" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== dependencies: source-map "^0.5.6" source-map-support@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.2.tgz#1a6297fd5b2e762b39688c7fc91233b60984f0a5" + integrity sha512-9zHceZbQwERaMK1MiFguvx1dL9GQPLXInr2D/wUxAsuV6ZKc9F0DHYWeloMcalkYRbtanwqUakoDjvj55cL/4A== dependencies: source-map "^0.6.0" source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= source-map-url@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" + integrity sha1-fsrxO1e80J2opAxdJp2zN5nUqvk= source-map@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86" + integrity sha1-D+llA6yGpa213mP05BKuSHLNvoY= source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3, source-map@~0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= source-map@0.6.1, source-map@>=0.5.6, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== source-map@^0.1.38: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= dependencies: amdefine ">=0.0.4" source-map@^0.4.2, source-map@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + integrity sha1-66T12pwNyZneaAMti092FzZSA2s= dependencies: amdefine ">=0.0.4" source-map@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + integrity sha1-2rc/vPwrqBm03gO9b26qSBZLP50= dependencies: amdefine ">=0.0.4" sparkles@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3" + integrity sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM= spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + integrity sha1-SzBz2TP/UfORLwOsVRlJikFQ20A= dependencies: spdx-license-ids "^1.0.2" spdx-expression-parse@~1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + integrity sha1-m98vIOH0DtRH++JzJmGR/O1RYmw= spdx-license-ids@^1.0.2: version "1.2.2" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc= spdy-transport@^2.0.18: version "2.0.20" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d" + integrity sha1-c15yBUxIayNU/onnAiVgBKOazk0= dependencies: debug "^2.6.8" detect-node "^2.0.3" @@ -8328,6 +9621,7 @@ spdy-transport@^2.0.18: spdy@^3.4.1: version "3.4.7" resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" + integrity sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw= dependencies: debug "^2.6.8" handle-thing "^1.2.5" @@ -8339,34 +9633,40 @@ spdy@^3.4.1: split-string@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/split-string/-/split-string-2.1.1.tgz#af4b06d821560426446c3cd931cda618940d37d0" + integrity sha1-r0sG2CFWBCZEbDzZMc2mGJQNN9A= dependencies: extend-shallow "^2.0.1" split-string@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.0.2.tgz#6129bc92731716e5aa1fb73c333078f0b7c114c8" + integrity sha512-d6myUSfwmBz1izkY4r7r7I0PL41rh21qUDYK1OgclmGHeoqQoujduGxMbzw6BlF3HKmJR4sMpbWVo7/Xzg4YBQ== dependencies: extend-shallow "^2.0.1" split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== dependencies: extend-shallow "^3.0.0" split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= dependencies: through "2" sprintf-js@^1.0.3, sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= sshpk@^1.7.0: version "1.13.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + integrity sha1-US322mKHFEMW3EwY/hzx2UBzm+M= dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -8381,12 +9681,14 @@ sshpk@^1.7.0: ssri@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.1.0.tgz#2cbf1df36b74d0fc91fcf89640a4b3e1d10b1899" + integrity sha512-TevC8fgxQKTfQ1nWtM9GNzr3q5rrHNntG9CDMH1k3QhSZI6Kb+NbjLRs8oPFZa2Hgo7zoekL+UTvoEk7tsbjQg== dependencies: safe-buffer "^5.1.0" static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= dependencies: define-property "^0.2.5" object-copy "^0.1.0" @@ -8394,16 +9696,19 @@ static-extend@^0.1.1: "statuses@>= 1.3.1 < 2", statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" + integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4= stdout-stream@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + integrity sha1-osfIWH5U2UJ+qe2zrD8s1SLfN4s= dependencies: readable-stream "^2.0.1" stream-browserify@^2.0.0, stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + integrity sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds= dependencies: inherits "~2.0.1" readable-stream "^2.0.2" @@ -8411,6 +9716,7 @@ stream-browserify@^2.0.0, stream-browserify@^2.0.1: stream-combiner2@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + integrity sha1-+02KFCDqNidk4hrUeAOXvry0HL4= dependencies: duplexer2 "~0.1.0" readable-stream "^2.0.2" @@ -8418,12 +9724,14 @@ stream-combiner2@^1.1.1: stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= dependencies: duplexer "~0.1.1" stream-each@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" + integrity sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA== dependencies: end-of-stream "^1.1.0" stream-shift "^1.0.0" @@ -8431,6 +9739,7 @@ stream-each@^1.1.0: stream-http@^2.0.0: version "2.8.0" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" + integrity sha512-sZOFxI/5xw058XIRHl4dU3dZ+TTOIGJR78Dvo0oEAejIt4ou27k+3ne1zYmCV+v7UucbxIFQuOgnkTVHh8YPnw== dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -8441,6 +9750,7 @@ stream-http@^2.0.0: stream-http@^2.3.1: version "2.7.2" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad" + integrity sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw== dependencies: builtin-status-codes "^3.0.0" inherits "^2.0.1" @@ -8451,10 +9761,12 @@ stream-http@^2.3.1: stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= stream-splicer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" + integrity sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM= dependencies: inherits "^2.0.1" readable-stream "^2.0.2" @@ -8462,6 +9774,7 @@ stream-splicer@^2.0.0: streamroller@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b" + integrity sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ== dependencies: date-format "^1.2.0" debug "^3.1.0" @@ -8471,10 +9784,12 @@ streamroller@^0.7.0: strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= string-replace-loader@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-1.3.0.tgz#1d404a7bf5e2ec21b08ffc76d89445fbe49bc01d" + integrity sha512-zj8J2ELc5HWOYFkS3MaRMgaHu+mPTG/EfHHaFesJqXjpaKOAruaONEWt3/Em5Urc6n8qDlvabIN6umiU8lH4QA== dependencies: loader-utils "^1.1.0" lodash "^4" @@ -8482,6 +9797,7 @@ string-replace-loader@1.3.0: string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= dependencies: code-point-at "^1.0.0" is-fullwidth-code-point "^1.0.0" @@ -8490,6 +9806,7 @@ string-width@^1.0.1, string-width@^1.0.2: string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== dependencies: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" @@ -8497,6 +9814,7 @@ string-width@^2.0.0, string-width@^2.1.1: string.prototype.padend@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" + integrity sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA= dependencies: define-properties "^1.1.2" es-abstract "^1.4.3" @@ -8505,96 +9823,114 @@ string.prototype.padend@^3.0.0: string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= string_decoder@~1.0.0, string_decoder@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + integrity sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ== dependencies: safe-buffer "~5.1.0" stringstream@~0.0.4, stringstream@~0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + integrity sha1-TkhM1N5aC7vuGORjB3EKioFiGHg= strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= dependencies: ansi-regex "^2.0.0" strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= dependencies: ansi-regex "^3.0.0" strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= dependencies: is-utf8 "^0.2.0" strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= dependencies: get-stdin "^4.0.1" strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + integrity sha1-9izxdYHplrSPyWVpn1TAauJouNI= dependencies: minimist "^1.1.0" sugarss@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-1.0.1.tgz#be826d9003e0f247735f92365dc3fd7f1bae9e44" + integrity sha512-3qgLZytikQQEVn1/FrhY7B68gPUUGY3R1Q1vTiD5xT+Ti1DP/8iZuwFet9ONs5+bmL8pZoDQ6JrQHVgrNlK6mA== dependencies: postcss "^6.0.14" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= supports-color@^3.1.0, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY= dependencies: has-flag "^1.0.0" supports-color@^4.0.0, supports-color@^4.2.1, supports-color@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.4.0.tgz#883f7ddabc165142b2a61427f3352ded195d1a3e" + integrity sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ== dependencies: has-flag "^2.0.0" supports-color@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" + integrity sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ== dependencies: has-flag "^2.0.0" supports-color@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a" + integrity sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q== dependencies: has-flag "^3.0.0" svgo@^0.7.0: version "0.7.2" resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + integrity sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U= dependencies: coa "~1.0.1" colors "~1.1.2" @@ -8607,20 +9943,24 @@ svgo@^0.7.0: symbol-observable@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= syntax-error@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1" + integrity sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE= dependencies: acorn "^4.0.3" tapable@^0.2.5, tapable@^0.2.7: version "0.2.8" resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" + integrity sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI= tar-pack@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + integrity sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ= dependencies: debug "^2.2.0" fstream "^1.0.10" @@ -8634,6 +9974,7 @@ tar-pack@^3.4.0: tar-stream@^1.5.0: version "1.5.4" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016" + integrity sha1-NlSc8E7RrumyowwBQyUiONr5QBY= dependencies: bl "^1.0.0" end-of-stream "^1.0.0" @@ -8643,6 +9984,7 @@ tar-stream@^1.5.0: tar@^2.0.0, tar@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE= dependencies: block-stream "*" fstream "^1.0.2" @@ -8651,24 +9993,29 @@ tar@^2.0.0, tar@^2.2.1: term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + integrity sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk= dependencies: execa "^0.7.0" text-mask-core@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/text-mask-core/-/text-mask-core-5.0.1.tgz#86db742bdfe3b4c383bb51a3b4ca342c86110639" + integrity sha1-htt0K9/jtMODu1GjtMo0LIYRBjk= text-mask-core@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/text-mask-core/-/text-mask-core-5.1.1.tgz#a7f65634e11236818fd36a92668e17bf9368f357" + integrity sha512-tcHJSs0jlHpIed2flCuZZhUIGN1KfDsN/qCW7lkYxyXkKt4OLd3GXCHPFsY9rOLhmyC43ZRTM+tCxizBzluWpw== throttleit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= through2@2.0.1, through2@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" + integrity sha1-OE51MU1J8y3hLuu4E2uOtrXVnak= dependencies: readable-stream "~2.0.0" xtend "~4.0.0" @@ -8676,86 +10023,104 @@ through2@2.0.1, through2@^2.0.0: through@2, "through@>=2.2.7 <3", through@X.X.X, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= thunkify@~2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d" + integrity sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0= thunky@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/thunky/-/thunky-0.1.0.tgz#bf30146824e2b6e67b0f2d7a4ac8beb26908684e" + integrity sha1-vzAUaCTituZ7Dy16Ssi+smkIaE4= time-stamp@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" + integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= time-stamp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" + integrity sha1-lcakRTDhW6jW9KPsuMOj+sRto1c= timed-out@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= timers-browserify@^1.0.1: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + integrity sha1-ycWLV1voQHN1y14kYtrO50NZ9B0= dependencies: process "~0.11.0" timers-browserify@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6" + integrity sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg== dependencies: setimmediate "^1.0.4" timespan@2.3.x: version "2.3.0" resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" + integrity sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk= tmp@0.0.24: version "0.0.24" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12" + integrity sha1-1qXhmNFKmDXMby18PZ4wJCjIzxI= tmp@0.0.30: version "0.0.30" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" + integrity sha1-ckGdSovn1s51FI/YsyTlk6cRwu0= dependencies: os-tmpdir "~1.0.1" tmp@0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" tmp@0.0.x: version "0.0.31" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= dependencies: os-tmpdir "~1.0.1" to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= to-object-path@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= dependencies: kind-of "^3.0.2" to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= dependencies: is-number "^3.0.0" repeat-string "^1.6.1" @@ -8763,6 +10128,7 @@ to-regex-range@^2.1.0: to-regex@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" + integrity sha1-FTWL7kosg712N3uh3ASdDxiDeq4= dependencies: define-property "^0.2.5" extend-shallow "^2.0.1" @@ -8771,58 +10137,70 @@ to-regex@^3.0.1: to-string-loader@1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/to-string-loader/-/to-string-loader-1.1.5.tgz#7b7aa17891b7bb4947a7a11bfb03b5fde9c6e695" + integrity sha1-e3qheJG3u0lHp6Eb+wO1/enG5pU= dependencies: loader-utils "^0.2.16" toposort@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.4.tgz#a86107690cbee8cae43b349d2f60162500924dfc" + integrity sha1-qGEHaQy+6MrkOzSdL2AWJQCSTfw= touch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b" + integrity sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA== dependencies: nopt "~1.0.10" tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + integrity sha1-C2GKVWW23qkL80JdBNVe3EdadWE= dependencies: punycode "^1.4.1" tree-kill@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.0.tgz#5846786237b4239014f05db156b643212d4c6f36" + integrity sha512-DlX6dR0lOIRDFxI0mjL9IYg6OTncLm/Zt+JiBhE5OlFcAR8yc9S7FFXU9so0oda47frdM/JFsk7UjNt9vscKcg== trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= "true-case-path@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + integrity sha1-fskRMJJHZsf1c74wIMNPj9/QDWI= dependencies: glob "^6.0.4" tryer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7" + integrity sha1-Antp+oIyJeVRys4+8DsR9qs3wdc= ts-helpers@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/ts-helpers/-/ts-helpers-1.1.2.tgz#fc69be9f1f3baed01fb1a0ef8d4cfe748814d835" + integrity sha1-/Gm+nx87rtAfsaDvjUz+dIgU2DU= ts-md5@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/ts-md5/-/ts-md5-1.2.4.tgz#7030d7ba9134449deedf6f609d4b4509b94a5712" + integrity sha512-oW+rNjc9CAhalPFzbPWsLqPLzdNcJ8iSm+OXO+Uv+99r3PzCJuM5sVc0bO1eS+4LD2xv+nfU7ylBdwoemUV9Yw== ts-node@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-4.1.0.tgz#36d9529c7b90bb993306c408cd07f7743de20712" + integrity sha512-xcZH12oVg9PShKhy3UHyDmuDLV3y7iKwX25aMVPt1SIXSuAfWkFiGPEkg+th8R4YKW/QCxDoW7lJdb15lx6QWg== dependencies: arrify "^1.0.0" chalk "^2.3.0" @@ -8838,6 +10216,7 @@ ts-node@4.1.0: tsconfig@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7" + integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw== dependencies: "@types/strip-bom" "^3.0.0" "@types/strip-json-comments" "0.0.30" @@ -8847,6 +10226,7 @@ tsconfig@^7.0.0: tsickle@^0.26.0: version "0.26.0" resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.26.0.tgz#40b30a2dd6abcb33b182e37596674bd1cfe4039c" + integrity sha512-eWJ2CUfttGK0LqF9iJ/Avnxbj4M+fCyJ50Zag3wm73Fut1hsasPRHKxKdrMWVj4BMHnQNx7TO+DdNmLmJTSuNw== dependencies: minimist "^1.2.0" mkdirp "^0.5.1" @@ -8856,14 +10236,17 @@ tsickle@^0.26.0: tslib@^1.7.1: version "1.7.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.7.1.tgz#bc8004164691923a79fe8378bbeb3da2017538ec" + integrity sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw= tslib@^1.8.0, tslib@^1.8.1: version "1.9.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" + integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== tslint@5.9.1: version "5.9.1" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.9.1.tgz#1255f87a3ff57eb0b0e1f0e610a8b4748046c9ae" + integrity sha1-ElX4ej/1frCw4fDmEKi0dIBGya4= dependencies: babel-code-frame "^6.22.0" builtin-modules "^1.1.1" @@ -8881,40 +10264,48 @@ tslint@5.9.1: tsscmp@~1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97" + integrity sha1-fcSjOvcVgatDN9qR2FylQn69mpc= tsutils@^2.12.1: version "2.19.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.19.1.tgz#76d7ebdea9d7a7bf4a05f50ead3701b0168708d7" + integrity sha512-1B3z4H4HddgzWptqLzwrJloDEsyBt8DvZhnFO14k7A4RsQL/UhEfQjD4hpcY5NpF3veBkjJhQJ8Bl7Xp96cN+A== dependencies: tslib "^1.8.1" tty-browserify@0.0.0, tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= dependencies: safe-buffer "^5.0.1" tunnel-agent@~0.4.1: version "0.4.3" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us= tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" type-is@~1.6.15: version "1.6.15" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410" + integrity sha1-yrEPtJCeRByChC6v4a1kbIGARBA= dependencies: media-typer "0.3.0" mime-types "~2.1.15" @@ -8922,14 +10313,17 @@ type-is@~1.6.15: typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= typedoc-default-themes@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz#6dc2433e78ed8bea8e887a3acde2f31785bd6227" + integrity sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic= typedoc@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.9.0.tgz#159bff7c7784ce5b91d86f3e4cc8928e62040957" + integrity sha512-numP0CtcUK4I1Vssw6E1N/FjyJWpWqhLT4Zb7Gw3i7ca3ElnYh6z41Y/tcUhMsMYn6L8b67E/Fu4XYYKkNaLbA== dependencies: "@types/fs-extra" "4.0.0" "@types/handlebars" "4.0.31" @@ -8952,18 +10346,22 @@ typedoc@^0.9.0: typescript@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc" + integrity sha1-w8yxbdqgsjFN4DHn5v7onlujRrw= typescript@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" + integrity sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q= typescript@^2.5.0: version "2.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.5.3.tgz#df3dcdc38f3beb800d4bc322646b04a3f6ca7f0d" + integrity sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w== uglify-es@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.8.tgz#f2c68e6cff0d0f9dc9577e4da207151c2e753b7e" + integrity sha512-j8li0jWcAN6yBuAVYFZEFyYINZAm4WEdMwkA6qXFi4TLrze3Mp0Le7QjW6LR9HQjQJ2zRa9VgnFLs3PatijWOw== dependencies: commander "~2.13.0" source-map "~0.6.1" @@ -8971,6 +10369,7 @@ uglify-es@^3.3.7: uglify-js@3.1.x: version "3.1.2" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.2.tgz#b50bcf15a5fd9e9ed40afbcdef3b59d6891b291f" + integrity sha512-kKJ8zg7Ivw3DG9Ytgp4+iiSHq3HaHjEQMvyT2x2Bs8kSUwVemj6bPGFp6YWL81f5NAIOLVUKPxBSvqLRGXMpdw== dependencies: commander "~2.11.0" source-map "~0.5.1" @@ -8978,6 +10377,7 @@ uglify-js@3.1.x: uglify-js@^2.6, uglify-js@^2.8.29: version "2.8.29" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + integrity sha1-KcVzMUgFe7Th913zW3qcty5qWd0= dependencies: source-map "~0.5.1" yargs "~3.10.0" @@ -8987,10 +10387,12 @@ uglify-js@^2.6, uglify-js@^2.8.29: uglify-to-browserify@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= uglifyjs-webpack-plugin@^0.4.6: version "0.4.6" resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" + integrity sha1-uVH0q7a9YX5m9j64kUmOORdj4wk= dependencies: source-map "^0.5.6" uglify-js "^2.8.29" @@ -8999,34 +10401,41 @@ uglifyjs-webpack-plugin@^0.4.6: uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= uid-safe@~2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== dependencies: random-bytes "~1.0.0" ultron@1.0.x: version "1.0.2" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" + integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= ultron@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864" + integrity sha1-sHoualQagV/Go0zNRTO67DB8qGQ= umd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" + integrity sha1-iuVW4RAR9jwllnCKiDclnwGz1g4= undefsafe@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.1.tgz#03b2f2a16c94556e14b2edef326cd66aaf82707a" + integrity sha1-A7LyoWyUVW4Usu3vMmzWaq+CcHo= dependencies: debug "^2.2.0" underscore.string@3.3.4: version "3.3.4" resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.4.tgz#2c2a3f9f83e64762fdc45e6ceac65142864213db" + integrity sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s= dependencies: sprintf-js "^1.0.3" util-deprecate "^1.0.2" @@ -9034,10 +10443,12 @@ underscore.string@3.3.4: underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + integrity sha1-a7rwh3UA02vjTsqlhODbn+8DUgk= union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + integrity sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ= dependencies: arr-union "^3.1.0" get-value "^2.0.6" @@ -9047,44 +10458,52 @@ union-value@^1.0.0: union@~0.4.3: version "0.4.6" resolved "https://registry.yarnpkg.com/union/-/union-0.4.6.tgz#198fbdaeba254e788b0efcb630bc11f24a2959e0" + integrity sha1-GY+9rrolTniLDvy2MLwR8kopWeA= dependencies: qs "~2.3.3" uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= uniqid@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" + integrity sha1-iSIN32t1GuUrX3JISGNShZa7hME= dependencies: macaddress "^0.2.8" uniqs@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= unique-filename@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + integrity sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM= dependencies: unique-slug "^2.0.0" unique-slug@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + integrity sha1-22Z258fMBimHj/GWCXx4hVrp9Ks= dependencies: imurmurhash "^0.1.4" unique-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + integrity sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo= dependencies: crypto-random-string "^1.0.0" units-css@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07" + integrity sha1-1iKGU6UZg9fBb/KPi53Dsf/tOgc= dependencies: isnumeric "^0.2.0" viewport-dimensions "^0.2.0" @@ -9092,14 +10511,17 @@ units-css@^0.4.0: universalify@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + integrity sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc= unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= dependencies: has-value "^0.3.1" isobject "^3.0.0" @@ -9107,10 +10529,12 @@ unset-value@^1.0.0: unzip-response@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= upath@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.2.tgz#80aaae5395abc5fd402933ae2f58694f0860204c" + integrity sha512-fCmij7T5LnwUme3dbnVSejvOHHlARjB3ikJFwgZfz386pHmf/gueuTLRFU94FZEaeCLlbQrweiUU700gG41tUw== dependencies: lodash.endswith "^4.2.1" lodash.isfunction "^3.0.8" @@ -9120,6 +10544,7 @@ upath@^1.0.0: update-notifier@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451" + integrity sha1-TognpruRUUCrCTVZ1wFOPruDdFE= dependencies: boxen "^1.2.1" chalk "^2.0.1" @@ -9134,24 +10559,29 @@ update-notifier@^2.3.0: upper-case@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= urix@^0.1.0, urix@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= url-join@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/url-join/-/url-join-2.0.2.tgz#c072756967ad24b8b59e5741551caac78f50b8b7" + integrity sha1-wHJ1aWetJLi1nldBVRyqx49QuLc= url-parse-lax@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= dependencies: prepend-http "^1.0.1" url-parse@1.0.x: version "1.0.5" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" + integrity sha1-CFSGBCKv3P7+tsllxmLUgAFpkns= dependencies: querystringify "0.0.x" requires-port "1.0.x" @@ -9159,6 +10589,7 @@ url-parse@1.0.x: url-parse@^1.1.8: version "1.1.9" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19" + integrity sha1-xn8dd11R8KGJEd17P/rSe7nlvRk= dependencies: querystringify "~1.0.0" requires-port "1.0.x" @@ -9166,6 +10597,7 @@ url-parse@^1.1.8: url@^0.11.0, url@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= dependencies: punycode "1.3.2" querystring "0.2.0" @@ -9173,6 +10605,7 @@ url@^0.11.0, url@~0.11.0: use@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" + integrity sha1-riig1y+TvyJCKhii43mZMRLeyOg= dependencies: define-property "^0.2.5" isobject "^3.0.0" @@ -9181,6 +10614,7 @@ use@^2.0.0: useragent@^2.1.12: version "2.2.1" resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.2.1.tgz#cf593ef4f2d175875e8bb658ea92e18a4fd06d8e" + integrity sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4= dependencies: lru-cache "2.2.x" tmp "0.0.x" @@ -9188,50 +10622,61 @@ useragent@^2.1.12: util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= util@0.10.3, util@^0.10.3, util@~0.10.1: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= dependencies: inherits "2.0.1" utila@~0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/utila/-/utila-0.3.3.tgz#d7e8e7d7e309107092b05f8d9688824d633a4226" + integrity sha1-1+jn1+MJEHCSsF+NloiCTWM6QiY= utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= uuid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + integrity sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho= uuid@^3.0.0, uuid@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + integrity sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g== uuid@^3.0.1, uuid@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== uws@~0.14.4: version "0.14.5" resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc" + integrity sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw= v8flags@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.1.tgz#dce8fc379c17d9f2c9e9ed78d89ce00052b1b76b" + integrity sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s= dependencies: homedir-polyfill "^1.0.1" validate-npm-package-license@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + integrity sha1-KAS6vnEq0zeUWaz74kdGqywwP7w= dependencies: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" @@ -9239,18 +10684,22 @@ validate-npm-package-license@^3.0.1: vargs@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/vargs/-/vargs-0.1.0.tgz#6b6184da6520cc3204ce1b407cac26d92609ebff" + integrity sha1-a2GE2mUgzDIEzhtAfKwm2SYJ6/8= vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= vendors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" + integrity sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI= verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= dependencies: assert-plus "^1.0.0" core-util-is "1.0.2" @@ -9259,10 +10708,12 @@ verror@1.10.0: viewport-dimensions@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c" + integrity sha1-3nQHR9tTh/0XJfUXXpG6x2r982w= vinyl@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" + integrity sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4= dependencies: clone "^1.0.0" clone-stats "^0.0.1" @@ -9271,24 +10722,29 @@ vinyl@^0.5.0: vlq@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.2.tgz#e316d5257b40b86bb43cb8d5fea5d7f54d6b0ca1" + integrity sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE= vm-browserify@0.0.4, vm-browserify@~0.0.1: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= dependencies: indexof "0.0.1" void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= walkdir@^0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" + integrity sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI= watchpack@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.4.0.tgz#4a1472bcbb952bd0a9bb4036801f954dfb39faac" + integrity sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw= dependencies: async "^2.1.2" chokidar "^1.7.0" @@ -9297,12 +10753,14 @@ watchpack@^1.4.0: wbuf@^1.1.0, wbuf@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" + integrity sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4= dependencies: minimalistic-assert "^1.0.0" wd@^1.0.0: version "1.4.1" resolved "https://registry.yarnpkg.com/wd/-/wd-1.4.1.tgz#6b1ab39aab1728ee276c1a2b6d7321da68b16e8c" + integrity sha512-C0wWd2X4SWWcyx5qxaixiZE4Vb07sl0yDfWHPeml8lDHSbmI9erE9BmTHIqOGoDxGgJ3/hkFmODQ7ZLKiF8+8Q== dependencies: archiver "1.3.0" async "2.0.1" @@ -9316,6 +10774,7 @@ wd@^1.0.0: webdriver-js-extender@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-1.0.0.tgz#81c533a9e33d5bfb597b4e63e2cdb25b54777515" + integrity sha1-gcUzqeM9W/tZe05j4s2yW1R3dRU= dependencies: "@types/selenium-webdriver" "^2.53.35" selenium-webdriver "^2.53.2" @@ -9323,6 +10782,7 @@ webdriver-js-extender@^1.0.0: webdriver-manager@^12.0.6: version "12.0.6" resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.0.6.tgz#3df1a481977010b4cbf8c9d85c7a577828c0e70b" + integrity sha1-PfGkgZdwELTL+MnYXHpXeCjA5ws= dependencies: adm-zip "^0.4.7" chalk "^1.1.1" @@ -9339,10 +10799,12 @@ webdriver-manager@^12.0.6: webfontloader@1.6.28: version "1.6.28" resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae" + integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64= webpack-bundle-analyzer@^2.10.0: version "2.10.0" resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.10.0.tgz#d0646cda342939f6f05eb632a090abbd90317446" + integrity sha512-eA/9F/ZLFlVXfCLYqefHFbelJ3JcvyeFdmpAG6Vu3iJNcisj3KWNPqu00lCqK9caeaesipVrGb9alUSi2lEvAg== dependencies: acorn "^5.3.0" bfj-node4 "^5.2.0" @@ -9360,6 +10822,7 @@ webpack-bundle-analyzer@^2.10.0: webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0: version "1.12.2" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" + integrity sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A== dependencies: memory-fs "~0.4.1" mime "^1.5.0" @@ -9370,6 +10833,7 @@ webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0: webpack-dev-middleware@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-2.0.5.tgz#2a1d07afb599e1993033d72c2181ec2344c15e31" + integrity sha512-EPXudTrpQLksLt9klR0spnb7mt4dHtk3amGnohZNeQ+Y2QSqBbnWA7uNZ9+rqyfhEcYw18pUwcGIXuPFvIIELQ== dependencies: loud-rejection "^1.6.0" memory-fs "~0.4.1" @@ -9382,6 +10846,7 @@ webpack-dev-middleware@^2.0.5: webpack-dev-server@2.11.1: version "2.11.1" resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz#6f9358a002db8403f016e336816f4485384e5ec0" + integrity sha512-ombhu5KsO/85sVshIDTyQ5HF3xjZR3N0sf5Ao6h3vFwpNyzInEzA1GV3QPVjTMLTNckp8PjfG1PFGznzBwS5lg== dependencies: ansi-html "0.0.7" array-includes "^3.0.3" @@ -9414,6 +10879,7 @@ webpack-dev-server@2.11.1: webpack-log@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-1.1.1.tgz#a0c7beb385245da7b2172afe46c02cf3a471ef31" + integrity sha512-9AeZ12uxaS+DGpcIInWxNuoJMW2JbAc45bkn3fhWcdl4wK36MAq/yiyiITt5IS0TaZWjtLIWxwULCuT9V7/xoA== dependencies: chalk "^2.1.0" log-symbols "^2.1.0" @@ -9423,16 +10889,19 @@ webpack-log@^1.0.1: webpack-merge@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.1.1.tgz#f1197a0a973e69c6fbeeb6d658219aa8c0c13555" + integrity sha512-geQsZ86YkXOVOjvPC5yv3JSNnL6/X3Kzh935AQ/gJNEYXEfJDQFu/sdFuktS9OW2JcH/SJec8TGfRdrpHshH7A== dependencies: lodash "^4.17.4" webpack-node-externals@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz#232c62ec6092b100635a3d29d83c1747128df9bd" + integrity sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0= webpack-sources@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf" + integrity sha512-05tMxipUCwHqYaVS8xc7sYPTly8PzXayRCB4dTxLhWTqlKUiwH6ezmEe0OSreL1c30LAuA3Zqmc+uEBUGFJDjw== dependencies: source-list-map "^2.0.0" source-map "~0.5.3" @@ -9440,6 +10909,7 @@ webpack-sources@^1.0.1: webpack-sources@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + integrity sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw== dependencies: source-list-map "^2.0.0" source-map "~0.6.1" @@ -9447,6 +10917,7 @@ webpack-sources@^1.1.0: webpack@^3.11.0: version "3.11.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894" + integrity sha512-3kOFejWqj5ISpJk4Qj/V7w98h9Vl52wak3CLiw/cDOfbVTq7FeoZ0SdoHHY9PYlHr50ZS42OfvzE2vB4nncKQg== dependencies: acorn "^5.0.0" acorn-dynamic-import "^2.0.0" @@ -9474,6 +10945,7 @@ webpack@^3.11.0: websocket-driver@>=0.5.1: version "0.7.0" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + integrity sha1-DK+dLXVdk67gSdS90NP+LMoqJOs= dependencies: http-parser-js ">=0.4.0" websocket-extensions ">=0.1.1" @@ -9481,66 +10953,80 @@ websocket-driver@>=0.5.1: websocket-extensions@>=0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.2.tgz#0e18781de629a18308ce1481650f67ffa2693a5d" + integrity sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0= when@^3.7.7: version "3.7.8" resolved "https://registry.yarnpkg.com/when/-/when-3.7.8.tgz#c7130b6a7ea04693e842cdc9e7a1f2aa39a39f82" + integrity sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I= whet.extend@~0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + integrity sha1-+HfVv2SMl+WqVC+twW1qJZucEaE= which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= which@1, which@^1.1.1, which@^1.2.1, which@^1.2.4, which@^1.2.9: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + integrity sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg== dependencies: isexe "^2.0.0" which@~1.2.10: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + integrity sha1-mofEN48D6CfOyvGs31bHNsAcFOU= dependencies: isexe "^2.0.0" wide-align@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w== dependencies: string-width "^1.0.2" widest-line@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" + integrity sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM= dependencies: string-width "^2.1.1" window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= dependencies: string-width "^1.0.1" strip-ansi "^3.0.1" @@ -9548,10 +11034,12 @@ wrap-ansi@^2.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= write-file-atomic@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA== dependencies: graceful-fs "^4.1.11" imurmurhash "^0.1.4" @@ -9560,6 +11048,7 @@ write-file-atomic@^2.0.0: ws@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" + integrity sha1-iiRPoFJAHgjJiGz0SoUYnh/UBn8= dependencies: options ">=0.0.5" ultron "1.0.x" @@ -9567,6 +11056,7 @@ ws@^1.0.1: ws@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/ws/-/ws-4.0.0.tgz#bfe1da4c08eeb9780b986e0e4d10eccd7345999f" + integrity sha512-QYslsH44bH8O7/W2815u5DpnCpXWpEK44FmaHffNwgJI4JMaSZONgPBTOfrxJ29mXKbXak+LsJ2uAkDTYq2ptQ== dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" @@ -9575,6 +11065,7 @@ ws@^4.0.0: ws@~3.3.1: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" + integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== dependencies: async-limiter "~1.0.0" safe-buffer "~5.1.0" @@ -9583,18 +11074,22 @@ ws@~3.3.1: xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ= xhr2@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" + integrity sha1-f4dliEdxbbUCYyOBL4GMras4el8= xml-char-classes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/xml-char-classes/-/xml-char-classes-1.0.0.tgz#64657848a20ffc5df583a42ad8a277b4512bbc4d" + integrity sha1-ZGV4SKIP/F31g6Qq2KJ3tFErvE0= xml2js@0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.4.tgz#3111010003008ae19240eba17497b57c729c555d" + integrity sha1-MREBAAMAiuGSQOuhdJe1fHKcVV0= dependencies: sax "0.6.x" xmlbuilder ">=1.0.0" @@ -9602,6 +11097,7 @@ xml2js@0.4.4: xml2js@^0.4.17: version "0.4.19" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== dependencies: sax ">=0.6.0" xmlbuilder "~9.0.1" @@ -9609,54 +11105,65 @@ xml2js@^0.4.17: xmlbuilder@>=1.0.0, xmlbuilder@~9.0.1: version "9.0.4" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" + integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8= xmlhttprequest-ssl@~1.5.4: version "1.5.5" resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= xregexp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" + integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= xtend@^4.0.0, xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= yargs-parser@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" + integrity sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw= dependencies: camelcase "^3.0.0" yargs-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= dependencies: camelcase "^3.0.0" yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + integrity sha1-jQrELxbqVd69MyyvTEA4s+P139k= dependencies: camelcase "^4.1.0" yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" + integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc= dependencies: camelcase "^4.1.0" yargs@6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" + integrity sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg= dependencies: camelcase "^3.0.0" cliui "^3.2.0" @@ -9675,6 +11182,7 @@ yargs@6.6.0: yargs@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b" + integrity sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw== dependencies: cliui "^4.0.0" decamelize "^1.1.1" @@ -9692,6 +11200,7 @@ yargs@^11.0.0: yargs@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= dependencies: camelcase "^3.0.0" cliui "^3.2.0" @@ -9710,6 +11219,7 @@ yargs@^7.0.0: yargs@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" + integrity sha1-YpmpBVsc78lp/355wdkY3Osiw2A= dependencies: camelcase "^4.1.0" cliui "^3.2.0" @@ -9728,6 +11238,7 @@ yargs@^8.0.2: yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + integrity sha1-9+572FfdfB0tOMDnTvvWgdFDH9E= dependencies: camelcase "^1.0.2" cliui "^2.1.0" @@ -9737,20 +11248,24 @@ yargs@~3.10.0: yauzl@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= dependencies: fd-slicer "~1.0.1" yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= zip-stream@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-1.2.0.tgz#a8bc45f4c1b49699c6b90198baacaacdbcd4ba04" + integrity sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ= dependencies: archiver-utils "^1.3.0" compress-commons "^1.2.0" @@ -9760,3 +11275,4 @@ zip-stream@^1.1.0: zone.js@0.8.20: version "0.8.20" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.20.tgz#a218c48db09464b19ff6fc8f0d4bb5b1046e185d" + integrity sha512-FXlA37ErSXCMy5RNBcGFgCI/Zivqzr0D19GuvDxhcYIJc7xkFp6c29DKyODJu0Zo+EMyur/WPPgcBh1EHjB9jA== From 2d2d1daf5e9579fa8939066928a69e83a64c6a9b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 13:52:44 +0200 Subject: [PATCH 054/205] 56946: Added tests for toPaginatedList in remote-data-build service --- .../remote-data-build.service.spec.ts | 59 +++++++++++++++++++ .../builders/remote-data-build.service.ts | 2 - 2 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 src/app/core/cache/builders/remote-data-build.service.spec.ts diff --git a/src/app/core/cache/builders/remote-data-build.service.spec.ts b/src/app/core/cache/builders/remote-data-build.service.spec.ts new file mode 100644 index 0000000000..bab628a7fe --- /dev/null +++ b/src/app/core/cache/builders/remote-data-build.service.spec.ts @@ -0,0 +1,59 @@ +import { RemoteDataBuildService } from './remote-data-build.service'; +import { Item } from '../../shared/item.model'; +import { PaginatedList } from '../../data/paginated-list'; +import { PageInfo } from '../../shared/page-info.model'; +import { RemoteData } from '../../data/remote-data'; +import { Observable } from 'rxjs/Observable'; + +const pageInfo = new PageInfo(); +const array = [ + Object.assign(new Item(), { + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'Item nr 1' + }] + }), + Object.assign(new Item(), { + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'Item nr 2' + }] + }) +]; +const paginatedList = new PaginatedList(pageInfo, array); +const arrayRD = new RemoteData(false, false, true, undefined, array); +const paginatedListRD = new RemoteData(false, false, true, undefined, paginatedList); + +describe('RemoteDataBuildService', () => { + let service: RemoteDataBuildService; + + beforeEach(() => { + service = new RemoteDataBuildService(undefined, undefined, undefined); + }); + + describe('when toPaginatedList is called', () => { + let expected: RemoteData>; + + beforeEach(() => { + expected = paginatedListRD; + }); + + it('should return the correct remoteData of a paginatedList when the input is a (remoteData of an) array', () => { + const result = (service as any).toPaginatedList(Observable.of(arrayRD), pageInfo); + result.subscribe((resultRD) => { + expect(resultRD).toEqual(expected); + }); + }); + + it('should return the correct remoteData of a paginatedList when the input is a (remoteData of a) paginated list', () => { + const result = (service as any).toPaginatedList(Observable.of(paginatedListRD), pageInfo); + result.subscribe((resultRD) => { + expect(resultRD).toEqual(expected); + }); + }); + }); +}); diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index cb4a9a31fe..13da50cb87 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -8,8 +8,6 @@ import { RemoteDataError } from '../../data/remote-data-error'; import { GetRequest } from '../../data/request.models'; import { RequestEntry } from '../../data/request.reducer'; import { RequestService } from '../../data/request.service'; -import { Bitstream } from '../../shared/bitstream.model'; -import { Item } from '../../shared/item.model'; import { NormalizedObject } from '../models/normalized-object.model'; import { ObjectCacheService } from '../object-cache.service'; From 9918d9529fb861bddeecc532583470d20c8ec7f5 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 15:43:45 +0200 Subject: [PATCH 055/205] 56434: search-response parsing bug fix --- src/app/core/data/search-response-parsing.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index 62130eb6b5..4039b8f761 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -15,7 +15,7 @@ export class SearchResponseParsingService implements ResponseParsingService { } parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { - const payload = data.payload/*._embedded.searchResult*/; + const payload = data.payload._embedded.searchResult; const hitHighlights = payload._embedded.objects .map((object) => object.hitHighlights) .map((hhObject) => { @@ -55,6 +55,6 @@ export class SearchResponseParsingService implements ResponseParsingService { })); payload.objects = objects; const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload); - return new SearchSuccessResponse(deserialized, data.statusCode, this.dsoParser.processPageInfo(data.payload)); + return new SearchSuccessResponse(deserialized, data.statusCode, this.dsoParser.processPageInfo(payload)); } } From efcdc67d6882d526f5fda84921a00a1ba6990793 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 24 Oct 2018 16:09:55 +0200 Subject: [PATCH 056/205] 56434: Small TSLint fix --- src/app/shared/pagination/pagination-component-options.model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts index ac2c09bbb7..30ed2becd2 100644 --- a/src/app/shared/pagination/pagination-component-options.model.ts +++ b/src/app/shared/pagination/pagination-component-options.model.ts @@ -19,4 +19,4 @@ export class PaginationComponentOptions extends NgbPaginationConfig { pageSize: number; -} \ No newline at end of file +} From b70a58d008f09cf5d6069a899e07c535584daff7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 26 Oct 2018 10:30:47 +0200 Subject: [PATCH 057/205] 56434: EntityPageFields component tests refactoring --- .../journal-issue-page-fields.component.html | 8 +- ...ournal-issue-page-fields.component.spec.ts | 63 +-------------- .../journal-volume-page-fields.component.html | 6 +- ...urnal-volume-page-fields.component.spec.ts | 63 +-------------- .../orgunit-page-fields.component.html | 10 +-- .../orgunit-page-fields.component.spec.ts | 63 +-------------- .../person/person-page-fields.component.html | 14 ++-- .../person-page-fields.component.spec.ts | 65 +-------------- .../project-page-fields.component.html | 10 +-- .../project-page-fields.component.spec.ts | 63 +-------------- .../entity-page-fields.component.spec.ts | 81 +++++++++++++++++++ 11 files changed, 115 insertions(+), 331 deletions(-) create mode 100644 src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html index 9a7262334b..73fee41f8f 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html @@ -6,11 +6,11 @@ - - @@ -24,11 +24,11 @@ [entities]="publications$ | async" [label]="'relationships.isPublicationOfJournalIssue' | translate"> - - diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts index adf5529212..28802d68aa 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts @@ -1,23 +1,10 @@ -import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { JournalIssuePageFieldsComponent } from './journal-issue-page-fields.component'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { isNotEmpty } from '../../../../shared/empty.util'; - -let comp: JournalIssuePageFieldsComponent; -let fixture: ComponentFixture; +import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -44,50 +31,4 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('JournalIssuePageFieldsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - })], - declarations: [JournalIssuePageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], - providers: [ - {provide: ITEM, useValue: mockItem}, - {provide: ItemDataService, useValue: {}}, - {provide: TruncatableService, useValue: {}} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(JournalIssuePageFieldsComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(JournalIssuePageFieldsComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { - const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); - }); - } -}); - -function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { - for (const field of fields) { - const fieldComp = field.componentInstance; - if (isNotEmpty(fieldComp.fields)) { - if (fieldComp.fields.indexOf(metadataKey) > -1) { - return true; - } - } - } - return false; -} +describe('JournalIssuePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalIssuePageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html index ebac09b398..df770b06d8 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html @@ -6,11 +6,11 @@ - - @@ -24,7 +24,7 @@ [entities]="issues$ | async" [label]="'relationships.isIssueOf' | translate"> - diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts index ab77c4ae62..bd406db360 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts @@ -1,23 +1,10 @@ -import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { isNotEmpty } from '../../../../shared/empty.util'; import { JournalVolumePageFieldsComponent } from './journal-volume-page-fields.component'; - -let comp: JournalVolumePageFieldsComponent; -let fixture: ComponentFixture; +import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -39,50 +26,4 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('JournalVolumePageFieldsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - })], - declarations: [JournalVolumePageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], - providers: [ - {provide: ITEM, useValue: mockItem}, - {provide: ItemDataService, useValue: {}}, - {provide: TruncatableService, useValue: {}} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(JournalVolumePageFieldsComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(JournalVolumePageFieldsComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { - const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); - }); - } -}); - -function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { - for (const field of fields) { - const fieldComp = field.componentInstance; - if (isNotEmpty(fieldComp.fields)) { - if (fieldComp.fields.indexOf(metadataKey) > -1) { - return true; - } - } - } - return false; -} +describe('JournalVolumePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalVolumePageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html index 8314eeae08..e5a02b54e5 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html @@ -6,19 +6,19 @@ - - - - @@ -36,7 +36,7 @@ [entities]="publications$ | async" [label]="'relationships.isPublicationOf' | translate"> - diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts index cb066c0783..7ae90a7a9d 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts @@ -1,23 +1,10 @@ -import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { isNotEmpty } from '../../../../shared/empty.util'; import { OrgUnitPageFieldsComponent } from './orgunit-page-fields.component'; - -let comp: OrgUnitPageFieldsComponent; -let fixture: ComponentFixture; +import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -49,50 +36,4 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('OrgUnitPageFieldsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - })], - declarations: [OrgUnitPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], - providers: [ - {provide: ITEM, useValue: mockItem}, - {provide: ItemDataService, useValue: {}}, - {provide: TruncatableService, useValue: {}} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(OrgUnitPageFieldsComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(OrgUnitPageFieldsComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { - const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); - }); - } -}); - -function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { - for (const field of fields) { - const fieldComp = field.componentInstance; - if (isNotEmpty(fieldComp.fields)) { - if (fieldComp.fields.indexOf(metadataKey) > -1) { - return true; - } - } - } - return false; -} +describe('OrgUnitPageFieldsComponent', getEntityPageFieldsTest(mockItem, OrgUnitPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html index b712c5e479..548f46624d 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html @@ -6,19 +6,19 @@ - - - - @@ -32,15 +32,15 @@ [entities]="orgUnits$ | async" [label]="'relationships.isOrgUnitOf' | translate"> - - - diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts index 02f75966da..99432a3e66 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts @@ -1,24 +1,10 @@ -import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { isNotEmpty } from '../../../../shared/empty.util'; import { PersonPageFieldsComponent } from './person-page-fields.component'; -import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; - -let comp: PersonPageFieldsComponent; -let fixture: ComponentFixture; +import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -60,51 +46,4 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('PersonPageFieldsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - })], - declarations: [PersonPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], - providers: [ - {provide: ITEM, useValue: mockItem}, - {provide: ItemDataService, useValue: {}}, - {provide: SearchFixedFilterService, useValue: {}}, - {provide: TruncatableService, useValue: {}} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(PersonPageFieldsComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(PersonPageFieldsComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { - const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); - }); - } -}); - -function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { - for (const field of fields) { - const fieldComp = field.componentInstance; - if (isNotEmpty(fieldComp.fields)) { - if (fieldComp.fields.indexOf(metadataKey) > -1) { - return true; - } - } - } - return false; -} +describe('PersonPageFieldsComponent', getEntityPageFieldsTest(mockItem, PersonPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html index 159ae84584..36a71bd71a 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html @@ -6,15 +6,15 @@ - - - @@ -32,11 +32,11 @@ [entities]="orgUnits$ | async" [label]="'relationships.isOrgUnitOf' | translate"> - - diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts index 6494b19215..013e7e1c00 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts @@ -1,23 +1,10 @@ -import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; -import { ItemDataService } from '../../../../core/data/item-data.service'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { By } from '@angular/platform-browser'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; -import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { isNotEmpty } from '../../../../shared/empty.util'; import { ProjectPageFieldsComponent } from './project-page-fields.component'; - -let comp: ProjectPageFieldsComponent; -let fixture: ComponentFixture; +import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -49,50 +36,4 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('ProjectPageFieldsComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: MockTranslateLoader - } - })], - declarations: [ProjectPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], - providers: [ - {provide: ITEM, useValue: mockItem}, - {provide: ItemDataService, useValue: {}}, - {provide: TruncatableService, useValue: {}} - ], - - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ProjectPageFieldsComponent, { - set: {changeDetection: ChangeDetectionStrategy.Default} - }).compileComponents(); - })); - - beforeEach(async(() => { - fixture = TestBed.createComponent(ProjectPageFieldsComponent); - comp = fixture.componentInstance; - fixture.detectChanges(); - })); - - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { - const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); - }); - } -}); - -function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { - for (const field of fields) { - const fieldComp = field.componentInstance; - if (isNotEmpty(fieldComp.fields)) { - if (fieldComp.fields.indexOf(metadataKey) > -1) { - return true; - } - } - } - return false; -} +describe('ProjectPageFieldsComponent', getEntityPageFieldsTest(mockItem, ProjectPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts new file mode 100644 index 0000000000..a0d880ca15 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts @@ -0,0 +1,81 @@ +import { Item } from '../../../../core/shared/item.model'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; + +/** + * Create a generic test for an entity-page-fields component using a mockItem and the type of component + * @param {Item} mockItem The item to use for testing. The item needs to contain just the metadata necessary to + * execute the tests for it's component. + * @param component The type of component to create test cases for. + * @returns {() => void} Returns a specDefinition for the test. + */ +export function getEntityPageFieldsTest(mockItem: Item, component) { + return () => { + let comp: any; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [component, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: SearchFixedFilterService, useValue: {}}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(component, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(component); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + for (const metadata of mockItem.metadata) { + it(`should be calling a component with metadata field ${metadata.key}`, () => { + const fields = fixture.debugElement.queryAll(By.css('ds-generic-item-page-field')); + expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + }); + } + } +} + +/** + * Checks whether in a list of debug elements, at least one of them contains a specific metadata key in their + * fields property. + * @param {DebugElement[]} fields List of debug elements to check + * @param {string} metadataKey A metadata key to look for + * @returns {boolean} + */ +export function containsFieldInput(fields: DebugElement[], metadataKey: string): boolean { + for (const field of fields) { + const fieldComp = field.componentInstance; + if (isNotEmpty(fieldComp.fields)) { + if (fieldComp.fields.indexOf(metadataKey) > -1) { + return true; + } + } + } + return false; +} From 5d8c6358dbd4330b5319823a2c7805a44b595a47 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 26 Oct 2018 13:09:43 +0200 Subject: [PATCH 058/205] 56434: FullItemPageComponent tests and improved coverage --- .../full/full-item-page.component.spec.ts | 76 +++++++++++++++++++ ...ournal-issue-page-fields.component.spec.ts | 5 +- ...urnal-volume-page-fields.component.spec.ts | 5 +- .../orgunit-page-fields.component.spec.ts | 5 +- .../person-page-fields.component.spec.ts | 5 +- .../project-page-fields.component.spec.ts | 5 +- .../entity-page-fields.component.spec.ts | 22 +++++- 7 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 src/app/+item-page/full/full-item-page.component.spec.ts diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts new file mode 100644 index 0000000000..c2107831fa --- /dev/null +++ b/src/app/+item-page/full/full-item-page.component.spec.ts @@ -0,0 +1,76 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { TruncatePipe } from '../../shared/utils/truncate.pipe'; +import { FullItemPageComponent } from './full-item-page.component'; +import { MetadataService } from '../../core/metadata/metadata.service'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; +import { VarDirective } from '../../shared/utils/var.directive'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Item } from '../../core/shared/item.model'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { RemoteData } from '../../core/data/remote-data'; +import { Observable } from 'rxjs/Observable'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { By } from '@angular/platform-browser'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [ + { + key: 'dc.title', + language: 'en_US', + value: 'test item' + }] +}); +const routeStub = Object.assign(new ActivatedRouteStub(), { + data: Observable.of({ item: new RemoteData(false, false, true, null, mockItem) }) +}); +const metadataServiceStub = { + /* tslint:disable:no-empty */ + processRemoteData: () => {} + /* tslint:enable:no-empty */ +}; + +describe('FullItemPageComponent', () => { + let comp: FullItemPageComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), RouterTestingModule.withRoutes([]), BrowserAnimationsModule], + declarations: [FullItemPageComponent, TruncatePipe, VarDirective], + providers: [ + {provide: ActivatedRoute, useValue: routeStub}, + {provide: ItemDataService, useValue: {}}, + {provide: MetadataService, useValue: metadataServiceStub} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(FullItemPageComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(FullItemPageComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should display the item\'s metadata', () => { + const table = fixture.debugElement.query(By.css('table')); + for (const metadatum of mockItem.metadata) { + expect(table.nativeElement.innerHTML).toContain(metadatum.value); + } + }) +}); diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts index 28802d68aa..2de039a343 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts @@ -4,7 +4,7 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -28,7 +28,8 @@ const mockItem: Item = Object.assign(new Item(), { key: 'journalissue.identifier.keyword', language: 'en_US', value: 'keyword' - }] + }], + relationships: createRelationshipsObservable() }); describe('JournalIssuePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalIssuePageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts index bd406db360..16d1689a04 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts @@ -4,7 +4,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { JournalVolumePageFieldsComponent } from './journal-volume-page-fields.component'; -import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -23,7 +23,8 @@ const mockItem: Item = Object.assign(new Item(), { key: 'journalvolume.identifier.description', language: 'en_US', value: 'desc' - }] + }], + relationships: createRelationshipsObservable() }); describe('JournalVolumePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalVolumePageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts index 7ae90a7a9d..2976894a5e 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts @@ -4,7 +4,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { OrgUnitPageFieldsComponent } from './orgunit-page-fields.component'; -import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -33,7 +33,8 @@ const mockItem: Item = Object.assign(new Item(), { key: 'orgunit.identifier.description', language: 'en_US', value: 'desc' - }] + }], + relationships: createRelationshipsObservable() }); describe('OrgUnitPageFieldsComponent', getEntityPageFieldsTest(mockItem, OrgUnitPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts index 99432a3e66..b95c3451e7 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts @@ -4,7 +4,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { PersonPageFieldsComponent } from './person-page-fields.component'; -import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -43,7 +43,8 @@ const mockItem: Item = Object.assign(new Item(), { key: 'person.identifier.firstname', language: 'en_US', value: 'John' - }] + }], + relationships: createRelationshipsObservable() }); describe('PersonPageFieldsComponent', getEntityPageFieldsTest(mockItem, PersonPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts index 013e7e1c00..b0f0f1f80d 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts @@ -4,7 +4,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { ProjectPageFieldsComponent } from './project-page-fields.component'; -import { getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -33,7 +33,8 @@ const mockItem: Item = Object.assign(new Item(), { key: 'project.identifier.keyword', language: 'en_US', value: 'keyword' - }] + }], + relationships: createRelationshipsObservable() }); describe('ProjectPageFieldsComponent', getEntityPageFieldsTest(mockItem, ProjectPageFieldsComponent)); diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts index a0d880ca15..90964eb5c1 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts @@ -11,6 +11,12 @@ import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher. import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { isNotEmpty } from '../../../../shared/empty.util'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; +import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { Relationship } from '../../../../core/shared/entities/relationship.model'; +import { Observable } from 'rxjs/Observable'; +import { PageInfo } from '../../../../core/shared/page-info.model'; /** * Create a generic test for an entity-page-fields component using a mockItem and the type of component @@ -24,6 +30,12 @@ export function getEntityPageFieldsTest(mockItem: Item, component) { let comp: any; let fixture: ComponentFixture; + const searchFixedFilterServiceStub = { + /* tslint:disable:no-empty */ + getQueryByRelations: () => {} + /* tslint:enable:no-empty */ + }; + beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ @@ -36,7 +48,7 @@ export function getEntityPageFieldsTest(mockItem: Item, component) { providers: [ {provide: ITEM, useValue: mockItem}, {provide: ItemDataService, useValue: {}}, - {provide: SearchFixedFilterService, useValue: {}}, + {provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub}, {provide: TruncatableService, useValue: {}} ], @@ -79,3 +91,11 @@ export function containsFieldInput(fields: DebugElement[], metadataKey: string): } return false; } + +export function createRelationshipsObservable() { + return Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [ + Object.assign(new Relationship(), { + relationshipType: Observable.of(new RemoteData(false, false, true, null, new RelationshipType())) + }) + ]))); +} From e39879b76212f27f1dcb60b11e45c030f50c21f7 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 26 Oct 2018 13:47:48 +0200 Subject: [PATCH 059/205] 56434: e2e test fix --- e2e/app.e2e-spec.ts | 4 ++-- e2e/app.po.ts | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/e2e/app.e2e-spec.ts b/e2e/app.e2e-spec.ts index f5ac9094d0..6856a6f01b 100644 --- a/e2e/app.e2e-spec.ts +++ b/e2e/app.e2e-spec.ts @@ -12,8 +12,8 @@ describe('protractor App', () => { expect(page.getPageTitleText()).toEqual('DSpace Angular :: Home'); }); - it('should display header "Welcome to DSpace"', () => { + it('should contain a news section', () => { page.navigateTo(); - expect(page.getFirstHeaderText()).toEqual('Welcome to DSpace'); + expect(page.getHomePageNewsText()).toBeDefined(); }); }); diff --git a/e2e/app.po.ts b/e2e/app.po.ts index d8d2acf120..54b5b55af3 100644 --- a/e2e/app.po.ts +++ b/e2e/app.po.ts @@ -9,11 +9,7 @@ export class ProtractorPage { return browser.getTitle(); } - getFirstPText() { - return element(by.xpath('//p[1]')).getText(); - } - - getFirstHeaderText() { - return element(by.xpath('//h1[1]')).getText(); + getHomePageNewsText() { + return element(by.xpath('//ds-home-news')).getText(); } } From 1126af6b4363b5cfedf2ab20349aed8661cd1709 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 26 Oct 2018 13:57:36 +0200 Subject: [PATCH 060/205] 56434: By.css call by tag instead of class refactoring --- .../metadata-uri-values/metadata-uri-values.component.html | 4 ++-- .../metadata-uri-values.component.spec.ts | 6 +++--- .../metadata-values/metadata-values.component.html | 2 +- .../metadata-values/metadata-values.component.spec.ts | 2 +- .../search-results/search-results.component.html | 2 +- .../search-results/search-results.component.spec.ts | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html index 10676e93c0..cc618bcd50 100644 --- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.html @@ -1,5 +1,5 @@ - + {{ linktext || metadatum.value }} diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts index f2ec0bedbc..d12a73a885 100644 --- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts @@ -57,14 +57,14 @@ describe('MetadataUriValuesComponent', () => { }); it('should contain the correct hrefs', () => { - const links = fixture.debugElement.queryAll(By.css('.metadata-value-links')); + const links = fixture.debugElement.queryAll(By.css('a')); for (const metadatum of mockMetadata) { expect(containsHref(links, metadatum.value)).toBeTruthy(); } }); it('should contain separators equal to the amount of metadata values minus one', () => { - const separators = fixture.debugElement.queryAll(By.css('.metadata-value-separator')); + const separators = fixture.debugElement.queryAll(By.css('a span')); expect(separators.length).toBe(mockMetadata.length - 1); }); @@ -76,7 +76,7 @@ describe('MetadataUriValuesComponent', () => { }); it('should replace the metadata value with the linktext', () => { - const link = fixture.debugElement.query(By.css('.metadata-value-links')); + const link = fixture.debugElement.query(By.css('a')); expect(link.nativeElement.textContent).toContain(mockLinkText); }); diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.html b/src/app/+item-page/field-components/metadata-values/metadata-values.component.html index c85699b092..f16655c63c 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.html +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.html @@ -1,5 +1,5 @@ - {{metadatum.value}} + {{metadatum.value}} diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts index fdcf6679cc..c60946b99e 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts @@ -60,7 +60,7 @@ describe('MetadataValuesComponent', () => { }); it('should contain separators equal to the amount of metadata values minus one', () => { - const separators = fixture.debugElement.queryAll(By.css('.metadata-value-separator')); + const separators = fixture.debugElement.queryAll(By.css('span>span')); expect(separators.length).toBe(mockMetadata.length - 1); }); diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index 126efecbf9..b685d0b28d 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,4 +1,4 @@ -

{{ getTitleKey() | translate }}

+

{{ getTitleKey() | translate }}

{ fixture = TestBed.createComponent(SearchResultsComponent); comp = fixture.componentInstance; // SearchFormComponent test instance heading = fixture.debugElement.query(By.css('heading')); - title = fixture.debugElement.query(By.css('.search-results-title')); + title = fixture.debugElement.query(By.css('h2')); }); describe('when results are not empty', () => { From ec6124035d35a505b6d6dec17305baa8220d3fd4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 8 Nov 2018 09:58:31 +0100 Subject: [PATCH 061/205] Default REST API server set to dspace7-entities.atmire.com for testing purposes --- config/environment.default.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/environment.default.js b/config/environment.default.js index a6ef738f41..2926440338 100644 --- a/config/environment.default.js +++ b/config/environment.default.js @@ -10,10 +10,10 @@ module.exports = { // The REST API server settings. rest: { ssl: true, - host: 'dspace7.4science.it', + host: 'dspace7-entities.atmire.com', port: 443, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/dspace-spring-rest/api' + nameSpace: '/rest/api' }, // Caching settings cache: { From 626b9fee1033346a4e3b970b7e02ff2bfd6b12c4 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 22 Nov 2018 13:15:00 +0100 Subject: [PATCH 062/205] 57557: reset homepage and fix collection sorting --- resources/images/atmire-logo.svg | 37 ------------------- .../collection-page.component.ts | 2 +- .../home-news/home-news.component.html | 13 ++++--- 3 files changed, 9 insertions(+), 43 deletions(-) delete mode 100644 resources/images/atmire-logo.svg diff --git a/resources/images/atmire-logo.svg b/resources/images/atmire-logo.svg deleted file mode 100644 index 5a416693ce..0000000000 --- a/resources/images/atmire-logo.svg +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index fb38108125..557c94c5e3 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -53,7 +53,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy { this.paginationConfig.id = 'collection-page-pagination'; this.paginationConfig.pageSize = 5; this.paginationConfig.currentPage = 1; - this.sortConfig = new SortOptions('dc.title', SortDirection.ASC); + this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.ASC); } ngOnInit(): void { diff --git a/src/app/+home-page/home-news/home-news.component.html b/src/app/+home-page/home-news/home-news.component.html index c7943bb7f5..ffc88be574 100644 --- a/src/app/+home-page/home-news/home-news.component.html +++ b/src/app/+home-page/home-news/home-news.component.html @@ -2,16 +2,19 @@
- +
-

DSpace 7 entities prototype

-

This is the demo site of the prototype implementation of a flexible data model for DSpace 7:

+

Welcome to DSpace

+

DSpace is an open source software platform that enables organisations to:

    -
  • The functionality is governed and discussed by the DSpace 7 Entities Working Group, if you would like to participate, please visit the wiki
  • -
  • Documentation on this prototype implementation and links to the source code can be found here
  • +
  • capture and describe digital material using a submission workflow module, or a variety of programmatic ingest options +
  • +
  • distribute an organisation's digital assets over the web through a search and retrieval system +
  • +
  • preserve digital assets over the long term
From ba92882d7fe593fa77b3859ae2fa28e26e5f790d Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 22 Nov 2018 13:37:52 +0100 Subject: [PATCH 063/205] 57557: Refactored EntitySearchResultComponent --- .../entity-search-result-component.ts | 81 +++---------------- .../search-result-list-element.component.ts | 8 +- 2 files changed, 18 insertions(+), 71 deletions(-) diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts index 7c05584cf0..ed53c45395 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts @@ -1,13 +1,11 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; -import { Metadatum } from '../../../../core/shared/metadatum.model'; -import { hasNoValue, hasValue, isEmpty } from '../../../empty.util'; +import { hasValue } from '../../../empty.util'; import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; -// TODO lot of overlap with SearchResultListElementComponent => refactor! /** * A generic component for displaying entity list elements */ @@ -15,77 +13,24 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; selector: 'ds-entity-search-result', template: '' }) -export class EntitySearchResultComponent { +export class EntitySearchResultComponent extends SearchResultListElementComponent { item: Item; - searchResult: ItemSearchResult; constructor( - private truncatableService: TruncatableService, - @Inject(ITEM) public object: Item | ItemSearchResult, + protected truncatableService: TruncatableService, + @Inject(ITEM) public obj: Item | ItemSearchResult, ) { - - if (hasValue((this.object as any).dspaceObject)) { - this.searchResult = this.object as ItemSearchResult; - this.item = this.searchResult.dspaceObject; + super(undefined, truncatableService); + if (hasValue((obj as any).dspaceObject)) { + this.object = obj as ItemSearchResult; + this.dso = this.object.dspaceObject; } else { - this.searchResult = { - dspaceObject: this.object as Item, + this.object = { + dspaceObject: obj as Item, hitHighlights: [] }; - this.item = this.object as Item; + this.dso = obj as Item; } - } - - /** - * Get the values of metadata by keys - * @param {string[]} keys List of metadata keys to get values for - * @returns {string[]} List of metadata values - */ - getValues(keys: string[]): string[] { - const results: string[] = new Array(); - this.searchResult.hitHighlights.forEach( - (md: Metadatum) => { - if (keys.indexOf(md.key) > -1) { - results.push(md.value); - } - } - ); - if (isEmpty(results)) { - this.item.filterMetadata(keys).forEach( - (md: Metadatum) => { - results.push(md.value); - } - ); - } - return results; - } - - /** - * Get the first value of a metadatum by key - * @param {string} key Metadatum key - * @returns {string} Metadatum value - */ - getFirstValue(key: string): string { - let result: string; - this.searchResult.hitHighlights.some( - (md: Metadatum) => { - if (key === md.key) { - result = md.value; - return true; - } - } - ); - if (hasNoValue(result)) { - result = this.item.findMetadata(key); - } - return result; - } - - /** - * Whether or not the item's values are collapsed - * @returns {Observable} - */ - isCollapsed(): Observable { - return this.truncatableService.isCollapsed(this.item.id); + this.item = this.dso; } } diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index fd821997ad..e84d558552 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs/Observable'; import { SearchResult } from '../../../+search-page/search-result.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { Metadatum } from '../../../core/shared/metadatum.model'; -import { hasNoValue, isEmpty } from '../../empty.util'; +import { hasNoValue, hasValue, isEmpty } from '../../empty.util'; import { ListableObject } from '../../object-collection/shared/listable-object.model'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { TruncatableService } from '../../truncatable/truncatable.service'; @@ -17,9 +17,11 @@ import { TruncatableService } from '../../truncatable/truncatable.service'; export class SearchResultListElementComponent, K extends DSpaceObject> extends AbstractListableElementComponent { dso: K; - public constructor(@Inject('objectElementProvider') public listable: ListableObject, private truncatableService: TruncatableService) { + public constructor(@Inject('objectElementProvider') public listable: ListableObject, protected truncatableService: TruncatableService) { super(listable); - this.dso = this.object.dspaceObject; + if (hasValue(this.object)) { + this.dso = this.object.dspaceObject; + } } getValues(keys: string[]): string[] { From 6d69c7de5c513def496bea40cfb8c055754aa304 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 22 Nov 2018 13:54:54 +0100 Subject: [PATCH 064/205] 57557: Generic publishers in spec files --- .../metadata-values/metadata-values.component.spec.ts | 2 +- .../entity-types/journal/journal-page-fields.component.spec.ts | 2 +- .../publication/publication-list-element.component.spec.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts index c60946b99e..9447e1c48e 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts @@ -17,7 +17,7 @@ const mockMetadata = [ { key: 'journal.publisher', language: 'en_US', - value: 'Atmire' + value: 'a publisher' }, { key: 'journal.identifier.description', diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts index 1b7142b108..9e743c1426 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts @@ -30,7 +30,7 @@ const mockItem: Item = Object.assign(new Item(), { { key: 'journal.publisher', language: 'en_US', - value: 'Atmire' + value: 'a publisher' }, { key: 'journal.identifier.description', diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts index 0e96999c3f..8bfc0d9077 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts @@ -27,7 +27,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { { key: 'dc.publisher', language: 'en_US', - value: 'Atmire' + value: 'a publisher' }, { key: 'dc.date.issued', From 7b6665d33eef66f8ce68019ab9ce2f9162a4da59 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Thu, 22 Nov 2018 17:19:14 +0100 Subject: [PATCH 065/205] 57557: Added missing spec files --- .../publication-page-fields.component.spec.ts | 89 ++++++++++++++++++ .../simple/item-page.component.spec.ts | 91 +++++++++++++++++++ .../related-entities.component.spec.ts | 51 +++++++++++ ...very-page-response-parsing.service.spec.ts | 35 +++++++ .../entity-list-element.component.spec.ts | 46 ++++++++++ .../entity-search-result.component.spec.ts | 82 +++++++++++++++++ ...arch-result-list-element.component.spec.ts | 49 ++++++++++ 7 files changed, 443 insertions(+) create mode 100644 src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts create mode 100644 src/app/+item-page/simple/item-page.component.spec.ts create mode 100644 src/app/+item-page/simple/related-entities/related-entities.component.spec.ts create mode 100644 src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts create mode 100644 src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts new file mode 100644 index 0000000000..29e421b2b9 --- /dev/null +++ b/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts @@ -0,0 +1,89 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; +import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; +import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; +import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ItemDataService } from '../../../../core/data/item-data.service'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; +import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { PublicationPageFieldsComponent } from './publication-page-fields.component'; +import { Item } from '../../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { createRelationshipsObservable } from '../shared/entity-page-fields.component.spec'; +import { By } from '@angular/platform-browser'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); + +describe('PublicationPageFieldsComponent', () => { + let comp: PublicationPageFieldsComponent; + let fixture: ComponentFixture; + + const searchFixedFilterServiceStub = { + /* tslint:disable:no-empty */ + getQueryByRelations: () => {} + /* tslint:enable:no-empty */ + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + })], + declarations: [PublicationPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + providers: [ + {provide: ITEM, useValue: mockItem}, + {provide: ItemDataService, useValue: {}}, + {provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub}, + {provide: TruncatableService, useValue: {}} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PublicationPageFieldsComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PublicationPageFieldsComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should contain a component to display the date', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-date-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the author', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-author-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the abstract', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-abstract-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the uri', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-uri-field')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + + it('should contain a component to display the collections', () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-page-collections')); + expect(fields.length).toBeGreaterThanOrEqual(1); + }); + +}); diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts new file mode 100644 index 0000000000..6b62811227 --- /dev/null +++ b/src/app/+item-page/simple/item-page.component.spec.ts @@ -0,0 +1,91 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; +import { TruncatePipe } from '../../shared/utils/truncate.pipe'; +import { ItemDataService } from '../../core/data/item-data.service'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ItemPageComponent } from './item-page.component'; +import { ActivatedRoute } from '@angular/router'; +import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; +import { MetadataService } from '../../core/metadata/metadata.service'; +import { VarDirective } from '../../shared/utils/var.directive'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../core/data/remote-data'; +import { Item } from '../../core/shared/item.model'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { createRelationshipsObservable } from './entity-types/shared/entity-page-fields.component.spec'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { By } from '@angular/platform-browser'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); + +describe('ItemPageComponent', () => { + let comp: ItemPageComponent; + let fixture: ComponentFixture; + + const mockMetadataService = { + /* tslint:disable:no-empty */ + processRemoteData: () => {} + /* tslint:enable:no-empty */ + }; + const mockRoute = Object.assign(new ActivatedRouteStub(), { + data: Observable.of({ item: new RemoteData(false, false, true, null, mockItem) }) + }); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), BrowserAnimationsModule], + declarations: [ItemPageComponent, VarDirective], + providers: [ + {provide: ActivatedRoute, useValue: mockRoute}, + {provide: ItemDataService, useValue: {}}, + {provide: MetadataService, useValue: mockMetadataService} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemPageComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + describe('when the item is loading', () => { + beforeEach(() => { + comp.itemRD$ = Observable.of(new RemoteData(true, true, true, null, undefined)); + fixture.detectChanges(); + }); + + it('should display a loading component', () => { + const loading = fixture.debugElement.query(By.css('ds-loading')); + expect(loading.nativeElement).toBeDefined(); + }); + }); + + describe('when the item failed loading', () => { + beforeEach(() => { + comp.itemRD$ = Observable.of(new RemoteData(false, false, false, null, undefined)); + fixture.detectChanges(); + }); + + it('should display an error component', () => { + const error = fixture.debugElement.query(By.css('ds-error')); + expect(error.nativeElement).toBeDefined(); + }); + }); + +}); diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts new file mode 100644 index 0000000000..6bf4c99e5d --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts @@ -0,0 +1,51 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { RelatedEntitiesComponent } from './related-entities-component'; +import { Item } from '../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { createRelationshipsObservable } from '../entity-types/shared/entity-page-fields.component.spec'; +import { By } from '@angular/platform-browser'; + +const mockItem1: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); +const mockItem2: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); +const mockEntities = [mockItem1, mockItem2]; + +describe('RelatedEntitiesComponent', () => { + let comp: RelatedEntitiesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [RelatedEntitiesComponent], + providers: [], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(RelatedEntitiesComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(RelatedEntitiesComponent); + comp = fixture.componentInstance; + comp.entities = mockEntities; + fixture.detectChanges(); + })); + + it(`should load ${mockEntities.length} entity-type-switcher components`, () => { + const fields = fixture.debugElement.queryAll(By.css('ds-entity-type-switcher')); + expect(fields.length).toBe(mockEntities.length); + }); + +}); diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts new file mode 100644 index 0000000000..13eaeb03d4 --- /dev/null +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts @@ -0,0 +1,35 @@ +import { FilteredDiscoveryPageResponseParsingService } from './filtered-discovery-page-response-parsing.service'; +import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service'; +import { GenericConstructor } from '../shared/generic-constructor'; +import { ResponseParsingService } from './parsing.service'; +import { GetRequest } from './request.models'; +import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; +import { FilteredDiscoveryQueryResponse } from '../cache/response-cache.models'; + +describe('FilteredDiscoveryPageResponseParsingService', () => { + let service: FilteredDiscoveryPageResponseParsingService; + + beforeEach(() => { + service = new FilteredDiscoveryPageResponseParsingService(undefined, getMockObjectCacheService()); + }); + + describe('parse', () => { + const request = Object.assign(new GetRequest('client/f5b4ccb8-fbb0-4548-b558-f234d9fdfad6', 'https://rest.api/path'), { + getResponseParser(): GenericConstructor { + return FilteredDiscoveryPageResponseParsingService; + } + }); + + const mockResponse = { + payload: { + 'discovery-query': 'query' + }, + statusCode: '200' + } as DSpaceRESTV2Response; + + it('should return a FilteredDiscoveryQueryResponse containing the correct query', () => { + const response = service.parse(request, mockResponse); + expect((response as FilteredDiscoveryQueryResponse).filterQuery).toBe(mockResponse.payload['discovery-query']); + }) + }); +}); diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts new file mode 100644 index 0000000000..ccf7307216 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts @@ -0,0 +1,46 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { EntityListElementComponent } from './entity-list-element.component'; +import { Item } from '../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PaginatedList } from '../../../core/data/paginated-list'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; +import { By } from '@angular/platform-browser'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); + +describe('EntityListElementComponent', () => { + let comp: EntityListElementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [EntityListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: mockItem } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(EntityListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(EntityListElementComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should call an entity-type-switcher component and pass the item', () => { + const entityTypeSwitcher = fixture.debugElement.query(By.css('ds-entity-type-switcher')).componentInstance; + expect(entityTypeSwitcher.object).toBe(mockItem); + }); + +}); diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts new file mode 100644 index 0000000000..419622df77 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts @@ -0,0 +1,82 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { EntitySearchResultComponent } from './entity-search-result-component'; +import { Item } from '../../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; +import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; +import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); +const mockSearchResult = { + dspaceObject: mockItem as Item, + hitHighlights: [] +} as ItemSearchResult; + +describe('EntitySearchResultComponent', () => { + let comp: EntitySearchResultComponent; + let fixture: ComponentFixture; + + describe('when injecting an Item', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EntitySearchResultComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: ITEM, useValue: mockItem} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(EntitySearchResultComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(EntitySearchResultComponent); + comp = fixture.componentInstance; + })); + + it('should initiate item, object and dso correctly', () => { + expect(comp.item).toBe(mockItem); + expect(comp.dso).toBe(mockItem); + expect(comp.object.dspaceObject).toBe(mockItem); + }) + }); + + describe('when injecting an ItemSearchResult', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [EntitySearchResultComponent, TruncatePipe], + providers: [ + {provide: TruncatableService, useValue: {}}, + {provide: ITEM, useValue: mockSearchResult} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(EntitySearchResultComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(EntitySearchResultComponent); + comp = fixture.componentInstance; + })); + + it('should initiate item, object and dso correctly', () => { + expect(comp.item).toBe(mockItem); + expect(comp.dso).toBe(mockItem); + expect(comp.object.dspaceObject).toBe(mockItem); + }) + }); +}); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts new file mode 100644 index 0000000000..f71acda02c --- /dev/null +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -0,0 +1,49 @@ +import { Item } from '../../../../core/shared/item.model'; +import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { PaginatedList } from '../../../../core/data/paginated-list'; +import { PageInfo } from '../../../../core/shared/page-info.model'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { ItemSearchResultListElementComponent } from './item-search-result-list-element.component'; +import { TruncatableService } from '../../../truncatable/truncatable.service'; +import { TruncatePipe } from '../../../utils/truncate.pipe'; + +const mockItem: Item = Object.assign(new Item(), { + bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + metadata: [], + relationships: createRelationshipsObservable() +}); + +describe('ItemSearchResultListElementComponent', () => { + let comp: ItemSearchResultListElementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [ItemSearchResultListElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: {} }, + { provide: 'objectElementProvider', useValue: mockItem } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemSearchResultListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemSearchResultListElementComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should call an entity-type-switcher component and pass the item', () => { + const entityTypeSwitcher = fixture.debugElement.query(By.css('ds-entity-type-switcher')).componentInstance; + expect(entityTypeSwitcher.object).toBe(mockItem); + }); + +}); From 3f98fed050391e99bd7fb6572e045abbbdbf927e Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 23 Nov 2018 13:06:29 +0100 Subject: [PATCH 066/205] 57557: Added JSDocs --- .../shared/entity-page-fields.component.ts | 7 +++++ src/app/+search-page/search-page.component.ts | 7 +---- .../search-results.component.ts | 5 ---- .../entities/normalized-entity-type.model.ts | 12 +++++++++ .../normalized-relationship-type.model.ts | 22 +++++++++++++++ .../entities/normalized-relationship.model.ts | 18 +++++++++++++ ...discovery-page-response-parsing.service.ts | 11 ++++++++ .../core/shared/entities/entity-type.model.ts | 15 +++++++++++ .../entities/relationship-type.model.ts | 27 +++++++++++++++++++ .../shared/entities/relationship.model.ts | 21 +++++++++++++++ .../shared/entities/entity-type-decorator.ts | 11 ++++++++ src/app/shared/view-mode.ts | 13 +++++++++ 12 files changed, 158 insertions(+), 11 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts index 7b23c87806..e480472dac 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts @@ -11,6 +11,10 @@ import { getRemoteDataPayload } from '../../../../core/shared/operators'; import { hasValue } from '../../../../shared/empty.util'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +/** + * Operator for comparing arrays using a mapping function + * @param mapFn Function for mapping the arrays + */ const compareArraysUsing = (mapFn: (t: T) => any) => (a: T[], b: T[]): boolean => { if (!Array.isArray(a) || ! Array.isArray(b)) { @@ -25,6 +29,9 @@ const compareArraysUsing = (mapFn: (t: T) => any) => bIds.every((e) => aIds.includes(e)); }; +/** + * Operator for comparing arrays using the object's ids + */ const compareArraysUsingIds = () => compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 7c4879409c..417c61152c 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -18,12 +18,6 @@ import { SearchConfigurationService } from './search-service/search-configuratio import { getSucceededRemoteData } from '../core/shared/operators'; import { RouteService } from '../shared/services/route.service'; -/** - * This component renders a simple item page. - * The route parameter 'id' is used to request the item it represents. - * All fields of the item that should be displayed, are defined in its template. - */ - @Component({ selector: 'ds-search-page', styleUrls: ['./search-page.component.scss'], @@ -34,6 +28,7 @@ import { RouteService } from '../shared/services/route.service'; /** * This component represents the whole search page + * It renders search results depending on the current search options */ export class SearchPageComponent implements OnInit { diff --git a/src/app/+search-page/search-results/search-results.component.ts b/src/app/+search-page/search-results/search-results.component.ts index 62ef393a2e..07360add0c 100644 --- a/src/app/+search-page/search-results/search-results.component.ts +++ b/src/app/+search-page/search-results/search-results.component.ts @@ -9,11 +9,6 @@ import { SearchResult } from '../search-result.model'; import { PaginatedList } from '../../core/data/paginated-list'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; -/** - * This component renders a simple item page. - * The route parameter 'id' is used to request the item it represents. - * All fields of the item that should be displayed, are defined in its template. - */ @Component({ selector: 'ds-search-results', templateUrl: './search-results.component.html', diff --git a/src/app/core/cache/models/entities/normalized-entity-type.model.ts b/src/app/core/cache/models/entities/normalized-entity-type.model.ts index 2abbd71ed1..729aa39d7b 100644 --- a/src/app/core/cache/models/entities/normalized-entity-type.model.ts +++ b/src/app/core/cache/models/entities/normalized-entity-type.model.ts @@ -5,16 +5,28 @@ import { mapsTo } from '../../builders/build-decorators'; import { NormalizedObject } from '../normalized-object.model'; import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; +/** + * Normalized model class for a DSpace EntityType + */ @mapsTo(EntityType) @inheritSerialization(NormalizedObject) export class NormalizedEntityType extends NormalizedObject { + /** + * The label that describes the ResourceType of the Entity + */ @autoserialize label: string; + /** + * The identifier of this EntityType + */ @autoserialize id: string; + /** + * The universally unique identifier of this EntityType + */ @autoserializeAs(new IDToUUIDSerializer(ResourceType.EntityType), 'id') uuid: string; } diff --git a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts index e8e832d18e..a7e46a9d1b 100644 --- a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts +++ b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts @@ -6,12 +6,22 @@ import { NormalizedDSpaceObject } from '../normalized-dspace-object.model'; import { NormalizedObject } from '../normalized-object.model'; import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; +/** + * Normalized model class for a DSpace RelationshipType + */ @mapsTo(RelationshipType) @inheritSerialization(NormalizedDSpaceObject) export class NormalizedRelationshipType extends NormalizedObject { + + /** + * The identifier of this RelationshipType + */ @autoserialize id: string; + /** + * The label that describes the Relation to the left of this RelationshipType + */ @autoserialize leftLabel: string; @@ -21,6 +31,9 @@ export class NormalizedRelationshipType extends NormalizedObject { @autoserialize leftMinCardinality: number; + /** + * The label that describes the Relation to the right of this RelationshipType + */ @autoserialize rightLabel: string; @@ -30,14 +43,23 @@ export class NormalizedRelationshipType extends NormalizedObject { @autoserialize rightMinCardinality: number; + /** + * The type of Entity found to the left of this RelationshipType + */ @autoserialize @relationship(ResourceType.EntityType, false) leftType: string; + /** + * The type of Entity found to the right of this RelationshipType + */ @autoserialize @relationship(ResourceType.EntityType, false) rightType: string; + /** + * The universally unique identifier of this RelationshipType + */ @autoserializeAs(new IDToUUIDSerializer(ResourceType.RelationshipType), 'id') uuid: string; } diff --git a/src/app/core/cache/models/entities/normalized-relationship.model.ts b/src/app/core/cache/models/entities/normalized-relationship.model.ts index 191dc08f7b..7c7508d9ef 100644 --- a/src/app/core/cache/models/entities/normalized-relationship.model.ts +++ b/src/app/core/cache/models/entities/normalized-relationship.model.ts @@ -5,26 +5,44 @@ import { mapsTo, relationship } from '../../builders/build-decorators'; import { NormalizedObject } from '../normalized-object.model'; import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; +/** + * Normalized model class for a DSpace Relationship + */ @mapsTo(Relationship) @inheritSerialization(NormalizedObject) export class NormalizedRelationship extends NormalizedObject { + /** + * The identifier of this Relationship + */ @autoserialize id: string; + /** + * The identifier of the Relationship to the left side of this Relationship + */ @autoserialize leftId: string; @autoserialize place: number; + /** + * The identifier of the Relationship to the right side of this Relationship + */ @autoserialize rightId: string; + /** + * The type of Relationship + */ @autoserialize @relationship(ResourceType.RelationshipType, false) relationshipType: string; + /** + * The universally unique identifier of this Relationship + */ @autoserializeAs(new IDToUUIDSerializer(ResourceType.Relationship), 'id') uuid: string; } diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts index ee87230214..299207b12b 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.ts @@ -8,6 +8,10 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { GlobalConfig } from '../../../config/global-config.interface'; import { GLOBAL_CONFIG } from '../../../config'; +/** + * A ResponseParsingService used to parse DSpaceRESTV2Response coming from the REST API to a discovery query (string) + * wrapped in a FilteredDiscoveryQueryResponse + */ @Injectable() export class FilteredDiscoveryPageResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { objectFactory = {}; @@ -17,6 +21,13 @@ export class FilteredDiscoveryPageResponseParsingService extends BaseResponsePar protected objectCache: ObjectCacheService, ) { super(); } + + /** + * Parses data from the REST API to a discovery query wrapped in a FilteredDiscoveryQueryResponse + * @param {RestRequest} request + * @param {DSpaceRESTV2Response} data + * @returns {RestResponse} + */ parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { const query = data.payload['discovery-query']; return new FilteredDiscoveryQueryResponse(query, data.statusCode); diff --git a/src/app/core/shared/entities/entity-type.model.ts b/src/app/core/shared/entities/entity-type.model.ts index 93e60f3efc..bfca58fa1a 100644 --- a/src/app/core/shared/entities/entity-type.model.ts +++ b/src/app/core/shared/entities/entity-type.model.ts @@ -2,8 +2,23 @@ import { CacheableObject } from '../../cache/object-cache.reducer'; import { ResourceType } from '../resource-type'; export class EntityType implements CacheableObject { + /** + * The identifier of this EntityType + */ id: string; + + /** + * The link to the rest endpoint where this object can be found + */ self: string; + + /** + * The type of Resource this is + */ type: ResourceType; + + /** + * The universally unique identifier of this EntityType + */ uuid: string; } diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/entities/relationship-type.model.ts index ff3abd2d1b..943a8f94f5 100644 --- a/src/app/core/shared/entities/relationship-type.model.ts +++ b/src/app/core/shared/entities/relationship-type.model.ts @@ -5,29 +5,56 @@ import { ResourceType } from '../resource-type'; import { EntityType } from './entity-type.model'; export class RelationshipType implements CacheableObject { + /** + * The link to the rest endpoint where this object can be found + */ self: string; + /** + * The type of Resource this is + */ type: ResourceType; + /** + * The label that describes this RelationshipType + */ label: string; + /** + * The identifier of this RelationshipType + */ id: string; + /** + * The universally unique identifier of this RelationshipType + */ uuid: string; + /** + * The label that describes the Relation to the left of this RelationshipType + */ leftLabel: string; leftMaxCardinality: number; leftMinCardinality: number; + /** + * The label that describes the Relation to the right of this RelationshipType + */ rightLabel: string; rightMaxCardinality: number; rightMinCardinality: number; + /** + * The type of Entity found to the left of this RelationshipType + */ leftType: Observable>; + /** + * The type of Entity found to the right of this RelationshipType + */ rightType: Observable>; } diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts index a26d6f5179..4df3e32b86 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/entities/relationship.model.ts @@ -5,19 +5,40 @@ import { ResourceType } from '../resource-type'; import { RelationshipType } from './relationship-type.model'; export class Relationship implements CacheableObject { + /** + * The link to the rest endpoint where this object can be found + */ self: string; + /** + * The type of Resource this is + */ type: ResourceType; + /** + * The universally unique identifier of this Relationship + */ uuid: string; + /** + * The identifier of this Relationship + */ id: string; + /** + * The identifier of the Relationship to the left side of this Relationship + */ leftId: string; place: string; + /** + * The identifier of the Relationship to the right side of this Relationship + */ rightId: string; + /** + * The type of Relationship + */ relationshipType: Observable>; } diff --git a/src/app/shared/entities/entity-type-decorator.ts b/src/app/shared/entities/entity-type-decorator.ts index 60bdab7412..9af226ea3d 100644 --- a/src/app/shared/entities/entity-type-decorator.ts +++ b/src/app/shared/entities/entity-type-decorator.ts @@ -4,6 +4,12 @@ import { ElementViewMode } from '../view-mode'; export const DEFAULT_ENTITY_TYPE = 'Default'; const map = new Map(); + +/** + * Decorator used for rendering simple item pages for an Entity by type and viewMode + * @param type + * @param viewMode + */ export function rendersEntityType(type: string, viewMode: ElementViewMode) { return function decorator(component: any) { if (hasNoValue(map.get(viewMode))) { @@ -16,6 +22,11 @@ export function rendersEntityType(type: string, viewMode: ElementViewMode) { }; } +/** + * Get the component used for rendering an entity by type and viewMode + * @param type + * @param viewMode + */ export function getComponentByEntityType(type: string, viewMode: ElementViewMode) { let component = map.get(viewMode).get(type); if (hasNoValue(component)) { diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts index b0e180c290..2825f20c50 100644 --- a/src/app/shared/view-mode.ts +++ b/src/app/shared/view-mode.ts @@ -1,11 +1,24 @@ +/** + * Enum used for defining the view-mode of a set of elements + * List Display the elements in a (vertical) list + * Grid Display the elements in a grid + */ export enum SetViewMode { List = 'list', Grid = 'grid' } +/** + * Enum used for defining the view-mode of a single element + * Full Display the full element + * SetElement Display the element as part of a set + */ export enum ElementViewMode { Full, SetElement } +/** + * ViewMode refers to either a SetViewMode or ElementViewMode + */ export type ViewMode = SetViewMode | ElementViewMode; From 19033e638af9213f1be6c66fa0f9df963ed4aa2a Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 26 Nov 2018 10:48:51 +0100 Subject: [PATCH 067/205] 57557: Fixed/Added some more JSDocs --- .../entities/normalized-relationship-type.model.ts | 12 ++++++++++++ .../models/entities/normalized-relationship.model.ts | 7 ++----- .../core/shared/entities/relationship-type.model.ts | 12 ++++++++++++ src/app/core/shared/entities/relationship.model.ts | 6 ++---- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts index a7e46a9d1b..ea71e217b9 100644 --- a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts +++ b/src/app/core/cache/models/entities/normalized-relationship-type.model.ts @@ -25,9 +25,15 @@ export class NormalizedRelationshipType extends NormalizedObject { @autoserialize leftLabel: string; + /** + * The maximum amount of Relationships allowed to the left of this RelationshipType + */ @autoserialize leftMaxCardinality: number; + /** + * The minimum amount of Relationships allowed to the left of this RelationshipType + */ @autoserialize leftMinCardinality: number; @@ -37,9 +43,15 @@ export class NormalizedRelationshipType extends NormalizedObject { @autoserialize rightLabel: string; + /** + * The maximum amount of Relationships allowed to the right of this RelationshipType + */ @autoserialize rightMaxCardinality: number; + /** + * The minimum amount of Relationships allowed to the right of this RelationshipType + */ @autoserialize rightMinCardinality: number; diff --git a/src/app/core/cache/models/entities/normalized-relationship.model.ts b/src/app/core/cache/models/entities/normalized-relationship.model.ts index 7c7508d9ef..0f5bb6cf90 100644 --- a/src/app/core/cache/models/entities/normalized-relationship.model.ts +++ b/src/app/core/cache/models/entities/normalized-relationship.model.ts @@ -19,16 +19,13 @@ export class NormalizedRelationship extends NormalizedObject { id: string; /** - * The identifier of the Relationship to the left side of this Relationship + * The identifier of the Entity to the left side of this Relationship */ @autoserialize leftId: string; - @autoserialize - place: number; - /** - * The identifier of the Relationship to the right side of this Relationship + * The identifier of the Entity to the right side of this Relationship */ @autoserialize rightId: string; diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/entities/relationship-type.model.ts index 943a8f94f5..b505d6dddc 100644 --- a/src/app/core/shared/entities/relationship-type.model.ts +++ b/src/app/core/shared/entities/relationship-type.model.ts @@ -35,8 +35,14 @@ export class RelationshipType implements CacheableObject { */ leftLabel: string; + /** + * The maximum amount of Relationships allowed to the left of this RelationshipType + */ leftMaxCardinality: number; + /** + * The minimum amount of Relationships allowed to the left of this RelationshipType + */ leftMinCardinality: number; /** @@ -44,8 +50,14 @@ export class RelationshipType implements CacheableObject { */ rightLabel: string; + /** + * The maximum amount of Relationships allowed to the right of this RelationshipType + */ rightMaxCardinality: number; + /** + * The minimum amount of Relationships allowed to the right of this RelationshipType + */ rightMinCardinality: number; /** diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts index 4df3e32b86..62c204925e 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/entities/relationship.model.ts @@ -26,14 +26,12 @@ export class Relationship implements CacheableObject { id: string; /** - * The identifier of the Relationship to the left side of this Relationship + * The identifier of the Entity to the left side of this Relationship */ leftId: string; - place: string; - /** - * The identifier of the Relationship to the right side of this Relationship + * The identifier of the Entity to the right side of this Relationship */ rightId: string; From 1ba7d1e29d62f6c99b6464b9ab7a97424e682332 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 26 Nov 2018 11:03:49 +0100 Subject: [PATCH 068/205] 57557: Added leftPlace and rightPlace to Relationship + JSDocs --- .../models/entities/normalized-relationship.model.ts | 12 ++++++++++++ src/app/core/shared/entities/relationship.model.ts | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/app/core/cache/models/entities/normalized-relationship.model.ts b/src/app/core/cache/models/entities/normalized-relationship.model.ts index 0f5bb6cf90..598687bbde 100644 --- a/src/app/core/cache/models/entities/normalized-relationship.model.ts +++ b/src/app/core/cache/models/entities/normalized-relationship.model.ts @@ -30,6 +30,18 @@ export class NormalizedRelationship extends NormalizedObject { @autoserialize rightId: string; + /** + * The place of the Entity to the left side of this Relationship + */ + @autoserialize + leftPlace: number; + + /** + * The place of the Entity to the right side of this Relationship + */ + @autoserialize + rightPlace: number; + /** * The type of Relationship */ diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts index 62c204925e..2106a83f5d 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/entities/relationship.model.ts @@ -35,6 +35,16 @@ export class Relationship implements CacheableObject { */ rightId: string; + /** + * The place of the Entity to the left side of this Relationship + */ + leftPlace: number; + + /** + * The place of the Entity to the right side of this Relationship + */ + rightPlace: number; + /** * The type of Relationship */ From 3988b7afe206ab3e577d9b061c4db1ad4b1cd1c3 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 26 Nov 2018 15:53:05 +0100 Subject: [PATCH 069/205] 57557: Added JSDocs and Specs for EntityPageFieldsComponent operators --- .../entity-page-fields.component.spec.ts | 208 ++++++++++++++++++ .../shared/entity-page-fields.component.ts | 7 +- .../core/shared/entities/entity-type.model.ts | 3 + .../entities/relationship-type.model.ts | 3 + .../shared/entities/relationship.model.ts | 3 + 5 files changed, 222 insertions(+), 2 deletions(-) diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts index 90964eb5c1..4791101244 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts @@ -17,6 +17,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { Relationship } from '../../../../core/shared/entities/relationship.model'; import { Observable } from 'rxjs/Observable'; import { PageInfo } from '../../../../core/shared/page-info.model'; +import { compareArraysUsing, compareArraysUsingIds } from './entity-page-fields.component'; /** * Create a generic test for an entity-page-fields component using a mockItem and the type of component @@ -99,3 +100,210 @@ export function createRelationshipsObservable() { }) ]))); } +describe('EntityPageFieldsComponent', () => { + const arr1 = [ + { + id: 1, + name: 'test' + }, + { + id: 2, + name: 'another test' + }, + { + id: 3, + name: 'one last test' + } + ]; + const arrWithWrongId = [ + { + id: 1, + name: 'test' + }, + { + id: 5, // Wrong id on purpose + name: 'another test' + }, + { + id: 3, + name: 'one last test' + } + ]; + const arrWithWrongName = [ + { + id: 1, + name: 'test' + }, + { + id: 2, + name: 'wrong test' // Wrong name on purpose + }, + { + id: 3, + name: 'one last test' + } + ]; + const arrWithDifferentOrder = [arr1[0], arr1[2], arr1[1]]; + const arrWithOneMore = [...arr1, { + id: 4, + name: 'fourth test' + }]; + const arrWithAddedProperties = [ + { + id: 1, + name: 'test', + extra: 'extra property' + }, + { + id: 2, + name: 'another test', + extra: 'extra property' + }, + { + id: 3, + name: 'one last test', + extra: 'extra property' + } + ]; + const arrOfPrimitiveTypes = [1, 2, 3, 4]; + const arrOfPrimitiveTypesWithOneWrong = [1, 5, 3, 4]; + const arrOfPrimitiveTypesWithDifferentOrder = [1, 3, 2, 4]; + const arrOfPrimitiveTypesWithOneMore = [1, 2, 3, 4, 5]; + + describe('when calling compareArraysUsing', () => { + + describe('and comparing by id', () => { + const compare = compareArraysUsing((o) => o.id); + + it('should return true when comparing the same array', () => { + expect(compare(arr1, arr1)).toBeTruthy(); + }); + + it('should return true regardless of the order', () => { + expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy(); + }); + + it('should return true regardless of other properties being different', () => { + expect(compare(arr1, arrWithWrongName)).toBeTruthy(); + }); + + it('should return true regardless of extra properties', () => { + expect(compare(arr1, arrWithAddedProperties)).toBeTruthy(); + }); + + it('should return false when the ids don\'t match', () => { + expect(compare(arr1, arrWithWrongId)).toBeFalsy(); + }); + + it('should return false when the sizes don\'t match', () => { + expect(compare(arr1, arrWithOneMore)).toBeFalsy(); + }); + }); + + describe('and comparing by name', () => { + const compare = compareArraysUsing((o) => o.name); + + it('should return true when comparing the same array', () => { + expect(compare(arr1, arr1)).toBeTruthy(); + }); + + it('should return true regardless of the order', () => { + expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy(); + }); + + it('should return true regardless of other properties being different', () => { + expect(compare(arr1, arrWithWrongId)).toBeTruthy(); + }); + + it('should return true regardless of extra properties', () => { + expect(compare(arr1, arrWithAddedProperties)).toBeTruthy(); + }); + + it('should return false when the names don\'t match', () => { + expect(compare(arr1, arrWithWrongName)).toBeFalsy(); + }); + + it('should return false when the sizes don\'t match', () => { + expect(compare(arr1, arrWithOneMore)).toBeFalsy(); + }); + }); + + describe('and comparing by full objects', () => { + const compare = compareArraysUsing((o) => o); + + it('should return true when comparing the same array', () => { + expect(compare(arr1, arr1)).toBeTruthy(); + }); + + it('should return true regardless of the order', () => { + expect(compare(arr1, arrWithDifferentOrder)).toBeTruthy(); + }); + + it('should return false when extra properties are added', () => { + expect(compare(arr1, arrWithAddedProperties)).toBeFalsy(); + }); + + it('should return false when the ids don\'t match', () => { + expect(compare(arr1, arrWithWrongId)).toBeFalsy(); + }); + + it('should return false when the names don\'t match', () => { + expect(compare(arr1, arrWithWrongName)).toBeFalsy(); + }); + + it('should return false when the sizes don\'t match', () => { + expect(compare(arr1, arrWithOneMore)).toBeFalsy(); + }); + }); + + describe('and comparing with primitive objects as source', () => { + const compare = compareArraysUsing((o) => o); + + it('should return true when comparing the same array', () => { + expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypes)).toBeTruthy(); + }); + + it('should return true regardless of the order', () => { + expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithDifferentOrder)).toBeTruthy(); + }); + + it('should return false when at least one is wrong', () => { + expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithOneWrong)).toBeFalsy(); + }); + + it('should return false when the sizes don\'t match', () => { + expect(compare(arrOfPrimitiveTypes, arrOfPrimitiveTypesWithOneMore)).toBeFalsy(); + }); + }); + + }); + + describe('when calling compareArraysUsingIds', () => { + const compare = compareArraysUsingIds(); + + it('should return true when comparing the same array', () => { + expect(compare(arr1 as any, arr1 as any)).toBeTruthy(); + }); + + it('should return true regardless of the order', () => { + expect(compare(arr1 as any, arrWithDifferentOrder as any)).toBeTruthy(); + }); + + it('should return true regardless of other properties being different', () => { + expect(compare(arr1 as any, arrWithWrongName as any)).toBeTruthy(); + }); + + it('should return true regardless of extra properties', () => { + expect(compare(arr1 as any, arrWithAddedProperties as any)).toBeTruthy(); + }); + + it('should return false when the ids don\'t match', () => { + expect(compare(arr1 as any, arrWithWrongId as any)).toBeFalsy(); + }); + + it('should return false when the sizes don\'t match', () => { + expect(compare(arr1 as any, arrWithOneMore as any)).toBeFalsy(); + }); + }); + +}); diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts index e480472dac..0f6214fc15 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts @@ -13,9 +13,12 @@ import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher. /** * Operator for comparing arrays using a mapping function + * The mapping function should turn the source array into an array of basic types, so that the array can + * be compared using these basic types. + * For example: "(o) => o.id" will compare the two arrays by comparing their content by id. * @param mapFn Function for mapping the arrays */ -const compareArraysUsing = (mapFn: (t: T) => any) => +export const compareArraysUsing = (mapFn: (t: T) => any) => (a: T[], b: T[]): boolean => { if (!Array.isArray(a) || ! Array.isArray(b)) { return false @@ -32,7 +35,7 @@ const compareArraysUsing = (mapFn: (t: T) => any) => /** * Operator for comparing arrays using the object's ids */ -const compareArraysUsingIds = () => +export const compareArraysUsingIds = () => compareArraysUsing((t: T) => hasValue(t) ? t.id : undefined); /** diff --git a/src/app/core/shared/entities/entity-type.model.ts b/src/app/core/shared/entities/entity-type.model.ts index bfca58fa1a..4532e05b19 100644 --- a/src/app/core/shared/entities/entity-type.model.ts +++ b/src/app/core/shared/entities/entity-type.model.ts @@ -1,6 +1,9 @@ import { CacheableObject } from '../../cache/object-cache.reducer'; import { ResourceType } from '../resource-type'; +/** + * Describes a type of Entity + */ export class EntityType implements CacheableObject { /** * The identifier of this EntityType diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/entities/relationship-type.model.ts index b505d6dddc..864d019d0e 100644 --- a/src/app/core/shared/entities/relationship-type.model.ts +++ b/src/app/core/shared/entities/relationship-type.model.ts @@ -4,6 +4,9 @@ import { RemoteData } from '../../data/remote-data'; import { ResourceType } from '../resource-type'; import { EntityType } from './entity-type.model'; +/** + * Describes a type of Relationship between multiple possible Entities + */ export class RelationshipType implements CacheableObject { /** * The link to the rest endpoint where this object can be found diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts index 2106a83f5d..c1dc1a1971 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/entities/relationship.model.ts @@ -4,6 +4,9 @@ import { RemoteData } from '../../data/remote-data'; import { ResourceType } from '../resource-type'; import { RelationshipType } from './relationship-type.model'; +/** + * Describes a Relationship between two Entities + */ export class Relationship implements CacheableObject { /** * The link to the rest endpoint where this object can be found From d32ec1177891d83ef072cece3c86907bb05dbd71 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Nov 2018 13:16:12 +0100 Subject: [PATCH 070/205] 57557: Sort direction on collection page reverted to DESC --- src/app/+collection-page/collection-page.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index 557c94c5e3..d1555b8ee0 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -53,7 +53,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy { this.paginationConfig.id = 'collection-page-pagination'; this.paginationConfig.pageSize = 5; this.paginationConfig.currentPage = 1; - this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.ASC); + this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC); } ngOnInit(): void { From 69aaa18ec852fe2fb2b1a9f975890d435a4a292c Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Wed, 28 Nov 2018 15:51:14 +0100 Subject: [PATCH 071/205] 57557: Renamed EntityPageFieldsComponents to EntityComponents --- src/app/+item-page/item-page.module.ts | 46 +++++++++---------- ...nent.html => journal-issue.component.html} | 0 ...nent.scss => journal-issue.component.scss} | 0 ...pec.ts => journal-issue.component.spec.ts} | 6 +-- ...omponent.ts => journal-issue.component.ts} | 13 ++---- ...ent.html => journal-volume.component.html} | 0 ...ent.scss => journal-volume.component.scss} | 0 ...ec.ts => journal-volume.component.spec.ts} | 6 +-- ...mponent.ts => journal-volume.component.ts} | 13 ++---- ....component.html => journal.component.html} | 0 ....component.scss => journal.component.scss} | 0 ...nent.spec.ts => journal.component.spec.ts} | 14 +++--- ...elds.component.ts => journal.component.ts} | 13 ++---- ....component.html => orgunit.component.html} | 0 ....component.scss => orgunit.component.scss} | 0 ...nent.spec.ts => orgunit.component.spec.ts} | 6 +-- ...elds.component.ts => orgunit.component.ts} | 13 ++---- ...s.component.html => person.component.html} | 0 ...s.component.scss => person.component.scss} | 0 ...onent.spec.ts => person.component.spec.ts} | 6 +-- ...ields.component.ts => person.component.ts} | 13 ++---- ....component.html => project.component.html} | 0 ....component.scss => project.component.scss} | 0 ...nent.spec.ts => project.component.spec.ts} | 6 +-- ...elds.component.ts => project.component.ts} | 13 ++---- ...ponent.html => publication.component.html} | 0 ...ponent.scss => publication.component.scss} | 0 ....spec.ts => publication.component.spec.ts} | 16 +++---- ....component.ts => publication.component.ts} | 13 ++---- ...onent.spec.ts => entity.component.spec.ts} | 4 +- ...ields.component.ts => entity.component.ts} | 4 +- .../simple/item-page.component.spec.ts | 3 +- .../related-entities.component.spec.ts | 2 +- .../entity-list-element.component.spec.ts | 2 +- .../entity-search-result.component.spec.ts | 2 +- ...arch-result-list-element.component.spec.ts | 2 +- 36 files changed, 97 insertions(+), 119 deletions(-) rename src/app/+item-page/simple/entity-types/journal-issue/{journal-issue-page-fields.component.html => journal-issue.component.html} (100%) rename src/app/+item-page/simple/entity-types/journal-issue/{journal-issue-page-fields.component.scss => journal-issue.component.scss} (100%) rename src/app/+item-page/simple/entity-types/journal-issue/{journal-issue-page-fields.component.spec.ts => journal-issue.component.spec.ts} (79%) rename src/app/+item-page/simple/entity-types/journal-issue/{journal-issue-page-fields.component.ts => journal-issue.component.ts} (79%) rename src/app/+item-page/simple/entity-types/journal-volume/{journal-volume-page-fields.component.html => journal-volume.component.html} (100%) rename src/app/+item-page/simple/entity-types/journal-volume/{journal-volume-page-fields.component.scss => journal-volume.component.scss} (100%) rename src/app/+item-page/simple/entity-types/journal-volume/{journal-volume-page-fields.component.spec.ts => journal-volume.component.spec.ts} (77%) rename src/app/+item-page/simple/entity-types/journal-volume/{journal-volume-page-fields.component.ts => journal-volume.component.ts} (79%) rename src/app/+item-page/simple/entity-types/journal/{journal-page-fields.component.html => journal.component.html} (100%) rename src/app/+item-page/simple/entity-types/journal/{journal-page-fields.component.scss => journal.component.scss} (100%) rename src/app/+item-page/simple/entity-types/journal/{journal-page-fields.component.spec.ts => journal.component.spec.ts} (86%) rename src/app/+item-page/simple/entity-types/journal/{journal-page-fields.component.ts => journal.component.ts} (76%) rename src/app/+item-page/simple/entity-types/orgunit/{orgunit-page-fields.component.html => orgunit.component.html} (100%) rename src/app/+item-page/simple/entity-types/orgunit/{orgunit-page-fields.component.scss => orgunit.component.scss} (100%) rename src/app/+item-page/simple/entity-types/orgunit/{orgunit-page-fields.component.spec.ts => orgunit.component.spec.ts} (82%) rename src/app/+item-page/simple/entity-types/orgunit/{orgunit-page-fields.component.ts => orgunit.component.ts} (82%) rename src/app/+item-page/simple/entity-types/person/{person-page-fields.component.html => person.component.html} (100%) rename src/app/+item-page/simple/entity-types/person/{person-page-fields.component.scss => person.component.scss} (100%) rename src/app/+item-page/simple/entity-types/person/{person-page-fields.component.spec.ts => person.component.spec.ts} (84%) rename src/app/+item-page/simple/entity-types/person/{person-page-fields.component.ts => person.component.ts} (86%) rename src/app/+item-page/simple/entity-types/project/{project-page-fields.component.html => project.component.html} (100%) rename src/app/+item-page/simple/entity-types/project/{project-page-fields.component.scss => project.component.scss} (100%) rename src/app/+item-page/simple/entity-types/project/{project-page-fields.component.spec.ts => project.component.spec.ts} (82%) rename src/app/+item-page/simple/entity-types/project/{project-page-fields.component.ts => project.component.ts} (82%) rename src/app/+item-page/simple/entity-types/publication/{publication-page-fields.component.html => publication.component.html} (100%) rename src/app/+item-page/simple/entity-types/publication/{publication-page-fields.component.scss => publication.component.scss} (100%) rename src/app/+item-page/simple/entity-types/publication/{publication-page-fields.component.spec.ts => publication.component.spec.ts} (85%) rename src/app/+item-page/simple/entity-types/publication/{publication-page-fields.component.ts => publication.component.ts} (83%) rename src/app/+item-page/simple/entity-types/shared/{entity-page-fields.component.spec.ts => entity.component.spec.ts} (99%) rename src/app/+item-page/simple/entity-types/shared/{entity-page-fields.component.ts => entity.component.ts} (98%) diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index cbc8dde575..2103a548c1 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -2,7 +2,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from './../shared/shared.module'; -import { EntityPageFieldsComponent } from './simple/entity-types/shared/entity-page-fields.component'; import { GenericItemPageFieldComponent } from './simple/field-components/specific-field/generic/generic-item-page-field.component'; import { ItemPageComponent } from './simple/item-page.component'; @@ -20,15 +19,16 @@ import { FileSectionComponent } from './simple/field-components/file-section/fil import { CollectionsComponent } from './field-components/collections/collections.component'; import { FullItemPageComponent } from './full/full-item-page.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; -import { PublicationPageFieldsComponent } from './simple/entity-types/publication/publication-page-fields.component'; -import { OrgUnitPageFieldsComponent } from './simple/entity-types/orgunit/orgunit-page-fields.component'; -import { PersonPageFieldsComponent } from './simple/entity-types/person/person-page-fields.component'; -import { ProjectPageFieldsComponent } from './simple/entity-types/project/project-page-fields.component'; import { RelatedEntitiesComponent } from './simple/related-entities/related-entities-component'; -import { JournalPageFieldsComponent } from './simple/entity-types/journal/journal-page-fields.component'; -import { JournalIssuePageFieldsComponent } from './simple/entity-types/journal-issue/journal-issue-page-fields.component'; -import { JournalVolumePageFieldsComponent } from './simple/entity-types/journal-volume/journal-volume-page-fields.component'; import { SearchPageModule } from '../+search-page/search-page.module'; +import { PublicationComponent } from './simple/entity-types/publication/publication.component'; +import { PersonComponent } from './simple/entity-types/person/person.component'; +import { OrgunitComponent } from './simple/entity-types/orgunit/orgunit.component'; +import { ProjectComponent } from './simple/entity-types/project/project.component'; +import { JournalComponent } from './simple/entity-types/journal/journal.component'; +import { JournalVolumeComponent } from './simple/entity-types/journal-volume/journal-volume.component'; +import { JournalIssueComponent } from './simple/entity-types/journal-issue/journal-issue.component'; +import { EntityComponent } from './simple/entity-types/shared/entity.component'; @NgModule({ imports: [ @@ -52,25 +52,25 @@ import { SearchPageModule } from '../+search-page/search-page.module'; FileSectionComponent, CollectionsComponent, FullFileSectionComponent, - PublicationPageFieldsComponent, - ProjectPageFieldsComponent, - OrgUnitPageFieldsComponent, - PersonPageFieldsComponent, + PublicationComponent, + ProjectComponent, + OrgunitComponent, + PersonComponent, RelatedEntitiesComponent, - EntityPageFieldsComponent, + EntityComponent, GenericItemPageFieldComponent, - JournalPageFieldsComponent, - JournalIssuePageFieldsComponent, - JournalVolumePageFieldsComponent + JournalComponent, + JournalIssueComponent, + JournalVolumeComponent ], entryComponents: [ - PublicationPageFieldsComponent, - ProjectPageFieldsComponent, - OrgUnitPageFieldsComponent, - PersonPageFieldsComponent, - JournalPageFieldsComponent, - JournalIssuePageFieldsComponent, - JournalVolumePageFieldsComponent + PublicationComponent, + ProjectComponent, + OrgunitComponent, + PersonComponent, + JournalComponent, + JournalIssueComponent, + JournalVolumeComponent ] }) export class ItemPageModule { diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.html rename to src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts similarity index 79% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts index 2de039a343..19de0ebff1 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts @@ -1,10 +1,10 @@ -import { JournalIssuePageFieldsComponent } from './journal-issue-page-fields.component'; import { Observable } from 'rxjs/Observable'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { JournalIssueComponent } from './journal-issue.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -32,4 +32,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('JournalIssuePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalIssuePageFieldsComponent)); +describe('JournalIssueComponent', getEntityPageFieldsTest(mockItem, JournalIssueComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts similarity index 79% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts index 49d24b623b..445d061699 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts @@ -5,22 +5,19 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('JournalIssue', ElementViewMode.Full) @Component({ - selector: 'ds-journal-issue-page-fields', - styleUrls: ['./journal-issue-page-fields.component.scss'], - templateUrl: './journal-issue-page-fields.component.html' + selector: 'ds-journal-issue', + styleUrls: ['./journal-issue.component.scss'], + templateUrl: './journal-issue.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Journal Issue */ -export class JournalIssuePageFieldsComponent extends EntityPageFieldsComponent { +export class JournalIssueComponent extends EntityComponent { /** * The volumes related to this journal issue */ diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.html rename to src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.html diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts similarity index 77% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts index 16d1689a04..0c664e5a4a 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { JournalVolumePageFieldsComponent } from './journal-volume-page-fields.component'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { JournalVolumeComponent } from './journal-volume.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -27,4 +27,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('JournalVolumePageFieldsComponent', getEntityPageFieldsTest(mockItem, JournalVolumePageFieldsComponent)); +describe('JournalVolumeComponent', getEntityPageFieldsTest(mockItem, JournalVolumeComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts similarity index 79% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts index 7549fbd2ff..4a730e7e61 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts @@ -5,22 +5,19 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('JournalVolume', ElementViewMode.Full) @Component({ - selector: 'ds-journal-volume-page-fields', - styleUrls: ['./journal-volume-page-fields.component.scss'], - templateUrl: './journal-volume-page-fields.component.html' + selector: 'ds-journal-volume', + styleUrls: ['./journal-volume.component.scss'], + templateUrl: './journal-volume.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Journal Volume */ -export class JournalVolumePageFieldsComponent extends EntityPageFieldsComponent { +export class JournalVolumeComponent extends EntityComponent { /** * The journals related to this journal volume */ diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html b/src/app/+item-page/simple/entity-types/journal/journal.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.html rename to src/app/+item-page/simple/entity-types/journal/journal.component.html diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss b/src/app/+item-page/simple/entity-types/journal/journal.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/journal/journal.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts similarity index 86% rename from src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts index 9e743c1426..ffbf465890 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts @@ -14,10 +14,10 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { JournalPageFieldsComponent } from './journal-page-fields.component'; +import { JournalComponent } from './journal.component'; -let comp: JournalPageFieldsComponent; -let fixture: ComponentFixture; +let comp: JournalComponent; +let fixture: ComponentFixture; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -39,7 +39,7 @@ const mockItem: Item = Object.assign(new Item(), { }] }); -describe('JournalPageFieldsComponent', () => { +describe('JournalComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot({ @@ -48,7 +48,7 @@ describe('JournalPageFieldsComponent', () => { useClass: MockTranslateLoader } })], - declarations: [JournalPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + declarations: [JournalComponent, GenericItemPageFieldComponent, TruncatePipe], providers: [ {provide: ITEM, useValue: mockItem}, {provide: ItemDataService, useValue: {}}, @@ -56,13 +56,13 @@ describe('JournalPageFieldsComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(JournalPageFieldsComponent, { + }).overrideComponent(JournalComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(JournalPageFieldsComponent); + fixture = TestBed.createComponent(JournalComponent); comp = fixture.componentInstance; fixture.detectChanges(); })); diff --git a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts b/src/app/+item-page/simple/entity-types/journal/journal.component.ts similarity index 76% rename from src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/journal/journal.component.ts index 93e2b3bfd2..78a8b54fdf 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal.component.ts @@ -5,22 +5,19 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('Journal', ElementViewMode.Full) @Component({ - selector: 'ds-journal-page-fields', - styleUrls: ['./journal-page-fields.component.scss'], - templateUrl: './journal-page-fields.component.html' + selector: 'ds-journal', + styleUrls: ['./journal.component.scss'], + templateUrl: './journal.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Journal */ -export class JournalPageFieldsComponent extends EntityPageFieldsComponent { +export class JournalComponent extends EntityComponent { /** * The volumes related to this journal */ diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.html rename to src/app/+item-page/simple/entity-types/orgunit/orgunit.component.html diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.scss b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/orgunit/orgunit.component.scss diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts similarity index 82% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts index 2976894a5e..7f270f2758 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { OrgUnitPageFieldsComponent } from './orgunit-page-fields.component'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { OrgunitComponent } from './orgunit.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -37,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('OrgUnitPageFieldsComponent', getEntityPageFieldsTest(mockItem, OrgUnitPageFieldsComponent)); +describe('OrgUnitComponent', getEntityPageFieldsTest(mockItem, OrgunitComponent)); diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts similarity index 82% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts index 28385b5d54..7e88c2dbf0 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts @@ -5,22 +5,19 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('OrgUnit', ElementViewMode.Full) @Component({ - selector: 'ds-orgunit-page-fields', - styleUrls: ['./orgunit-page-fields.component.scss'], - templateUrl: './orgunit-page-fields.component.html' + selector: 'ds-orgunit', + styleUrls: ['./orgunit.component.scss'], + templateUrl: './orgunit.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Organisation Unit */ -export class OrgUnitPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { +export class OrgunitComponent extends EntityComponent implements OnInit { /** * The people related to this organisation unit */ diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.html b/src/app/+item-page/simple/entity-types/person/person.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/person/person-page-fields.component.html rename to src/app/+item-page/simple/entity-types/person/person.component.html diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.scss b/src/app/+item-page/simple/entity-types/person/person.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/person/person-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/person/person.component.scss diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/person/person.component.spec.ts similarity index 84% rename from src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/person/person.component.spec.ts index b95c3451e7..328329db0e 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/person/person.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { PersonPageFieldsComponent } from './person-page-fields.component'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { PersonComponent } from './person.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -47,4 +47,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('PersonPageFieldsComponent', getEntityPageFieldsTest(mockItem, PersonPageFieldsComponent)); +describe('PersonComponent', getEntityPageFieldsTest(mockItem, PersonComponent)); diff --git a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts b/src/app/+item-page/simple/entity-types/person/person.component.ts similarity index 86% rename from src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/person/person.component.ts index 8c502a6ccf..dc6158a2c2 100644 --- a/src/app/+item-page/simple/entity-types/person/person-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person.component.ts @@ -5,23 +5,20 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('Person', ElementViewMode.Full) @Component({ - selector: 'ds-person-page-fields', - styleUrls: ['./person-page-fields.component.scss'], - templateUrl: './person-page-fields.component.html' + selector: 'ds-person', + styleUrls: ['./person.component.scss'], + templateUrl: './person.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Person */ -export class PersonPageFieldsComponent extends EntityPageFieldsComponent { +export class PersonComponent extends EntityComponent { /** * The publications related to this person */ diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.html b/src/app/+item-page/simple/entity-types/project/project.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/project/project-page-fields.component.html rename to src/app/+item-page/simple/entity-types/project/project.component.html diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.scss b/src/app/+item-page/simple/entity-types/project/project.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/project/project-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/project/project.component.scss diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/project/project.component.spec.ts similarity index 82% rename from src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/project/project.component.spec.ts index b0f0f1f80d..8fcbe9815b 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/project/project.component.spec.ts @@ -3,8 +3,8 @@ import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { ProjectPageFieldsComponent } from './project-page-fields.component'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity-page-fields.component.spec'; +import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { ProjectComponent } from './project.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -37,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('ProjectPageFieldsComponent', getEntityPageFieldsTest(mockItem, ProjectPageFieldsComponent)); +describe('ProjectComponent', getEntityPageFieldsTest(mockItem, ProjectComponent)); diff --git a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts b/src/app/+item-page/simple/entity-types/project/project.component.ts similarity index 82% rename from src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/project/project.component.ts index 54bed63e6d..142602de67 100644 --- a/src/app/+item-page/simple/entity-types/project/project-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project.component.ts @@ -5,22 +5,19 @@ import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; -import { - EntityPageFieldsComponent, filterRelationsByTypeLabel, - relationsToItems -} from '../shared/entity-page-fields.component'; import { isNotEmpty } from '../../../../shared/empty.util'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('Project', ElementViewMode.Full) @Component({ - selector: 'ds-project-page-fields', - styleUrls: ['./project-page-fields.component.scss'], - templateUrl: './project-page-fields.component.html' + selector: 'ds-project', + styleUrls: ['./project.component.scss'], + templateUrl: './project.component.html' }) /** * The component for displaying metadata and relations of an item with entity type Project */ -export class ProjectPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { +export class ProjectComponent extends EntityComponent implements OnInit { /** * The people related to this project */ diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html b/src/app/+item-page/simple/entity-types/publication/publication.component.html similarity index 100% rename from src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.html rename to src/app/+item-page/simple/entity-types/publication/publication.component.html diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.scss b/src/app/+item-page/simple/entity-types/publication/publication.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.scss rename to src/app/+item-page/simple/entity-types/publication/publication.component.scss diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts similarity index 85% rename from src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts index 29e421b2b9..acb361c532 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts @@ -8,14 +8,14 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { PublicationPageFieldsComponent } from './publication-page-fields.component'; import { Item } from '../../../../core/shared/item.model'; import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable } from '../shared/entity-page-fields.component.spec'; import { By } from '@angular/platform-browser'; +import { createRelationshipsObservable } from '../shared/entity.component.spec'; +import { PublicationComponent } from './publication.component'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -23,9 +23,9 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('PublicationPageFieldsComponent', () => { - let comp: PublicationPageFieldsComponent; - let fixture: ComponentFixture; +describe('PublicationComponent', () => { + let comp: PublicationComponent; + let fixture: ComponentFixture; const searchFixedFilterServiceStub = { /* tslint:disable:no-empty */ @@ -41,7 +41,7 @@ describe('PublicationPageFieldsComponent', () => { useClass: MockTranslateLoader } })], - declarations: [PublicationPageFieldsComponent, GenericItemPageFieldComponent, TruncatePipe], + declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe], providers: [ {provide: ITEM, useValue: mockItem}, {provide: ItemDataService, useValue: {}}, @@ -50,13 +50,13 @@ describe('PublicationPageFieldsComponent', () => { ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(PublicationPageFieldsComponent, { + }).overrideComponent(PublicationComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(PublicationPageFieldsComponent); + fixture = TestBed.createComponent(PublicationComponent); comp = fixture.componentInstance; fixture.detectChanges(); })); diff --git a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts b/src/app/+item-page/simple/entity-types/publication/publication.component.ts similarity index 83% rename from src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/publication/publication.component.ts index 8f2d980ec3..cfa68f0c5f 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication.component.ts @@ -8,20 +8,17 @@ import { } from '../../../../shared/entities/entity-type-decorator'; import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { - EntityPageFieldsComponent, - filterRelationsByTypeLabel, relationsToItems -} from '../shared/entity-page-fields.component'; +import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; @rendersEntityType('Publication', ElementViewMode.Full) @rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full) @Component({ - selector: 'ds-publication-page-fields', - styleUrls: ['./publication-page-fields.component.scss'], - templateUrl: './publication-page-fields.component.html', + selector: 'ds-publication', + styleUrls: ['./publication.component.scss'], + templateUrl: './publication.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PublicationPageFieldsComponent extends EntityPageFieldsComponent implements OnInit { +export class PublicationComponent extends EntityComponent implements OnInit { /** * The authors related to this publication */ diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts b/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts similarity index 99% rename from src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts rename to src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts index 4791101244..a171906c45 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts @@ -17,7 +17,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { Relationship } from '../../../../core/shared/entities/relationship.model'; import { Observable } from 'rxjs/Observable'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { compareArraysUsing, compareArraysUsingIds } from './entity-page-fields.component'; +import { compareArraysUsing, compareArraysUsingIds } from './entity.component'; /** * Create a generic test for an entity-page-fields component using a mockItem and the type of component @@ -100,7 +100,7 @@ export function createRelationshipsObservable() { }) ]))); } -describe('EntityPageFieldsComponent', () => { +describe('EntityComponent', () => { const arr1 = [ { id: 1, diff --git a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts b/src/app/+item-page/simple/entity-types/shared/entity.component.ts similarity index 98% rename from src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts rename to src/app/+item-page/simple/entity-types/shared/entity.component.ts index 0f6214fc15..5457c48142 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity-page-fields.component.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity.component.ts @@ -83,13 +83,13 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) => ); @Component({ - selector: 'ds-entity-page-fields', + selector: 'ds-entity', template: '' }) /** * A generic component for displaying metadata and relations of an item */ -export class EntityPageFieldsComponent implements OnInit { +export class EntityComponent implements OnInit { /** * Resolved relationships and types together in one observable */ diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts index 6b62811227..7685964e1c 100644 --- a/src/app/+item-page/simple/item-page.component.spec.ts +++ b/src/app/+item-page/simple/item-page.component.spec.ts @@ -1,7 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; -import { TruncatePipe } from '../../shared/utils/truncate.pipe'; import { ItemDataService } from '../../core/data/item-data.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ItemPageComponent } from './item-page.component'; @@ -14,9 +13,9 @@ import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { PaginatedList } from '../../core/data/paginated-list'; import { PageInfo } from '../../core/shared/page-info.model'; -import { createRelationshipsObservable } from './entity-types/shared/entity-page-fields.component.spec'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; +import { createRelationshipsObservable } from './entity-types/shared/entity.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts index 6bf4c99e5d..0f8361499e 100644 --- a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts +++ b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts @@ -6,8 +6,8 @@ import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; -import { createRelationshipsObservable } from '../entity-types/shared/entity-page-fields.component.spec'; import { By } from '@angular/platform-browser'; +import { createRelationshipsObservable } from '../entity-types/shared/entity.component.spec'; const mockItem1: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts index ccf7307216..59c8630f01 100644 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts @@ -6,8 +6,8 @@ import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; -import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; import { By } from '@angular/platform-browser'; +import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts index 419622df77..edfaa7f4f7 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts @@ -8,9 +8,9 @@ import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index f71acda02c..9efe7fed90 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -3,13 +3,13 @@ import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity-page-fields.component.spec'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { ItemSearchResultListElementComponent } from './item-search-result-list-element.component'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; const mockItem: Item = Object.assign(new Item(), { bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), From c5eb4b701d424cbe964bed3e3e9a6f43bac4702b Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Thu, 29 Nov 2018 13:50:09 +0100 Subject: [PATCH 072/205] add link to getBitstreamsByBundleName PR --- src/app/core/shared/item.model.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 21de4884f5..1e7c0acc85 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -91,6 +91,7 @@ export class Item extends DSpaceObject { * @param bundleName The name of the Bundle that should be returned * @returns {Observable} the bitstreams with the given bundleName * TODO now that bitstreams can be paginated this should move to the server + * see https://github.com/DSpace/dspace-angular/issues/332 */ getBitstreamsByBundleName(bundleName: string): Observable { return this.bitstreams From a7bebb10350c6bdb966122d19fb7502dd35785d9 Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 10 Dec 2018 13:48:48 +0100 Subject: [PATCH 073/205] upgrade to angular 6 --- .../full/full-item-page.component.spec.ts | 6 +++--- .../+item-page/full/full-item-page.component.ts | 3 +-- .../journal-issue.component.spec.ts | 5 +++-- .../journal-issue/journal-issue.component.ts | 2 +- .../journal-volume.component.spec.ts | 5 +++-- .../journal-volume/journal-volume.component.ts | 2 +- .../journal/journal.component.spec.ts | 5 +++-- .../entity-types/journal/journal.component.ts | 2 +- .../orgunit/orgunit.component.spec.ts | 5 +++-- .../entity-types/orgunit/orgunit.component.ts | 2 +- .../entity-types/person/person.component.spec.ts | 5 +++-- .../entity-types/person/person.component.ts | 4 ++-- .../project/project.component.spec.ts | 5 +++-- .../entity-types/project/project.component.ts | 2 +- .../publication/publication.component.spec.ts | 5 +++-- .../publication/publication.component.ts | 2 +- .../entity-types/shared/entity.component.spec.ts | 7 ++++--- .../entity-types/shared/entity.component.ts | 12 +++++------- .../item-page-field.component.spec.ts | 5 +++-- .../simple/item-page.component.spec.ts | 11 ++++++----- .../related-entities.component.spec.ts | 6 +++--- .../filtered-search-page.component.ts | 2 +- .../+search-page/filtered-search-page.guard.ts | 2 +- .../search-filter/search-filter.service.ts | 16 ++++++++-------- .../search-fixed-filter.service.spec.ts | 6 +++--- .../search-filter/search-fixed-filter.service.ts | 4 ++-- src/app/+search-page/search-page.component.ts | 4 +--- .../cache/builders/remote-data-build.service.ts | 3 +-- src/app/core/data/request.service.spec.ts | 2 +- .../shared/entities/relationship-type.model.ts | 2 +- .../core/shared/entities/relationship.model.ts | 2 +- src/app/core/shared/item.model.ts | 2 +- .../entity-type-switcher.component.spec.ts | 5 +++-- src/app/shared/form/form.component.spec.ts | 2 +- .../entity-list-element.component.spec.ts | 5 +++-- .../entity-search-result.component.spec.ts | 5 +++-- .../journal-issue-list-element.component.spec.ts | 7 ++++--- ...journal-volume-list-element.component.spec.ts | 7 ++++--- .../journal-list-element.component.spec.ts | 7 ++++--- .../orgunit-list-element.component.spec.ts | 7 ++++--- .../person/person-list-element.component.spec.ts | 7 ++++--- .../project-list-element.component.spec.ts | 7 ++++--- .../publication-list-element.component.spec.ts | 7 ++++--- ...-search-result-list-element.component.spec.ts | 5 +++-- src/app/shared/services/route.service.ts | 6 +++--- src/app/shared/shared.module.ts | 2 ++ src/app/shared/testing/router-stub.ts | 5 ++--- 47 files changed, 123 insertions(+), 107 deletions(-) diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts index c2107831fa..3377dec6db 100644 --- a/src/app/+item-page/full/full-item-page.component.spec.ts +++ b/src/app/+item-page/full/full-item-page.component.spec.ts @@ -14,12 +14,12 @@ import { Item } from '../../core/shared/item.model'; import { PageInfo } from '../../core/shared/page-info.model'; import { PaginatedList } from '../../core/data/paginated-list'; import { RemoteData } from '../../core/data/remote-data'; -import { Observable } from 'rxjs/Observable'; +import { of as observableOf } from 'rxjs'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'dc.title', @@ -28,7 +28,7 @@ const mockItem: Item = Object.assign(new Item(), { }] }); const routeStub = Object.assign(new ActivatedRouteStub(), { - data: Observable.of({ item: new RemoteData(false, false, true, null, mockItem) }) + data: observableOf({ item: new RemoteData(false, false, true, null, mockItem) }) }); const metadataServiceStub = { /* tslint:disable:no-empty */ diff --git a/src/app/+item-page/full/full-item-page.component.ts b/src/app/+item-page/full/full-item-page.component.ts index a439bfc632..a592a48eb3 100644 --- a/src/app/+item-page/full/full-item-page.component.ts +++ b/src/app/+item-page/full/full-item-page.component.ts @@ -2,7 +2,7 @@ import {filter, map} from 'rxjs/operators'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; +import { Observable , BehaviorSubject } from 'rxjs'; import { ItemPageComponent } from '../simple/item-page.component'; import { Metadatum } from '../../core/shared/metadatum.model'; @@ -15,7 +15,6 @@ import { MetadataService } from '../../core/metadata/metadata.service'; import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; /** * This component renders a simple item page. diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts index 19de0ebff1..a2db07362d 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts @@ -1,13 +1,14 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; import { JournalIssueComponent } from './journal-issue.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'journalissue.identifier.number', diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts index 445d061699..91d4b1ce8c 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts index 0c664e5a4a..2cab243924 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts @@ -1,13 +1,14 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; import { JournalVolumeComponent } from './journal-volume.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'journalvolume.identifier.volume', diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts index 4a730e7e61..c6b9233ece 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts +++ b/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts b/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts index ffbf465890..d9915e0410 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts @@ -4,7 +4,7 @@ import { TruncatableService } from '../../../../shared/truncatable/truncatable.s import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { ItemDataService } from '../../../../core/data/item-data.service'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { By } from '@angular/platform-browser'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -15,12 +15,13 @@ import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { isNotEmpty } from '../../../../shared/empty.util'; import { JournalComponent } from './journal.component'; +import { of as observableOf } from 'rxjs'; let comp: JournalComponent; let fixture: ComponentFixture; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'journal.identifier.issn', diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.ts b/src/app/+item-page/simple/entity-types/journal/journal.component.ts index 78a8b54fdf..7e1e0233b6 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.ts +++ b/src/app/+item-page/simple/entity-types/journal/journal.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts index 7f270f2758..e7e83ccc2a 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts @@ -1,13 +1,14 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; import { OrgunitComponent } from './orgunit.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'orgunit.identifier.dateestablished', diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts index 7e88c2dbf0..1f99f29ea2 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts +++ b/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; diff --git a/src/app/+item-page/simple/entity-types/person/person.component.spec.ts b/src/app/+item-page/simple/entity-types/person/person.component.spec.ts index 328329db0e..70eba79180 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/person/person.component.spec.ts @@ -1,13 +1,14 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; import { PersonComponent } from './person.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'person.identifier.email', diff --git a/src/app/+item-page/simple/entity-types/person/person.component.ts b/src/app/+item-page/simple/entity-types/person/person.component.ts index dc6158a2c2..2ba3a5bdcf 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.ts +++ b/src/app/+item-page/simple/entity-types/person/person.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable , of as observableOf } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; @@ -71,7 +71,7 @@ export class PersonComponent extends EntityComponent { ); this.fixedFilterQuery = this.fixedFilterService.getQueryByRelations('isAuthorOfPublication', this.item.id); - this.fixedFilter$ = Observable.of('publication'); + this.fixedFilter$ = observableOf('publication'); } } } diff --git a/src/app/+item-page/simple/entity-types/project/project.component.spec.ts b/src/app/+item-page/simple/entity-types/project/project.component.spec.ts index 8fcbe9815b..c2fceb4266 100644 --- a/src/app/+item-page/simple/entity-types/project/project.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/project/project.component.spec.ts @@ -1,13 +1,14 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; import { ProjectComponent } from './project.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'project.identifier.status', diff --git a/src/app/+item-page/simple/entity-types/project/project.component.ts b/src/app/+item-page/simple/entity-types/project/project.component.ts index 142602de67..e31d748129 100644 --- a/src/app/+item-page/simple/entity-types/project/project.component.ts +++ b/src/app/+item-page/simple/entity-types/project/project.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts index acb361c532..c3c9c2cd81 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts @@ -9,16 +9,17 @@ import { SearchFixedFilterService } from '../../../../+search-page/search-filter import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../shared/entity.component.spec'; import { PublicationComponent } from './publication.component'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.ts b/src/app/+item-page/simple/entity-types/publication/publication.component.ts index cfa68f0c5f..09481362c1 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/entity-types/publication/publication.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { diff --git a/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts b/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts index a171906c45..9565da1e7d 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts @@ -15,9 +15,10 @@ import { RelationshipType } from '../../../../core/shared/entities/relationship- import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; import { Relationship } from '../../../../core/shared/entities/relationship.model'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { compareArraysUsing, compareArraysUsingIds } from './entity.component'; +import { of as observableOf } from 'rxjs'; /** * Create a generic test for an entity-page-fields component using a mockItem and the type of component @@ -94,9 +95,9 @@ export function containsFieldInput(fields: DebugElement[], metadataKey: string): } export function createRelationshipsObservable() { - return Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [ + return observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [ Object.assign(new Relationship(), { - relationshipType: Observable.of(new RemoteData(false, false, true, null, new RelationshipType())) + relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType())) }) ]))); } diff --git a/src/app/+item-page/simple/entity-types/shared/entity.component.ts b/src/app/+item-page/simple/entity-types/shared/entity.component.ts index 5457c48142..616c14269b 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity.component.ts +++ b/src/app/+item-page/simple/entity-types/shared/entity.component.ts @@ -1,5 +1,5 @@ import { Component, Inject, OnInit } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; @@ -65,7 +65,7 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) => (source: Observable): Observable => source.pipe( flatMap((rels: Relationship[]) => - Observable.zip( + observableZip( ...rels.map((rel: Relationship) => { let queryId = rel.leftId; if (rel.leftId === thisId) { @@ -111,16 +111,14 @@ export class EntityComponent implements OnInit { const relTypesCurrentPage$ = relsCurrentPage$.pipe( flatMap((rels: Relationship[]) => - Observable.zip( - ...rels.map((rel: Relationship) => rel.relationshipType), - (...arr: Array>) => - arr.map((d: RemoteData) => d.payload) + observableZip(...rels.map((rel: Relationship) => rel.relationshipType)).pipe( + map(([...arr]: Array>) => arr.map((d: RemoteData) => d.payload)) ) ), distinctUntilChanged(compareArraysUsingIds()) ); - this.resolvedRelsAndTypes$ = Observable.combineLatest( + this.resolvedRelsAndTypes$ = observableCombineLatest( relsCurrentPage$, relTypesCurrentPage$ ); diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts index daa1e64ac5..a0d24387cf 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts @@ -4,11 +4,12 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Item } from '../../../../core/shared/item.model'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { ItemPageFieldComponent } from './item-page-field.component'; import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component'; +import { of as observableOf } from 'rxjs'; let comp: ItemPageFieldComponent; let fixture: ComponentFixture; @@ -50,7 +51,7 @@ describe('ItemPageFieldComponent', () => { export function mockItemWithMetadataFieldAndValue(field: string, value: string): Item { return Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: field, diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts index 7685964e1c..bc1df00300 100644 --- a/src/app/+item-page/simple/item-page.component.spec.ts +++ b/src/app/+item-page/simple/item-page.component.spec.ts @@ -8,7 +8,7 @@ import { ActivatedRoute } from '@angular/router'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; import { MetadataService } from '../../core/metadata/metadata.service'; import { VarDirective } from '../../shared/utils/var.directive'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { PaginatedList } from '../../core/data/paginated-list'; @@ -16,9 +16,10 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from './entity-types/shared/entity.component.spec'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); @@ -33,7 +34,7 @@ describe('ItemPageComponent', () => { /* tslint:enable:no-empty */ }; const mockRoute = Object.assign(new ActivatedRouteStub(), { - data: Observable.of({ item: new RemoteData(false, false, true, null, mockItem) }) + data: observableOf({ item: new RemoteData(false, false, true, null, mockItem) }) }); beforeEach(async(() => { @@ -65,7 +66,7 @@ describe('ItemPageComponent', () => { describe('when the item is loading', () => { beforeEach(() => { - comp.itemRD$ = Observable.of(new RemoteData(true, true, true, null, undefined)); + comp.itemRD$ = observableOf(new RemoteData(true, true, true, null, undefined)); fixture.detectChanges(); }); @@ -77,7 +78,7 @@ describe('ItemPageComponent', () => { describe('when the item failed loading', () => { beforeEach(() => { - comp.itemRD$ = Observable.of(new RemoteData(false, false, false, null, undefined)); + comp.itemRD$ = observableOf(new RemoteData(false, false, false, null, undefined)); fixture.detectChanges(); }); diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts index 0f8361499e..a8897c1e88 100644 --- a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts +++ b/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts @@ -2,20 +2,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { RelatedEntitiesComponent } from './related-entities-component'; import { Item } from '../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../entity-types/shared/entity.component.spec'; +import { of as observableOf } from 'rxjs'; const mockItem1: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); const mockItem2: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); diff --git a/src/app/+search-page/filtered-search-page.component.ts b/src/app/+search-page/filtered-search-page.component.ts index 6e2875ee39..21e8b8c1c1 100644 --- a/src/app/+search-page/filtered-search-page.component.ts +++ b/src/app/+search-page/filtered-search-page.component.ts @@ -7,7 +7,7 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { pushInOut } from '../shared/animations/push'; import { RouteService } from '../shared/services/route.service'; import { SearchConfigurationService } from './search-service/search-configuration.service'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { PaginatedSearchOptions } from './paginated-search-options.model'; /** diff --git a/src/app/+search-page/filtered-search-page.guard.ts b/src/app/+search-page/filtered-search-page.guard.ts index 7d022b81da..548e4d8dce 100644 --- a/src/app/+search-page/filtered-search-page.guard.ts +++ b/src/app/+search-page/filtered-search-page.guard.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; @Injectable() diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 08fb0c5514..e0c189e26f 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -1,6 +1,6 @@ import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { mergeMap, map, distinctUntilChanged } from 'rxjs/operators'; import { Injectable, InjectionToken } from '@angular/core'; -import { map, distinctUntilChanged } from 'rxjs/operators'; import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { @@ -82,12 +82,12 @@ export class SearchFilterService { getCurrentPagination(pagination: any = {}): Observable { const page$ = this.routeService.getQueryParameterValue('page'); const size$ = this.routeService.getQueryParameterValue('pageSize'); - return Observable.combineLatest(page$, size$, (page, size) => { + return observableCombineLatest(page$, size$).pipe(map(([page, size]) => { return Object.assign(new PaginationComponentOptions(), pagination, { currentPage: page || 1, pageSize: size || pagination.pageSize }); - }); + })) } /** @@ -99,12 +99,12 @@ export class SearchFilterService { getCurrentSort(defaultSort: SortOptions): Observable { const sortDirection$ = this.routeService.getQueryParameterValue('sortDirection'); const sortField$ = this.routeService.getQueryParameterValue('sortField'); - return Observable.combineLatest(sortDirection$, sortField$, (sortDirection, sortField) => { + return observableCombineLatest(sortDirection$, sortField$).pipe(map(([sortDirection, sortField]) => { const field = sortField || defaultSort.field; const direction = SortDirection[sortDirection] || defaultSort.direction; return new SortOptions(field, direction) } - ); + )) } /** @@ -121,7 +121,7 @@ export class SearchFilterService { */ getCurrentFixedFilter(): Observable { const filter: Observable = this.routeService.getRouteParameterValue('filter'); - return filter.flatMap((f) => this.fixedFilterService.getQueryByFilterName(f)); + return filter.pipe(mergeMap((f) => this.fixedFilterService.getQueryByFilterName(f))); } /** @@ -139,7 +139,7 @@ export class SearchFilterService { * @returns {Observable} */ getPaginatedSearchOptions(defaults: any = {}): Observable { - return Observable.combineLatest( + return observableCombineLatest( this.getCurrentPagination(defaults.pagination), this.getCurrentSort(defaults.sort), this.getCurrentView(), @@ -170,7 +170,7 @@ export class SearchFilterService { * @returns {Observable} */ getSearchOptions(defaults: any = {}): Observable { - return Observable.combineLatest( + return observableCombineLatest( this.getCurrentView(), this.getCurrentScope(), this.getCurrentQuery(), diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts index 9f745d4ca5..06e12d444e 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts @@ -6,7 +6,7 @@ import { ResponseCacheService } from '../../../core/cache/response-cache.service import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response-cache.models'; import { PageInfo } from '../../../core/shared/page-info.model'; -import { Observable } from 'rxjs/Observable'; +import { of as observableOf } from 'rxjs'; describe('SearchFixedFilterService', () => { let service: SearchFixedFilterService; @@ -21,12 +21,12 @@ describe('SearchFixedFilterService', () => { generateRequestId: () => 'fake-id' }) as RequestService; const responseCacheStub = Object.assign(new ResponseCacheService(undefined), { - get: () => Observable.of(Object.assign(new ResponseCacheEntry(), { + get: () => observableOf(Object.assign(new ResponseCacheEntry(), { response: new FilteredDiscoveryQueryResponse(filterQuery, '200', new PageInfo()) })) }); const halServiceStub = Object.assign(new HALEndpointService(responseCacheStub, requestServiceStub, undefined), { - getEndpoint: () => Observable.of('fake-url') + getEndpoint: () => observableOf('fake-url') }); beforeEach(() => { diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index b156aada01..1e559b848d 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { flatMap, map } from 'rxjs/operators'; -import { Observable } from 'rxjs/Observable'; +import { Observable , of as observableOf } from 'rxjs'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { GetRequest, RestRequest } from '../../../core/data/request.models'; import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response-cache.models'; @@ -61,7 +61,7 @@ export class SearchFixedFilterService { return filterQuery; } - return Observable.of(undefined); + return observableOf(undefined); } /** diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 95d63d082e..8db94bc8d2 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -1,5 +1,5 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable , Subscription , BehaviorSubject } from 'rxjs'; import { switchMap, } from 'rxjs/operators'; import { PaginatedList } from '../core/data/paginated-list'; import { RemoteData } from '../core/data/remote-data'; @@ -11,9 +11,7 @@ import { SearchFilterService } from './search-filters/search-filter/search-filte import { SearchResult } from './search-result.model'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; -import { Subscription } from 'rxjs/Subscription'; import { hasValue, isNotEmpty } from '../shared/empty.util'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { getSucceededRemoteData } from '../core/shared/operators'; import { RouteService } from '../shared/services/route.service'; diff --git a/src/app/core/cache/builders/remote-data-build.service.ts b/src/app/core/cache/builders/remote-data-build.service.ts index d4863d98c6..aa622b20c5 100644 --- a/src/app/core/cache/builders/remote-data-build.service.ts +++ b/src/app/core/cache/builders/remote-data-build.service.ts @@ -5,8 +5,7 @@ import { race as observableRace } from 'rxjs'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; -import { distinctUntilChanged, filter, flatMap, map, startWith, switchMap } from 'rxjs/operators'; +import { distinctUntilChanged, flatMap, map, startWith } from 'rxjs/operators'; import { hasValue, hasValueOperator, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { PaginatedList } from '../../data/paginated-list'; import { RemoteData } from '../../data/remote-data'; diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index 39f29a9beb..7b86ed8e67 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -22,7 +22,7 @@ import { import { RequestService } from './request.service'; import { ActionsSubject, Store } from '@ngrx/store'; import { TestScheduler } from 'rxjs/testing'; -import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { BehaviorSubject } from 'rxjs'; describe('RequestService', () => { let scheduler: TestScheduler; diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/entities/relationship-type.model.ts index 864d019d0e..05dd0cd754 100644 --- a/src/app/core/shared/entities/relationship-type.model.ts +++ b/src/app/core/shared/entities/relationship-type.model.ts @@ -1,4 +1,4 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { ResourceType } from '../resource-type'; diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/entities/relationship.model.ts index c1dc1a1971..ef76b0a32a 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/entities/relationship.model.ts @@ -1,4 +1,4 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { ResourceType } from '../resource-type'; diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 65d48fd6cd..e8646777cf 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -1,4 +1,4 @@ -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { filter, map, startWith, tap } from 'rxjs/operators'; import { DSpaceObject } from './dspace-object.model'; diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts index d22e4a1c2a..d6191b098a 100644 --- a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts +++ b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts @@ -1,7 +1,7 @@ import { EntityTypeSwitcherComponent } from './entity-type-switcher.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { PageInfo } from '../../../core/shared/page-info.model'; import { Item } from '../../../core/shared/item.model'; import { PaginatedList } from '../../../core/data/paginated-list'; @@ -9,10 +9,11 @@ import { RemoteData } from '../../../core/data/remote-data'; import * as decorator from '../entity-type-decorator'; import { ElementViewMode } from '../../view-mode'; import { getComponentByEntityType } from '../entity-type-decorator'; +import { of as observableOf } from 'rxjs'; const relationType = 'type'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/form/form.component.spec.ts b/src/app/shared/form/form.component.spec.ts index 06676d191e..bcd4fb19e5 100644 --- a/src/app/shared/form/form.component.spec.ts +++ b/src/app/shared/form/form.component.spec.ts @@ -23,7 +23,7 @@ import { MockStore } from '../testing/mock-store'; import { FormFieldMetadataValueObject } from './builder/models/form-field-metadata-value.model'; import { GLOBAL_CONFIG } from '../../../config'; import { createTestComponent } from '../testing/utils'; -import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; +import { BehaviorSubject } from 'rxjs'; let TEST_FORM_MODEL; diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts index 59c8630f01..b32db3df06 100644 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts @@ -2,15 +2,16 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { EntityListElementComponent } from './entity-list-element.component'; import { Item } from '../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity.component.spec'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts index edfaa7f4f7..e9a871985b 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts @@ -4,16 +4,17 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { EntitySearchResultComponent } from './entity-search-result-component'; import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts index 58a27ffa8b..07be07d051 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { JournalIssueListElementComponent } from './journal-issue-list-element.component'; +import { of as observableOf } from 'rxjs'; let journalIssueListElementComponent: JournalIssueListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -31,7 +32,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts index 9c7eb7a029..7388d12f25 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { JournalVolumeListElementComponent } from './journal-volume-list-element.component'; +import { of as observableOf } from 'rxjs'; let journalVolumeListElementComponent: JournalVolumeListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -31,7 +32,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts index bcf6cb7058..8fd8d870fa 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { JournalListElementComponent } from './journal-list-element.component'; +import { of as observableOf } from 'rxjs'; let journalListElementComponent: JournalListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -26,7 +27,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts index 2bc69e94fd..678e1e42bc 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { OrgUnitListElementComponent } from './orgunit-list-element.component'; +import { of as observableOf } from 'rxjs'; let orgUnitListElementComponent: OrgUnitListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -26,7 +27,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts index 60910474fd..3005c852af 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { PersonListElementComponent } from './person-list-element.component'; +import { of as observableOf } from 'rxjs'; let personListElementComponent: PersonListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -26,7 +27,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts index e0cba5f11e..b4437ceaea 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; import { ProjectListElementComponent } from './project-list-element.component'; +import { of as observableOf } from 'rxjs'; let projectListElementComponent: ProjectListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -26,7 +27,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts index 8bfc0d9077..9a2fdbcc0b 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts @@ -1,18 +1,19 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { PublicationListElementComponent } from './publication-list-element.component'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { of as observableOf } from 'rxjs'; let publicationListElementComponent: PublicationListElementComponent; let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', @@ -41,7 +42,7 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), { }] }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { - bitstreams: Observable.of({}), + bitstreams: observableOf({}), metadata: [ { key: 'dc.title', diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index ebe2096c67..38b0eb683d 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -1,5 +1,5 @@ import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; @@ -12,9 +12,10 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { - bitstreams: Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), + bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), metadata: [], relationships: createRelationshipsObservable() }); diff --git a/src/app/shared/services/route.service.ts b/src/app/shared/services/route.service.ts index 7b89cf2154..dbb967c766 100644 --- a/src/app/shared/services/route.service.ts +++ b/src/app/shared/services/route.service.ts @@ -1,6 +1,6 @@ import { distinctUntilChanged, map, mergeMap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs/Observable'; +import { Observable } from 'rxjs'; import { ActivatedRoute, Params, Router, } from '@angular/router'; @Injectable() @@ -41,11 +41,11 @@ export class RouteService { } getRouteParameterValue(paramName: string): Observable { - return this.params.map((params) => params[paramName]).distinctUntilChanged(); + return this.params.pipe(map((params) => params[paramName]),distinctUntilChanged(),); } getRouteDataValue(datafield: string): Observable { - return this.route.data.map((data) => data[datafield]).distinctUntilChanged(); + return this.route.data.pipe(map((data) => data[datafield]),distinctUntilChanged(),); } getQueryParamsWithPrefix(prefix: string): Observable { diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 40565251fa..106b330744 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -93,6 +93,7 @@ import { InputSuggestionsComponent } from './input-suggestions/input-suggestions import { CapitalizePipe } from './utils/capitalize.pipe'; import { ObjectKeysPipe } from './utils/object-keys-pipe'; import { MomentModule } from 'ngx-moment'; +import { NouisliderModule } from 'ng2-nouislider'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -108,6 +109,7 @@ const MODULES = [ NgbTimepickerModule, NgbTypeaheadModule, NgxPaginationModule, + NouisliderModule, ReactiveFormsModule, RouterModule, TranslateModule, diff --git a/src/app/shared/testing/router-stub.ts b/src/app/shared/testing/router-stub.ts index 5d74b33e2f..7e0315698c 100644 --- a/src/app/shared/testing/router-stub.ts +++ b/src/app/shared/testing/router-stub.ts @@ -1,11 +1,10 @@ -import { Observable } from 'rxjs/Observable'; - +import { of as observableOf } from 'rxjs'; export class RouterStub { url: string; //noinspection TypeScriptUnresolvedFunction navigate = jasmine.createSpy('navigate'); parseUrl = jasmine.createSpy('parseUrl'); - events = Observable.of({}); + events = observableOf({}); navigateByUrl(url): void { this.url = url; } From 6d44381982372e51b7419f6387ef6393c80d5d1d Mon Sep 17 00:00:00 2001 From: lotte Date: Mon, 10 Dec 2018 16:13:15 +0100 Subject: [PATCH 074/205] angular 6 upgrade --- .../paginated-search-options.model.ts | 2 +- .../search-fixed-filter.service.ts | 1 - .../search-page.component.spec.ts | 321 +++++++++--------- .../core/metadata/metadata.service.spec.ts | 4 +- .../entity-type-switcher.component.spec.ts | 8 +- ...arch-result-list-element.component.spec.ts | 4 +- 6 files changed, 168 insertions(+), 172 deletions(-) diff --git a/src/app/+search-page/paginated-search-options.model.ts b/src/app/+search-page/paginated-search-options.model.ts index 8f4d93b0df..387c116a56 100644 --- a/src/app/+search-page/paginated-search-options.model.ts +++ b/src/app/+search-page/paginated-search-options.model.ts @@ -12,7 +12,7 @@ export class PaginatedSearchOptions extends SearchOptions { pagination?: PaginationComponentOptions; sort?: SortOptions; - constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], pagination?: PaginationComponentOptions, sort?: SortOptions}) { + constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[], fixedFilter?: any, pagination?: PaginationComponentOptions, sort?: SortOptions}) { super(options); this.pagination = options.pagination; this.sort = options.sort; diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 1e559b848d..8f8997e63c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -58,7 +58,6 @@ export class SearchFixedFilterService { map((response: FilteredDiscoveryQueryResponse) => response.filterQuery )); - return filterQuery; } return observableOf(undefined); diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index df12917f31..68a0dc6352 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -22,183 +22,182 @@ import { SearchConfigurationService } from './search-service/search-configuratio import { RemoteData } from '../core/data/remote-data'; import { RouteService } from '../shared/services/route.service'; -describe('SearchPageComponent', () => { - let comp: SearchPageComponent; - let fixture: ComponentFixture; - let searchServiceObject: SearchService; - const store: Store = jasmine.createSpyObj('store', { - /* tslint:disable:no-empty */ - dispatch: {}, - /* tslint:enable:no-empty */ - select: observableOf(true) - }); - const pagination: PaginationComponentOptions = new PaginationComponentOptions(); - pagination.id = 'search-results-pagination'; - pagination.currentPage = 1; - pagination.pageSize = 10; - const sort: SortOptions = new SortOptions('score', SortDirection.DESC); - const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data'])); - const searchServiceStub = jasmine.createSpyObj('SearchService', { - search: mockResults, - getSearchLink: '/search', - getScopes: observableOf(['test-scope']) - }); - const queryParam = 'test query'; - const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; - const fixedFilter = 'fixed filter'; - const paginatedSearchOptions = { +let comp: SearchPageComponent; +let fixture: ComponentFixture; +let searchServiceObject: SearchService; +const store: Store = jasmine.createSpyObj('store', { + /* tslint:disable:no-empty */ + dispatch: {}, + /* tslint:enable:no-empty */ + select: observableOf(true) +}); +const pagination: PaginationComponentOptions = new PaginationComponentOptions(); +pagination.id = 'search-results-pagination'; +pagination.currentPage = 1; +pagination.pageSize = 10; +const sort: SortOptions = new SortOptions('score', SortDirection.DESC); +const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data'])); +const searchServiceStub = jasmine.createSpyObj('SearchService', { + search: mockResults, + getSearchLink: '/search', + getScopes: observableOf(['test-scope']) +}); +const queryParam = 'test query'; +const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +const fixedFilter = 'fixed filter'; +const paginatedSearchOptions = { + query: queryParam, + scope: scopeParam, + fixedFilter: fixedFilter, + pagination, + sort +}; +const activatedRouteStub = { + queryParams: observableOf({ query: queryParam, - scope: scopeParam, - fixedFilter: fixedFilter, - pagination, - sort - }; - const activatedRouteStub = { - queryParams: observableOf({ - query: queryParam, - scope: scopeParam - }) - }; - const sidebarService = { - isCollapsed: observableOf(true), - collapse: () => this.isCollapsed = observableOf(true), - expand: () => this.isCollapsed = observableOf(false) - }; + scope: scopeParam + }) +}; +const sidebarService = { + isCollapsed: observableOf(true), + collapse: () => this.isCollapsed = observableOf(true), + expand: () => this.isCollapsed = observableOf(false) +}; - const routeServiceStub = { - getRouteParameterValue: () => { - return observableOf(''); - } - }; - - export function configureSearchComponentTestingModule(compType) { - TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], - declarations: [compType], - providers: [ - { provide: SearchService, useValue: searchServiceStub }, - { - provide: CommunityDataService, - useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) - }, - { provide: ActivatedRoute, useValue: activatedRouteStub }, - { - provide: Store, useValue: store - }, - { - provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', - { - isXs: observableOf(true), - isSm: observableOf(false), - isXsOrSm: observableOf(true) - }) - }, - { - provide: SearchSidebarService, - useValue: sidebarService - }, - { - provide: SearchFilterService, - useValue: {} - }, - { - provide: SearchConfigurationService, - useValue: { - paginatedSearchOptions: hot('a', { - a: paginatedSearchOptions - }), - getCurrentScope: (a) => observableOf('test-id'), - /* tslint:disable:no-empty */ - updateFixedFilter: (newFilter) => { - } - /* tslint:enable:no-empty */ - } - }, - { - provide: RouteService, - useValue: routeServiceStub - } - ], - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(compType, { - set: { changeDetection: ChangeDetectionStrategy.Default } - }).compileComponents(); +const routeServiceStub = { + getRouteParameterValue: () => { + return observableOf(''); } +}; - describe('SearchPageComponent', () => { +export function configureSearchComponentTestingModule(compType) { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], + declarations: [compType], + providers: [ + { provide: SearchService, useValue: searchServiceStub }, + { + provide: CommunityDataService, + useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) + }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { + provide: Store, useValue: store + }, + { + provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', + { + isXs: observableOf(true), + isSm: observableOf(false), + isXsOrSm: observableOf(true) + }) + }, + { + provide: SearchSidebarService, + useValue: sidebarService + }, + { + provide: SearchFilterService, + useValue: {} + }, + { + provide: SearchConfigurationService, + useValue: { + paginatedSearchOptions: hot('a', { + a: paginatedSearchOptions + }), + getCurrentScope: (a) => observableOf('test-id'), + /* tslint:disable:no-empty */ + updateFixedFilter: (newFilter) => { + } + /* tslint:enable:no-empty */ + } + }, + { + provide: RouteService, + useValue: routeServiceStub + } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(compType, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); +} - beforeEach(async(() => { - configureSearchComponentTestingModule(SearchPageComponent); +describe('SearchPageComponent', () => { + + beforeEach(async(() => { + configureSearchComponentTestingModule(SearchPageComponent); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SearchPageComponent); + comp = fixture.componentInstance; // SearchPageComponent test instance + fixture.detectChanges(); + searchServiceObject = (comp as any).service; + }); + + it('should get the scope and query from the route parameters', () => { + expect(comp.searchOptions$).toBeObservable(cold('b', { + b: paginatedSearchOptions })); + }); + + describe('when the closeSidebar event is emitted clicked in mobile view', () => { beforeEach(() => { - fixture = TestBed.createComponent(SearchPageComponent); - comp = fixture.componentInstance; // SearchPageComponent test instance + spyOn(comp, 'closeSidebar'); + const closeSidebarButton = fixture.debugElement.query(By.css('#search-sidebar-sm')); + closeSidebarButton.triggerEventHandler('toggleSidebar', null); + }); + + it('should trigger the closeSidebar function', () => { + expect(comp.closeSidebar).toHaveBeenCalled(); + }); + + }); + + describe('when the open sidebar button is clicked in mobile view', () => { + + beforeEach(() => { + spyOn(comp, 'openSidebar'); + const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar')); + openSidebarButton.triggerEventHandler('click', null); + }); + + it('should trigger the openSidebar function', () => { + expect(comp.openSidebar).toHaveBeenCalled(); + }); + + }); + + describe('when sidebarCollapsed is true in mobile view', () => { + let menu: HTMLElement; + + beforeEach(() => { + menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; + comp.isSidebarCollapsed = () => observableOf(true); fixture.detectChanges(); - searchServiceObject = (comp as any).service; }); - it('should get the scope and query from the route parameters', () => { - expect(comp.searchOptions$).toBeObservable(cold('b', { - b: paginatedSearchOptions - })); + it('should close the sidebar', () => { + expect(menu.classList).not.toContain('active'); }); - describe('when the closeSidebar event is emitted clicked in mobile view', () => { + }); - beforeEach(() => { - spyOn(comp, 'closeSidebar'); - const closeSidebarButton = fixture.debugElement.query(By.css('#search-sidebar-sm')); - closeSidebarButton.triggerEventHandler('toggleSidebar', null); - }); - - it('should trigger the closeSidebar function', () => { - expect(comp.closeSidebar).toHaveBeenCalled(); - }); + describe('when sidebarCollapsed is false in mobile view', () => { + let menu: HTMLElement; + beforeEach(() => { + menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; + comp.isSidebarCollapsed = () => observableOf(false); + fixture.detectChanges(); }); - describe('when the open sidebar button is clicked in mobile view', () => { - - beforeEach(() => { - spyOn(comp, 'openSidebar'); - const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar')); - openSidebarButton.triggerEventHandler('click', null); - }); - - it('should trigger the openSidebar function', () => { - expect(comp.openSidebar).toHaveBeenCalled(); - }); - + it('should open the menu', () => { + expect(menu.classList).toContain('active'); }); - describe('when sidebarCollapsed is true in mobile view', () => { - let menu: HTMLElement; - - beforeEach(() => { - menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; - comp.isSidebarCollapsed = () => observableOf(true); - fixture.detectChanges(); - }); - - it('should close the sidebar', () => { - expect(menu.classList).not.toContain('active'); - }); - - }); - - describe('when sidebarCollapsed is false in mobile view', () => { - let menu: HTMLElement; - - beforeEach(() => { - menu = fixture.debugElement.query(By.css('#search-sidebar-sm')).nativeElement; - comp.isSidebarCollapsed = () => observableOf(false); - fixture.detectChanges(); - }); - - it('should open the menu', () => { - expect(menu.classList).toContain('active'); - }); - - }); - }) + }); +}); diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 2456ae9e55..006707d710 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -204,7 +204,7 @@ describe('MetadataService', () => { undefined, MockItem )); - } + }; const mockType = (mockItem: Item, type: string): Item => { const typedMockItem = Object.assign(new Item(), mockItem) as Item; @@ -215,7 +215,7 @@ describe('MetadataService', () => { } } return typedMockItem; - } + }; const mockPublisher = (mockItem: Item): Item => { const publishedMockItem = Object.assign(new Item(), mockItem) as Item; diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts index d6191b098a..053b03512a 100644 --- a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts +++ b/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts @@ -1,15 +1,15 @@ import { EntityTypeSwitcherComponent } from './entity-type-switcher.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { Observable } from 'rxjs'; +import { of as observableOf } from 'rxjs'; import { PageInfo } from '../../../core/shared/page-info.model'; import { Item } from '../../../core/shared/item.model'; import { PaginatedList } from '../../../core/data/paginated-list'; import { RemoteData } from '../../../core/data/remote-data'; import * as decorator from '../entity-type-decorator'; -import { ElementViewMode } from '../../view-mode'; import { getComponentByEntityType } from '../entity-type-decorator'; -import { of as observableOf } from 'rxjs'; +import { ElementViewMode } from '../../view-mode'; +import createSpy = jasmine.createSpy; const relationType = 'type'; const mockItem: Item = Object.assign(new Item(), { @@ -44,7 +44,7 @@ describe('EntityTypeSwitcherComponent', () => { comp = fixture.componentInstance; comp.object = mockItem; comp.viewMode = viewMode; - spyOn(decorator, 'getComponentByEntityType').and.returnValue('component'); + spyOnProperty(decorator, 'getComponentByEntityType').and.returnValue(createSpy('getComponentByEntityType')) })); describe('when calling getComponent', () => { diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index 38b0eb683d..b1f511868a 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -1,18 +1,16 @@ import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs'; +import { of as observableOf } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ItemSearchResultListElementComponent } from './item-search-result-list-element.component'; -import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; -import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), From 5ed2b7536a479a0814cfaefcbb966b4174f44b37 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 12 Dec 2018 14:53:26 +0100 Subject: [PATCH 075/205] fix an issue where an empty bitstream page would cause an error --- src/app/core/data/base-response-parsing.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 63468295d4..3a8424b7b2 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -14,7 +14,7 @@ function isObjectLevel(halObj: any) { } function isPaginatedResponse(halObj: any) { - return hasValue(halObj.page) && hasValue(halObj._embedded); + return hasValue(halObj.page); } /* tslint:disable:max-classes-per-file */ @@ -77,7 +77,9 @@ export abstract class BaseResponseParsingService { let list = data._embedded; // Workaround for inconsistency in rest response. Issue: https://github.com/DSpace/dspace-angular/issues/238 - if (!Array.isArray(list)) { + if (hasNoValue(list)) { + list = []; + } else if (!Array.isArray(list)) { list = this.flattenSingleKeyObject(list); } const page: ObjectDomain[] = this.processArray(list, requestHref); From 593dcf7d06762351c8927a6e7d51a1ab68635a6d Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 12 Dec 2018 16:46:22 +0100 Subject: [PATCH 076/205] fix an issue where metadata-view-elements containing only images would be hidden --- .../metadata-field-wrapper.component.html | 2 +- .../metadata-field-wrapper.component.spec.ts | 66 +++++++++++++------ .../metadata-field-wrapper.component.ts | 7 ++ 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html index bbe6d8d95b..c791cec600 100644 --- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html +++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.html @@ -1,4 +1,4 @@ -
+
{{ label }}
diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts index cce54edf64..d7e1b80c76 100644 --- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.spec.ts @@ -1,18 +1,41 @@ -import { ComponentFixture, TestBed, async } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { Component, DebugElement } from '@angular/core'; +import { Component } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MetadataFieldWrapperComponent } from './metadata-field-wrapper.component'; +/* tslint:disable:max-classes-per-file */ @Component({ - selector: 'ds-component-with-content', + selector: 'ds-component-without-content', template: '\n' + - '
\n' + - ' \n' + - '
\n' + '
' }) -class ContentComponent {} +class NoContentComponent {} + +@Component({ + selector: 'ds-component-with-empty-spans', + template: '\n' + + ' \n' + + ' \n' + + '' +}) +class SpanContentComponent {} + +@Component({ + selector: 'ds-component-with-text', + template: '\n' + + ' The quick brown fox jumps over the lazy dog\n' + + '' +}) +class TextContentComponent {} + +@Component({ + selector: 'ds-component-with-image', + template: '\n' + + ' an alt text\n' + + '' +}) +class ImgContentComponent {} +/* tslint:enable:max-classes-per-file */ describe('MetadataFieldWrapperComponent', () => { let component: MetadataFieldWrapperComponent; @@ -20,7 +43,7 @@ describe('MetadataFieldWrapperComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [MetadataFieldWrapperComponent, ContentComponent] + declarations: [MetadataFieldWrapperComponent, NoContentComponent, SpanContentComponent, TextContentComponent, ImgContentComponent] }).compileComponents(); })); @@ -30,23 +53,21 @@ describe('MetadataFieldWrapperComponent', () => { }); const wrapperSelector = '.simple-view-element'; - const labelSelector = '.simple-view-element-header'; - const contentSelector = '.my-content'; it('should create', () => { expect(component).toBeDefined(); }); it('should not show the component when there is no content', () => { - component.label = 'test label'; - fixture.detectChanges(); - const parentNative = fixture.nativeElement; + const parentFixture = TestBed.createComponent(NoContentComponent); + parentFixture.detectChanges(); + const parentNative = parentFixture.nativeElement; const nativeWrapper = parentNative.querySelector(wrapperSelector); expect(nativeWrapper.classList.contains('d-none')).toBe(true); }); - it('should not show the component when there is DOM content but no text', () => { - const parentFixture = TestBed.createComponent(ContentComponent); + it('should not show the component when there is DOM content but not text or an image', () => { + const parentFixture = TestBed.createComponent(SpanContentComponent); parentFixture.detectChanges(); const parentNative = parentFixture.nativeElement; const nativeWrapper = parentNative.querySelector(wrapperSelector); @@ -54,11 +75,18 @@ describe('MetadataFieldWrapperComponent', () => { }); it('should show the component when there is text content', () => { - const parentFixture = TestBed.createComponent(ContentComponent); + const parentFixture = TestBed.createComponent(TextContentComponent); + parentFixture.detectChanges(); + const parentNative = parentFixture.nativeElement; + const nativeWrapper = parentNative.querySelector(wrapperSelector); + parentFixture.detectChanges(); + expect(nativeWrapper.classList.contains('d-none')).toBe(false); + }); + + it('should show the component when there is img content', () => { + const parentFixture = TestBed.createComponent(ImgContentComponent); parentFixture.detectChanges(); const parentNative = parentFixture.nativeElement; - const nativeContent = parentNative.querySelector(contentSelector); - nativeContent.textContent = 'lorem ipsum'; const nativeWrapper = parentNative.querySelector(wrapperSelector); parentFixture.detectChanges(); expect(nativeWrapper.classList.contains('d-none')).toBe(false); diff --git a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts index f528f06ba5..8af108cceb 100644 --- a/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts +++ b/src/app/+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component.ts @@ -1,4 +1,5 @@ import { Component, Input } from '@angular/core'; +import { hasNoValue } from '../../../shared/empty.util'; /** * This component renders any content inside this wrapper. @@ -16,4 +17,10 @@ export class MetadataFieldWrapperComponent { */ @Input() label: string; + /** + * Make hasNoValue() available in the template + */ + hasNoValue(o: any): boolean { + return hasNoValue(o); + } } From 72fb18d92fd65c6a7f8fc789308cd21b80031449 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 8 Jan 2019 13:12:12 +0100 Subject: [PATCH 077/205] Add virtual metadata fields for journals --- resources/i18n/en.json | 16 ++++- src/app/+item-page/item-page.module.ts | 4 +- .../journal-issue.component.html | 9 +++ .../journal/journal.component.html | 9 +++ .../entity-types/person/person.component.html | 11 ++-- .../publication/publication.component.html | 21 +++++++ .../related-entities-search.component.html | 6 ++ .../related-entities-search.component.spec.ts | 56 ++++++++++++++++++ .../related-entities-search.component.ts | 59 +++++++++++++++++++ .../search-fixed-filter.service.ts | 9 +++ src/app/+search-page/search-page.module.ts | 2 +- 11 files changed, 191 insertions(+), 11 deletions(-) create mode 100644 src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html create mode 100644 src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts create mode 100644 src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 667d5132ba..5d85b79279 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -32,6 +32,8 @@ "uri": "URI", "files": "Files", "collections": "Collections", + "subject": "Keywords", + "citation": "Citation", "filesection": { "download": "Download", "name": "Name:", @@ -95,7 +97,8 @@ "page": { "issn": "ISSN", "publisher": "Publisher", - "description": "Description" + "description": "Description", + "editor": "Editor-in-Chief" } }, "journalvolume": { @@ -110,7 +113,16 @@ "number": "Number", "issuedate": "Issue Date", "description": "Description", - "keyword": "Keywords" + "keyword": "Keywords", + "journal-title": "Journal Title", + "journal-issn": "Journal ISSN" + } + }, + "publication": { + "page": { + "journal-title": "Journal Title", + "journal-issn": "Journal ISSN", + "volume-title": "Volume Title" } }, "nav": { diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index 2103a548c1..44ccbacf82 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -29,6 +29,7 @@ import { JournalComponent } from './simple/entity-types/journal/journal.componen import { JournalVolumeComponent } from './simple/entity-types/journal-volume/journal-volume.component'; import { JournalIssueComponent } from './simple/entity-types/journal-issue/journal-issue.component'; import { EntityComponent } from './simple/entity-types/shared/entity.component'; +import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component'; @NgModule({ imports: [ @@ -61,7 +62,8 @@ import { EntityComponent } from './simple/entity-types/shared/entity.component'; GenericItemPageFieldComponent, JournalComponent, JournalIssueComponent, - JournalVolumeComponent + JournalVolumeComponent, + RelatedEntitiesSearchComponent ], entryComponents: [ PublicationComponent, diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html index 73fee41f8f..d83b4ed9b8 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html +++ b/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html @@ -14,6 +14,14 @@ [fields]="['journalissue.issuedate']" [label]="'journalissue.page.issuedate'"> + + + +
diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.html b/src/app/+item-page/simple/entity-types/journal/journal.component.html index b02000a9e4..df50f613fb 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.html +++ b/src/app/+item-page/simple/entity-types/journal/journal.component.html @@ -14,6 +14,10 @@ [fields]="['journal.publisher']" [label]="'journal.page.publisher'"> + +
+
+ + +
diff --git a/src/app/+item-page/simple/entity-types/person/person.component.html b/src/app/+item-page/simple/entity-types/person/person.component.html index 548f46624d..fdc03c36a8 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.html +++ b/src/app/+item-page/simple/entity-types/person/person.component.html @@ -50,12 +50,9 @@
-
- - +
+ +
diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.html b/src/app/+item-page/simple/entity-types/publication/publication.component.html index 84cd5a151b..2df7f0e53e 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.html +++ b/src/app/+item-page/simple/entity-types/publication/publication.component.html @@ -7,6 +7,18 @@ + + + + + +
+ + + +
diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html new file mode 100644 index 0000000000..9ec082db73 --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.html @@ -0,0 +1,6 @@ + + diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts new file mode 100644 index 0000000000..e76a9cf3d0 --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.spec.ts @@ -0,0 +1,56 @@ +import { RelatedEntitiesSearchComponent } from './related-entities-search.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { TranslateModule } from '@ngx-translate/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; +import { Item } from '../../../../core/shared/item.model'; + +describe('RelatedEntitiesSearchComponent', () => { + let comp: RelatedEntitiesSearchComponent; + let fixture: ComponentFixture; + let fixedFilterService: SearchFixedFilterService; + + const mockItem = Object.assign(new Item(), { + id: 'id1' + }); + const mockRelationType = 'publicationsOfAuthor'; + const mockRelationEntityType = 'publication'; + const mockFilter= `f.${mockRelationType}=${mockItem.id}`; + const fixedFilterServiceStub = { + getFilterByRelation: () => mockFilter + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule], + declarations: [RelatedEntitiesSearchComponent], + providers: [ + { provide: SearchFixedFilterService, useValue: fixedFilterServiceStub } + ], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RelatedEntitiesSearchComponent); + comp = fixture.componentInstance; + fixedFilterService = (comp as any).fixedFilterService; + comp.relationType = mockRelationType; + comp.item = mockItem; + comp.relationEntityType = mockRelationEntityType; + fixture.detectChanges(); + }); + + it('should create a fixedFilter', () => { + expect(comp.fixedFilter).toEqual(mockFilter); + }); + + it('should create a fixedFilter$', () => { + comp.fixedFilter$.subscribe((fixedFilter) => { + expect(fixedFilter).toEqual(mockRelationEntityType); + }) + }); + +}); diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts new file mode 100644 index 0000000000..7d835cfd68 --- /dev/null +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -0,0 +1,59 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { Item } from '../../../../core/shared/item.model'; +import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; +import { isNotEmpty } from '../../../../shared/empty.util'; +import { of } from 'rxjs/internal/observable/of'; + +@Component({ + selector: 'ds-related-entities-search', + templateUrl: './related-entities-search.component.html' +}) +export class RelatedEntitiesSearchComponent implements OnInit { + + /** + * The type of relationship to fetch items for + * e.g. 'isAuthorOfPublication' + */ + @Input() relationType: string; + + /** + * The item to render relationships for + */ + @Input() item: Item; + + /** + * The entity type of the relationship items to be displayed + * e.g. 'publication' + * This determines the title of the search results (if search is enabled) + */ + @Input() relationEntityType: string; + + /** + * Whether or not the search bar and title should be displayed (defaults to true) + * @type {boolean} + */ + @Input() searchEnabled = true; + + /** + * The ratio of the sidebar's width compared to the search results (1-12) (defaults to 4) + * @type {number} + */ + @Input() sideBarWidth = 4; + + fixedFilter: string; + fixedFilter$: Observable; + + constructor(private fixedFilterService: SearchFixedFilterService) { + } + + ngOnInit(): void { + if (isNotEmpty(this.relationType) && isNotEmpty(this.item)) { + this.fixedFilter = this.fixedFilterService.getFilterByRelation(this.relationType, this.item.id); + } + if (isNotEmpty(this.relationEntityType)) { + this.fixedFilter$ = of(this.relationEntityType); + } + } + +} diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 8f8997e63c..bf812fe4cc 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -73,4 +73,13 @@ export class SearchFixedFilterService { return `query=relation.${relationType}:${itemUUID}`; } + /** + * Get the filter for a relation with the item's UUID + * @param relationType The type of relation e.g. 'isAuthorOfPublication' + * @param itemUUID The item's UUID + */ + getFilterByRelation(relationType: string, itemUUID: string): string { + return `f.${relationType}=${itemUUID}`; + } + } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 658c32d27e..b32e504966 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -91,7 +91,7 @@ const effects = [ SearchBooleanFilterComponent, ], exports: [ - FilteredSearchPageComponent + FilteredSearchPageComponent, ] }) From 16576a77912a47e482a217d2ec2ff6a8bc0efd2d Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 8 Jan 2019 16:51:43 +0100 Subject: [PATCH 078/205] Add typedocs to RelatedEntitiesSearchComponent --- .../related-entities-search.component.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts index 7d835cfd68..672655a8b8 100644 --- a/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts +++ b/src/app/+item-page/simple/related-entities/related-entities-search/related-entities-search.component.ts @@ -9,6 +9,11 @@ import { of } from 'rxjs/internal/observable/of'; selector: 'ds-related-entities-search', templateUrl: './related-entities-search.component.html' }) +/** + * A component to show related items as search results. + * Related items can be facetted, or queried using an + * optional search box. + */ export class RelatedEntitiesSearchComponent implements OnInit { /** From 8c1874c5842dfb1a36e7c464b826b659e2ac32ce Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 16 Jan 2019 15:34:01 +0100 Subject: [PATCH 079/205] rename 'entity' to 'item' --- src/app/+item-page/item-page.module.ts | 22 ++++++++-------- .../simple/item-page.component.html | 2 +- .../simple/item-page.component.spec.ts | 3 +-- .../journal-issue.component.html | 12 ++++----- .../journal-issue.component.scss | 0 .../journal-issue.component.spec.ts | 5 ++-- .../journal-issue/journal-issue.component.ts | 12 ++++----- .../journal-volume.component.html | 12 ++++----- .../journal-volume.component.scss | 0 .../journal-volume.component.spec.ts | 5 ++-- .../journal-volume.component.ts | 12 ++++----- .../journal/journal.component.html | 6 ++--- .../journal/journal.component.scss | 0 .../journal/journal.component.spec.ts | 3 +-- .../journal/journal.component.ts | 12 ++++----- .../orgunit/orgunit.component.html | 18 ++++++------- .../orgunit/orgunit.component.scss | 0 .../orgunit/orgunit.component.spec.ts | 5 ++-- .../orgunit/orgunit.component.ts | 12 ++++----- .../person/person.component.html | 12 ++++----- .../person/person.component.scss | 0 .../person/person.component.spec.ts | 5 ++-- .../person/person.component.ts | 12 ++++----- .../project/project.component.html | 18 ++++++------- .../project/project.component.scss | 0 .../project/project.component.spec.ts | 5 ++-- .../project/project.component.ts | 12 ++++----- .../publication/publication.component.html | 24 ++++++++--------- .../publication/publication.component.scss | 0 .../publication/publication.component.spec.ts | 5 ++-- .../publication/publication.component.ts | 16 ++++++------ .../shared/item.component.spec.ts} | 15 +++++------ .../shared/item.component.ts} | 10 +++---- .../related-entities.component.html | 5 ---- .../related-items-component.ts} | 16 ++++++------ .../related-items.component.html | 5 ++++ .../related-items.component.scss} | 0 .../related-items.component.spec.ts} | 26 +++++++++---------- .../normalized-item-type.model.ts} | 16 ++++++------ .../normalized-relationship-type.model.ts | 10 +++---- .../normalized-relationship.model.ts | 10 +++---- .../cache/models/normalized-object-factory.ts | 10 +++---- .../item-type.model.ts} | 8 +++--- .../relationship-type.model.ts | 12 ++++----- .../relationship.model.ts | 10 +++---- src/app/core/shared/item.model.ts | 2 +- src/app/core/shared/resource-type.ts | 2 +- .../item-type-decorator.ts} | 12 ++++----- .../item-type-switcher.component.html} | 0 .../item-type-switcher.component.scss} | 0 .../item-type-switcher.component.spec.ts} | 22 ++++++++-------- .../switcher/item-type-switcher.component.ts} | 12 ++++----- .../entity-list-element.component.html | 1 - .../journal-issue-list-element.component.ts | 16 ------------ .../journal-volume-list-element.component.ts | 16 ------------ .../journal/journal-list-element.component.ts | 16 ------------ .../orgunit/orgunit-list-element.component.ts | 16 ------------ .../person/person-list-element.component.ts | 16 ------------ .../project/project-list-element.component.ts | 16 ------------ .../publication-list-element.component.ts | 17 ------------ .../item-list-element.component.html | 1 + ....scss => item-list-element.component.scss} | 0 ...ts => item-list-element.component.spec.ts} | 23 ++++++++-------- ...nent.ts => item-list-element.component.ts} | 12 ++++----- .../item-search-result-component.ts} | 8 +++--- .../item-search-result.component.spec.ts} | 25 +++++++++--------- .../journal-issue-list-element.component.html | 0 .../journal-issue-list-element.component.scss | 0 ...urnal-issue-list-element.component.spec.ts | 3 +-- .../journal-issue-list-element.component.ts | 16 ++++++++++++ ...journal-volume-list-element.component.html | 0 ...journal-volume-list-element.component.scss | 0 ...rnal-volume-list-element.component.spec.ts | 3 +-- .../journal-volume-list-element.component.ts | 16 ++++++++++++ .../journal-list-element.component.html | 0 .../journal-list-element.component.scss | 0 .../journal-list-element.component.spec.ts | 3 +-- .../journal/journal-list-element.component.ts | 16 ++++++++++++ .../orgunit-list-element.component.html | 0 .../orgunit-list-element.component.scss | 0 .../orgunit-list-element.component.spec.ts | 3 +-- .../orgunit/orgunit-list-element.component.ts | 16 ++++++++++++ .../person/person-list-element.component.html | 0 .../person/person-list-element.component.scss | 0 .../person-list-element.component.spec.ts | 3 +-- .../person/person-list-element.component.ts | 16 ++++++++++++ .../project-list-element.component.html | 0 .../project-list-element.component.scss | 0 .../project-list-element.component.spec.ts | 3 +-- .../project/project-list-element.component.ts | 16 ++++++++++++ .../publication-list-element.component.html | 0 .../publication-list-element.component.scss | 0 ...publication-list-element.component.spec.ts | 3 +-- .../publication-list-element.component.ts | 17 ++++++++++++ ...-search-result-list-element.component.html | 2 +- ...arch-result-list-element.component.spec.ts | 8 +++--- src/app/shared/shared.module.ts | 26 +++++++++---------- 97 files changed, 394 insertions(+), 412 deletions(-) rename src/app/+item-page/simple/{entity-types => item-types}/journal-issue/journal-issue.component.html (88%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-issue/journal-issue.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-issue/journal-issue.component.spec.ts (81%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-issue/journal-issue.component.ts (75%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-volume/journal-volume.component.html (87%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-volume/journal-volume.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-volume/journal-volume.component.spec.ts (79%) rename src/app/+item-page/simple/{entity-types => item-types}/journal-volume/journal-volume.component.ts (74%) rename src/app/+item-page/simple/{entity-types => item-types}/journal/journal.component.html (93%) rename src/app/+item-page/simple/{entity-types => item-types}/journal/journal.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/journal/journal.component.spec.ts (96%) rename src/app/+item-page/simple/{entity-types => item-types}/journal/journal.component.ts (70%) rename src/app/+item-page/simple/{entity-types => item-types}/orgunit/orgunit.component.html (86%) rename src/app/+item-page/simple/{entity-types => item-types}/orgunit/orgunit.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/orgunit/orgunit.component.spec.ts (82%) rename src/app/+item-page/simple/{entity-types => item-types}/orgunit/orgunit.component.ts (77%) rename src/app/+item-page/simple/{entity-types => item-types}/person/person.component.html (92%) rename src/app/+item-page/simple/{entity-types => item-types}/person/person.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/person/person.component.spec.ts (85%) rename src/app/+item-page/simple/{entity-types => item-types}/person/person.component.ts (83%) rename src/app/+item-page/simple/{entity-types => item-types}/project/project.component.html (86%) rename src/app/+item-page/simple/{entity-types => item-types}/project/project.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/project/project.component.spec.ts (83%) rename src/app/+item-page/simple/{entity-types => item-types}/project/project.component.ts (78%) rename src/app/+item-page/simple/{entity-types => item-types}/publication/publication.component.html (76%) rename src/app/+item-page/simple/{entity-types => item-types}/publication/publication.component.scss (100%) rename src/app/+item-page/simple/{entity-types => item-types}/publication/publication.component.spec.ts (94%) rename src/app/+item-page/simple/{entity-types => item-types}/publication/publication.component.ts (78%) rename src/app/+item-page/simple/{entity-types/shared/entity.component.spec.ts => item-types/shared/item.component.spec.ts} (94%) rename src/app/+item-page/simple/{entity-types/shared/entity.component.ts => item-types/shared/item.component.ts} (92%) delete mode 100644 src/app/+item-page/simple/related-entities/related-entities.component.html rename src/app/+item-page/simple/{related-entities/related-entities-component.ts => related-items/related-items-component.ts} (54%) create mode 100644 src/app/+item-page/simple/related-items/related-items.component.html rename src/app/+item-page/simple/{related-entities/related-entities.component.scss => related-items/related-items.component.scss} (100%) rename src/app/+item-page/simple/{related-entities/related-entities.component.spec.ts => related-items/related-items.component.spec.ts} (62%) rename src/app/core/cache/models/{entities/normalized-entity-type.model.ts => items/normalized-item-type.model.ts} (53%) rename src/app/core/cache/models/{entities => items}/normalized-relationship-type.model.ts (85%) rename src/app/core/cache/models/{entities => items}/normalized-relationship.model.ts (75%) rename src/app/core/shared/{entities/entity-type.model.ts => item-relationships/item-type.model.ts} (65%) rename src/app/core/shared/{entities => item-relationships}/relationship-type.model.ts (80%) rename src/app/core/shared/{entities => item-relationships}/relationship.model.ts (73%) rename src/app/shared/{entities/entity-type-decorator.ts => items/item-type-decorator.ts} (62%) rename src/app/shared/{entities/switcher/entity-type-switcher.component.html => items/switcher/item-type-switcher.component.html} (100%) rename src/app/shared/{entities/switcher/entity-type-switcher.component.scss => items/switcher/item-type-switcher.component.scss} (100%) rename src/app/shared/{entities/switcher/entity-type-switcher.component.spec.ts => items/switcher/item-type-switcher.component.spec.ts} (63%) rename src/app/shared/{entities/switcher/entity-type-switcher.component.ts => items/switcher/item-type-switcher.component.ts} (82%) delete mode 100644 src/app/shared/object-list/item-list-element/entity-list-element.component.html delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts delete mode 100644 src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts create mode 100644 src/app/shared/object-list/item-list-element/item-list-element.component.html rename src/app/shared/object-list/item-list-element/{entity-list-element.component.scss => item-list-element.component.scss} (100%) rename src/app/shared/object-list/item-list-element/{entity-list-element.component.spec.ts => item-list-element.component.spec.ts} (62%) rename src/app/shared/object-list/item-list-element/{entity-list-element.component.ts => item-list-element.component.ts} (59%) rename src/app/shared/object-list/item-list-element/{entity-types/entity-search-result-component.ts => item-types/item-search-result-component.ts} (77%) rename src/app/shared/object-list/item-list-element/{entity-types/entity-search-result.component.spec.ts => item-types/item-search-result.component.spec.ts} (75%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-issue/journal-issue-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-issue/journal-issue-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-issue/journal-issue-list-element.component.spec.ts (96%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-volume/journal-volume-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-volume/journal-volume-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal-volume/journal-volume-list-element.component.spec.ts (96%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal/journal-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal/journal-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/journal/journal-list-element.component.spec.ts (95%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/orgunit/orgunit-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/orgunit/orgunit-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/orgunit/orgunit-list-element.component.spec.ts (95%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/person/person-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/person/person-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/person/person-list-element.component.spec.ts (95%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/project/project-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/project/project-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/project/project-list-element.component.spec.ts (95%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/publication/publication-list-element.component.html (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/publication/publication-list-element.component.scss (100%) rename src/app/shared/object-list/item-list-element/{entity-types => item-types}/publication/publication-list-element.component.spec.ts (97%) create mode 100644 src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index 2103a548c1..b4e5ba1cd8 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -19,16 +19,16 @@ import { FileSectionComponent } from './simple/field-components/file-section/fil import { CollectionsComponent } from './field-components/collections/collections.component'; import { FullItemPageComponent } from './full/full-item-page.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; -import { RelatedEntitiesComponent } from './simple/related-entities/related-entities-component'; +import { RelatedItemsComponent } from './simple/related-items/related-items-component'; import { SearchPageModule } from '../+search-page/search-page.module'; -import { PublicationComponent } from './simple/entity-types/publication/publication.component'; -import { PersonComponent } from './simple/entity-types/person/person.component'; -import { OrgunitComponent } from './simple/entity-types/orgunit/orgunit.component'; -import { ProjectComponent } from './simple/entity-types/project/project.component'; -import { JournalComponent } from './simple/entity-types/journal/journal.component'; -import { JournalVolumeComponent } from './simple/entity-types/journal-volume/journal-volume.component'; -import { JournalIssueComponent } from './simple/entity-types/journal-issue/journal-issue.component'; -import { EntityComponent } from './simple/entity-types/shared/entity.component'; +import { PublicationComponent } from './simple/item-types/publication/publication.component'; +import { PersonComponent } from './simple/item-types/person/person.component'; +import { OrgunitComponent } from './simple/item-types/orgunit/orgunit.component'; +import { ProjectComponent } from './simple/item-types/project/project.component'; +import { JournalComponent } from './simple/item-types/journal/journal.component'; +import { JournalVolumeComponent } from './simple/item-types/journal-volume/journal-volume.component'; +import { JournalIssueComponent } from './simple/item-types/journal-issue/journal-issue.component'; +import { ItemComponent } from './simple/item-types/shared/item.component'; @NgModule({ imports: [ @@ -56,8 +56,8 @@ import { EntityComponent } from './simple/entity-types/shared/entity.component'; ProjectComponent, OrgunitComponent, PersonComponent, - RelatedEntitiesComponent, - EntityComponent, + RelatedItemsComponent, + ItemComponent, GenericItemPageFieldComponent, JournalComponent, JournalIssueComponent, diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index 0db8bbf318..e59d37fb9d 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/+item-page/simple/item-page.component.spec.ts b/src/app/+item-page/simple/item-page.component.spec.ts index bc1df00300..fbe616227f 100644 --- a/src/app/+item-page/simple/item-page.component.spec.ts +++ b/src/app/+item-page/simple/item-page.component.spec.ts @@ -8,14 +8,13 @@ import { ActivatedRoute } from '@angular/router'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; import { MetadataService } from '../../core/metadata/metadata.service'; import { VarDirective } from '../../shared/utils/var.directive'; -import { Observable } from 'rxjs'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { PaginatedList } from '../../core/data/paginated-list'; import { PageInfo } from '../../core/shared/page-info.model'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; -import { createRelationshipsObservable } from './entity-types/shared/entity.component.spec'; +import { createRelationshipsObservable } from './item-types/shared/item.component.spec'; import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html similarity index 88% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html rename to src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html index 73fee41f8f..13e977ac5c 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.html +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html @@ -16,14 +16,14 @@
- - - + - + diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.scss b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.scss rename to src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts similarity index 81% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts rename to src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts index a2db07362d..a648250b03 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts @@ -1,9 +1,8 @@ -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec'; import { JournalIssueComponent } from './journal-issue.component'; import { of as observableOf } from 'rxjs'; @@ -33,4 +32,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('JournalIssueComponent', getEntityPageFieldsTest(mockItem, JournalIssueComponent)); +describe('JournalIssueComponent', getItemPageFieldsTest(mockItem, JournalIssueComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts similarity index 75% rename from src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts rename to src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts index 91d4b1ce8c..c663b15256 100644 --- a/src/app/+item-page/simple/entity-types/journal-issue/journal-issue.component.ts +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts @@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('JournalIssue', ElementViewMode.Full) +@rendersItemType('JournalIssue', ElementViewMode.Full) @Component({ selector: 'ds-journal-issue', styleUrls: ['./journal-issue.component.scss'], templateUrl: './journal-issue.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Journal Issue + * The component for displaying metadata and relations of an item of the type Journal Issue */ -export class JournalIssueComponent extends EntityComponent { +export class JournalIssueComponent extends ItemComponent { /** * The volumes related to this journal issue */ diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.html b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html similarity index 87% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.html rename to src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html index df770b06d8..a956e40fdd 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.html +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html @@ -16,14 +16,14 @@
- - - + - + diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.scss b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.scss rename to src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts similarity index 79% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts rename to src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts index 2cab243924..5442048a50 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts @@ -1,9 +1,8 @@ -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec'; import { JournalVolumeComponent } from './journal-volume.component'; import { of as observableOf } from 'rxjs'; @@ -28,4 +27,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('JournalVolumeComponent', getEntityPageFieldsTest(mockItem, JournalVolumeComponent)); +describe('JournalVolumeComponent', getItemPageFieldsTest(mockItem, JournalVolumeComponent)); diff --git a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts similarity index 74% rename from src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts rename to src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts index c6b9233ece..ebb6919234 100644 --- a/src/app/+item-page/simple/entity-types/journal-volume/journal-volume.component.ts +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts @@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('JournalVolume', ElementViewMode.Full) +@rendersItemType('JournalVolume', ElementViewMode.Full) @Component({ selector: 'ds-journal-volume', styleUrls: ['./journal-volume.component.scss'], templateUrl: './journal-volume.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Journal Volume + * The component for displaying metadata and relations of an item of the type Journal Volume */ -export class JournalVolumeComponent extends EntityComponent { +export class JournalVolumeComponent extends ItemComponent { /** * The journals related to this journal volume */ diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.html b/src/app/+item-page/simple/item-types/journal/journal.component.html similarity index 93% rename from src/app/+item-page/simple/entity-types/journal/journal.component.html rename to src/app/+item-page/simple/item-types/journal/journal.component.html index b02000a9e4..548f6304bc 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.html +++ b/src/app/+item-page/simple/item-types/journal/journal.component.html @@ -16,10 +16,10 @@
- - + diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.scss b/src/app/+item-page/simple/item-types/journal/journal.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/journal/journal.component.scss rename to src/app/+item-page/simple/item-types/journal/journal.component.scss diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts similarity index 96% rename from src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts rename to src/app/+item-page/simple/item-types/journal/journal.component.spec.ts index d9915e0410..8a18f0f727 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts @@ -1,10 +1,9 @@ import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { ItemDataService } from '../../../../core/data/item-data.service'; -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { By } from '@angular/platform-browser'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; diff --git a/src/app/+item-page/simple/entity-types/journal/journal.component.ts b/src/app/+item-page/simple/item-types/journal/journal.component.ts similarity index 70% rename from src/app/+item-page/simple/entity-types/journal/journal.component.ts rename to src/app/+item-page/simple/item-types/journal/journal.component.ts index 7e1e0233b6..3af725062c 100644 --- a/src/app/+item-page/simple/entity-types/journal/journal.component.ts +++ b/src/app/+item-page/simple/item-types/journal/journal.component.ts @@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core'; import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('Journal', ElementViewMode.Full) +@rendersItemType('Journal', ElementViewMode.Full) @Component({ selector: 'ds-journal', styleUrls: ['./journal.component.scss'], templateUrl: './journal.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Journal + * The component for displaying metadata and relations of an item of the type Journal */ -export class JournalComponent extends EntityComponent { +export class JournalComponent extends ItemComponent { /** * The volumes related to this journal */ diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.html b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html similarity index 86% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit.component.html rename to src/app/+item-page/simple/item-types/orgunit/orgunit.component.html index e5a02b54e5..7eacf66347 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.html +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html @@ -24,18 +24,18 @@
- - - + - - + - + diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.scss b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit.component.scss rename to src/app/+item-page/simple/item-types/orgunit/orgunit.component.scss diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts similarity index 82% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts rename to src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts index e7e83ccc2a..bb356ba7fb 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.spec.ts +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts @@ -1,9 +1,8 @@ -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec'; import { OrgunitComponent } from './orgunit.component'; import { of as observableOf } from 'rxjs'; @@ -38,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('OrgUnitComponent', getEntityPageFieldsTest(mockItem, OrgunitComponent)); +describe('OrgUnitComponent', getItemPageFieldsTest(mockItem, OrgunitComponent)); diff --git a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts similarity index 77% rename from src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts rename to src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts index 1f99f29ea2..f1979b0961 100644 --- a/src/app/+item-page/simple/entity-types/orgunit/orgunit.component.ts +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts @@ -2,22 +2,22 @@ import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('OrgUnit', ElementViewMode.Full) +@rendersItemType('OrgUnit', ElementViewMode.Full) @Component({ selector: 'ds-orgunit', styleUrls: ['./orgunit.component.scss'], templateUrl: './orgunit.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Organisation Unit + * The component for displaying metadata and relations of an item of the type Organisation Unit */ -export class OrgunitComponent extends EntityComponent implements OnInit { +export class OrgunitComponent extends ItemComponent implements OnInit { /** * The people related to this organisation unit */ diff --git a/src/app/+item-page/simple/entity-types/person/person.component.html b/src/app/+item-page/simple/item-types/person/person.component.html similarity index 92% rename from src/app/+item-page/simple/entity-types/person/person.component.html rename to src/app/+item-page/simple/item-types/person/person.component.html index 548f46624d..a490361ac3 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.html +++ b/src/app/+item-page/simple/item-types/person/person.component.html @@ -24,14 +24,14 @@
- - - + - + diff --git a/src/app/+item-page/simple/entity-types/person/person.component.scss b/src/app/+item-page/simple/item-types/person/person.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/person/person.component.scss rename to src/app/+item-page/simple/item-types/person/person.component.scss diff --git a/src/app/+item-page/simple/entity-types/person/person.component.spec.ts b/src/app/+item-page/simple/item-types/person/person.component.spec.ts similarity index 85% rename from src/app/+item-page/simple/entity-types/person/person.component.spec.ts rename to src/app/+item-page/simple/item-types/person/person.component.spec.ts index 70eba79180..4c582f67e8 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.spec.ts +++ b/src/app/+item-page/simple/item-types/person/person.component.spec.ts @@ -1,9 +1,8 @@ -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec'; import { PersonComponent } from './person.component'; import { of as observableOf } from 'rxjs'; @@ -48,4 +47,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('PersonComponent', getEntityPageFieldsTest(mockItem, PersonComponent)); +describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent)); diff --git a/src/app/+item-page/simple/entity-types/person/person.component.ts b/src/app/+item-page/simple/item-types/person/person.component.ts similarity index 83% rename from src/app/+item-page/simple/entity-types/person/person.component.ts rename to src/app/+item-page/simple/item-types/person/person.component.ts index 2ba3a5bdcf..bb98799da5 100644 --- a/src/app/+item-page/simple/entity-types/person/person.component.ts +++ b/src/app/+item-page/simple/item-types/person/person.component.ts @@ -2,23 +2,23 @@ import { Component, Inject } from '@angular/core'; import { Observable , of as observableOf } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('Person', ElementViewMode.Full) +@rendersItemType('Person', ElementViewMode.Full) @Component({ selector: 'ds-person', styleUrls: ['./person.component.scss'], templateUrl: './person.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Person + * The component for displaying metadata and relations of an item of the type Person */ -export class PersonComponent extends EntityComponent { +export class PersonComponent extends ItemComponent { /** * The publications related to this person */ diff --git a/src/app/+item-page/simple/entity-types/project/project.component.html b/src/app/+item-page/simple/item-types/project/project.component.html similarity index 86% rename from src/app/+item-page/simple/entity-types/project/project.component.html rename to src/app/+item-page/simple/item-types/project/project.component.html index 36a71bd71a..17aa8b2065 100644 --- a/src/app/+item-page/simple/entity-types/project/project.component.html +++ b/src/app/+item-page/simple/item-types/project/project.component.html @@ -20,18 +20,18 @@
- - - + - - + - + diff --git a/src/app/+item-page/simple/entity-types/project/project.component.scss b/src/app/+item-page/simple/item-types/project/project.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/project/project.component.scss rename to src/app/+item-page/simple/item-types/project/project.component.scss diff --git a/src/app/+item-page/simple/entity-types/project/project.component.spec.ts b/src/app/+item-page/simple/item-types/project/project.component.spec.ts similarity index 83% rename from src/app/+item-page/simple/entity-types/project/project.component.spec.ts rename to src/app/+item-page/simple/item-types/project/project.component.spec.ts index c2fceb4266..e28c97f87d 100644 --- a/src/app/+item-page/simple/entity-types/project/project.component.spec.ts +++ b/src/app/+item-page/simple/item-types/project/project.component.spec.ts @@ -1,9 +1,8 @@ -import { Observable } from 'rxjs'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec'; +import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec'; import { ProjectComponent } from './project.component'; import { of as observableOf } from 'rxjs'; @@ -38,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('ProjectComponent', getEntityPageFieldsTest(mockItem, ProjectComponent)); +describe('ProjectComponent', getItemPageFieldsTest(mockItem, ProjectComponent)); diff --git a/src/app/+item-page/simple/entity-types/project/project.component.ts b/src/app/+item-page/simple/item-types/project/project.component.ts similarity index 78% rename from src/app/+item-page/simple/entity-types/project/project.component.ts rename to src/app/+item-page/simple/item-types/project/project.component.ts index e31d748129..dfbdacff86 100644 --- a/src/app/+item-page/simple/entity-types/project/project.component.ts +++ b/src/app/+item-page/simple/item-types/project/project.component.ts @@ -2,22 +2,22 @@ import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; -import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator'; +import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { isNotEmpty } from '../../../../shared/empty.util'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('Project', ElementViewMode.Full) +@rendersItemType('Project', ElementViewMode.Full) @Component({ selector: 'ds-project', styleUrls: ['./project.component.scss'], templateUrl: './project.component.html' }) /** - * The component for displaying metadata and relations of an item with entity type Project + * The component for displaying metadata and relations of an item of the type Project */ -export class ProjectComponent extends EntityComponent implements OnInit { +export class ProjectComponent extends ItemComponent implements OnInit { /** * The people related to this project */ diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html similarity index 76% rename from src/app/+item-page/simple/entity-types/publication/publication.component.html rename to src/app/+item-page/simple/item-types/publication/publication.component.html index 84cd5a151b..e619e18b01 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -9,22 +9,22 @@
- - - + - - + - - + - + diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.scss b/src/app/+item-page/simple/item-types/publication/publication.component.scss similarity index 100% rename from src/app/+item-page/simple/entity-types/publication/publication.component.scss rename to src/app/+item-page/simple/item-types/publication/publication.component.scss diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts similarity index 94% rename from src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts rename to src/app/+item-page/simple/item-types/publication/publication.component.spec.ts index c3c9c2cd81..603d358761 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.spec.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts @@ -3,18 +3,17 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; import { TruncatableService } from '../../../../shared/truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; -import { createRelationshipsObservable } from '../shared/entity.component.spec'; +import { createRelationshipsObservable } from '../shared/item.component.spec'; import { PublicationComponent } from './publication.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/+item-page/simple/entity-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts similarity index 78% rename from src/app/+item-page/simple/entity-types/publication/publication.component.ts rename to src/app/+item-page/simple/item-types/publication/publication.component.ts index 09481362c1..b6178893d6 100644 --- a/src/app/+item-page/simple/entity-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -3,22 +3,22 @@ import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { - DEFAULT_ENTITY_TYPE, - rendersEntityType -} from '../../../../shared/entities/entity-type-decorator'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; + DEFAULT_ITEM_TYPE, + rendersItemType +} from '../../../../shared/items/item-type-decorator'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ElementViewMode } from '../../../../shared/view-mode'; -import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component'; +import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; -@rendersEntityType('Publication', ElementViewMode.Full) -@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full) +@rendersItemType('Publication', ElementViewMode.Full) +@rendersItemType(DEFAULT_ITEM_TYPE, ElementViewMode.Full) @Component({ selector: 'ds-publication', styleUrls: ['./publication.component.scss'], templateUrl: './publication.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PublicationComponent extends EntityComponent implements OnInit { +export class PublicationComponent extends ItemComponent implements OnInit { /** * The authors related to this publication */ diff --git a/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts similarity index 94% rename from src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts rename to src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 9565da1e7d..30f39957db 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -7,27 +7,26 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader'; import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { TruncatePipe } from '../../../../shared/utils/truncate.pipe'; import { isNotEmpty } from '../../../../shared/empty.util'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; -import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; +import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; -import { Relationship } from '../../../../core/shared/entities/relationship.model'; -import { Observable } from 'rxjs'; +import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { compareArraysUsing, compareArraysUsingIds } from './entity.component'; +import { compareArraysUsing, compareArraysUsingIds } from './item.component'; import { of as observableOf } from 'rxjs'; /** - * Create a generic test for an entity-page-fields component using a mockItem and the type of component + * Create a generic test for an item-page-fields component using a mockItem and the type of component * @param {Item} mockItem The item to use for testing. The item needs to contain just the metadata necessary to * execute the tests for it's component. * @param component The type of component to create test cases for. * @returns {() => void} Returns a specDefinition for the test. */ -export function getEntityPageFieldsTest(mockItem: Item, component) { +export function getItemPageFieldsTest(mockItem: Item, component) { return () => { let comp: any; let fixture: ComponentFixture; @@ -101,7 +100,7 @@ export function createRelationshipsObservable() { }) ]))); } -describe('EntityComponent', () => { +describe('ItemComponent', () => { const arr1 = [ { id: 1, diff --git a/src/app/+item-page/simple/entity-types/shared/entity.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts similarity index 92% rename from src/app/+item-page/simple/entity-types/shared/entity.component.ts rename to src/app/+item-page/simple/item-types/shared/item.component.ts index 616c14269b..5527114ec3 100644 --- a/src/app/+item-page/simple/entity-types/shared/entity.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -4,12 +4,12 @@ import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; -import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model'; -import { Relationship } from '../../../../core/shared/entities/relationship.model'; +import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; +import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../../core/shared/item.model'; import { getRemoteDataPayload } from '../../../../core/shared/operators'; import { hasValue } from '../../../../shared/empty.util'; -import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; /** * Operator for comparing arrays using a mapping function @@ -83,13 +83,13 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) => ); @Component({ - selector: 'ds-entity', + selector: 'ds-item', template: '' }) /** * A generic component for displaying metadata and relations of an item */ -export class EntityComponent implements OnInit { +export class ItemComponent implements OnInit { /** * Resolved relationships and types together in one observable */ diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.html b/src/app/+item-page/simple/related-entities/related-entities.component.html deleted file mode 100644 index f09f0ccdef..0000000000 --- a/src/app/+item-page/simple/related-entities/related-entities.component.html +++ /dev/null @@ -1,5 +0,0 @@ - - - - diff --git a/src/app/+item-page/simple/related-entities/related-entities-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts similarity index 54% rename from src/app/+item-page/simple/related-entities/related-entities-component.ts rename to src/app/+item-page/simple/related-items/related-items-component.ts index 85532eacbe..6ad2f0eeb3 100644 --- a/src/app/+item-page/simple/related-entities/related-entities-component.ts +++ b/src/app/+item-page/simple/related-items/related-items-component.ts @@ -3,19 +3,19 @@ import { Item } from '../../../core/shared/item.model'; import * as viewMode from '../../../shared/view-mode'; @Component({ - selector: 'ds-related-entities', - styleUrls: ['./related-entities.component.scss'], - templateUrl: './related-entities.component.html' + selector: 'ds-related-items', + styleUrls: ['./related-items.component.scss'], + templateUrl: './related-items.component.html' }) /** - * This component is used for displaying relations between entities - * It expects a list of entities to display and a label to put on top + * This component is used for displaying relations between items + * It expects a list of items to display and a label to put on top */ -export class RelatedEntitiesComponent { +export class RelatedItemsComponent { /** - * A list of entities to display + * A list of items to display */ - @Input() entities: Item[]; + @Input() items: Item[]; /** * An i18n label to use as a title for the list (usually describes the relation) diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html new file mode 100644 index 0000000000..bd8b7eb5f3 --- /dev/null +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -0,0 +1,5 @@ + + + + diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.scss b/src/app/+item-page/simple/related-items/related-items.component.scss similarity index 100% rename from src/app/+item-page/simple/related-entities/related-entities.component.scss rename to src/app/+item-page/simple/related-items/related-items.component.scss diff --git a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts b/src/app/+item-page/simple/related-items/related-items.component.spec.ts similarity index 62% rename from src/app/+item-page/simple/related-entities/related-entities.component.spec.ts rename to src/app/+item-page/simple/related-items/related-items.component.spec.ts index a8897c1e88..ef42ab1098 100644 --- a/src/app/+item-page/simple/related-entities/related-entities.component.spec.ts +++ b/src/app/+item-page/simple/related-items/related-items.component.spec.ts @@ -1,12 +1,12 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { RelatedEntitiesComponent } from './related-entities-component'; +import { RelatedItemsComponent } from './related-items-component'; import { Item } from '../../../core/shared/item.model'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; -import { createRelationshipsObservable } from '../entity-types/shared/entity.component.spec'; +import { createRelationshipsObservable } from '../item-types/shared/item.component.spec'; import { of as observableOf } from 'rxjs'; const mockItem1: Item = Object.assign(new Item(), { @@ -19,33 +19,33 @@ const mockItem2: Item = Object.assign(new Item(), { metadata: [], relationships: createRelationshipsObservable() }); -const mockEntities = [mockItem1, mockItem2]; +const mockItems = [mockItem1, mockItem2]; -describe('RelatedEntitiesComponent', () => { - let comp: RelatedEntitiesComponent; - let fixture: ComponentFixture; +describe('RelatedItemsComponent', () => { + let comp: RelatedItemsComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [], - declarations: [RelatedEntitiesComponent], + declarations: [RelatedItemsComponent], providers: [], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(RelatedEntitiesComponent, { + }).overrideComponent(RelatedItemsComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(RelatedEntitiesComponent); + fixture = TestBed.createComponent(RelatedItemsComponent); comp = fixture.componentInstance; - comp.entities = mockEntities; + comp.items = mockItems; fixture.detectChanges(); })); - it(`should load ${mockEntities.length} entity-type-switcher components`, () => { - const fields = fixture.debugElement.queryAll(By.css('ds-entity-type-switcher')); - expect(fields.length).toBe(mockEntities.length); + it(`should load ${mockItems.length} item-type-switcher components`, () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher')); + expect(fields.length).toBe(mockItems.length); }); }); diff --git a/src/app/core/cache/models/entities/normalized-entity-type.model.ts b/src/app/core/cache/models/items/normalized-item-type.model.ts similarity index 53% rename from src/app/core/cache/models/entities/normalized-entity-type.model.ts rename to src/app/core/cache/models/items/normalized-item-type.model.ts index 729aa39d7b..80ec132c42 100644 --- a/src/app/core/cache/models/entities/normalized-entity-type.model.ts +++ b/src/app/core/cache/models/items/normalized-item-type.model.ts @@ -1,32 +1,32 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { EntityType } from '../../../shared/entities/entity-type.model'; +import { ItemType } from '../../../shared/item-relationships/item-type.model'; import { ResourceType } from '../../../shared/resource-type'; import { mapsTo } from '../../builders/build-decorators'; import { NormalizedObject } from '../normalized-object.model'; import { IDToUUIDSerializer } from '../../id-to-uuid-serializer'; /** - * Normalized model class for a DSpace EntityType + * Normalized model class for a DSpace ItemType */ -@mapsTo(EntityType) +@mapsTo(ItemType) @inheritSerialization(NormalizedObject) -export class NormalizedEntityType extends NormalizedObject { +export class NormalizedItemType extends NormalizedObject { /** - * The label that describes the ResourceType of the Entity + * The label that describes the ResourceType of the Item */ @autoserialize label: string; /** - * The identifier of this EntityType + * The identifier of this ItemType */ @autoserialize id: string; /** - * The universally unique identifier of this EntityType + * The universally unique identifier of this ItemType */ - @autoserializeAs(new IDToUUIDSerializer(ResourceType.EntityType), 'id') + @autoserializeAs(new IDToUUIDSerializer(ResourceType.ItemType), 'id') uuid: string; } diff --git a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts b/src/app/core/cache/models/items/normalized-relationship-type.model.ts similarity index 85% rename from src/app/core/cache/models/entities/normalized-relationship-type.model.ts rename to src/app/core/cache/models/items/normalized-relationship-type.model.ts index ea71e217b9..271bd9cb6f 100644 --- a/src/app/core/cache/models/entities/normalized-relationship-type.model.ts +++ b/src/app/core/cache/models/items/normalized-relationship-type.model.ts @@ -1,5 +1,5 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { RelationshipType } from '../../../shared/entities/relationship-type.model'; +import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model'; import { ResourceType } from '../../../shared/resource-type'; import { mapsTo, relationship } from '../../builders/build-decorators'; import { NormalizedDSpaceObject } from '../normalized-dspace-object.model'; @@ -56,17 +56,17 @@ export class NormalizedRelationshipType extends NormalizedObject { rightMinCardinality: number; /** - * The type of Entity found to the left of this RelationshipType + * The type of Item found to the left of this RelationshipType */ @autoserialize - @relationship(ResourceType.EntityType, false) + @relationship(ResourceType.ItemType, false) leftType: string; /** - * The type of Entity found to the right of this RelationshipType + * The type of Item found to the right of this RelationshipType */ @autoserialize - @relationship(ResourceType.EntityType, false) + @relationship(ResourceType.ItemType, false) rightType: string; /** diff --git a/src/app/core/cache/models/entities/normalized-relationship.model.ts b/src/app/core/cache/models/items/normalized-relationship.model.ts similarity index 75% rename from src/app/core/cache/models/entities/normalized-relationship.model.ts rename to src/app/core/cache/models/items/normalized-relationship.model.ts index 598687bbde..5d0ca2a9ad 100644 --- a/src/app/core/cache/models/entities/normalized-relationship.model.ts +++ b/src/app/core/cache/models/items/normalized-relationship.model.ts @@ -1,5 +1,5 @@ import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize'; -import { Relationship } from '../../../shared/entities/relationship.model'; +import { Relationship } from '../../../shared/item-relationships/relationship.model'; import { ResourceType } from '../../../shared/resource-type'; import { mapsTo, relationship } from '../../builders/build-decorators'; import { NormalizedObject } from '../normalized-object.model'; @@ -19,25 +19,25 @@ export class NormalizedRelationship extends NormalizedObject { id: string; /** - * The identifier of the Entity to the left side of this Relationship + * The identifier of the Item to the left side of this Relationship */ @autoserialize leftId: string; /** - * The identifier of the Entity to the right side of this Relationship + * The identifier of the Item to the right side of this Relationship */ @autoserialize rightId: string; /** - * The place of the Entity to the left side of this Relationship + * The place of the Item to the left side of this Relationship */ @autoserialize leftPlace: number; /** - * The place of the Entity to the right side of this Relationship + * The place of the Item to the right side of this Relationship */ @autoserialize rightPlace: number; diff --git a/src/app/core/cache/models/normalized-object-factory.ts b/src/app/core/cache/models/normalized-object-factory.ts index 2f8cab737f..e3b2f8dcd2 100644 --- a/src/app/core/cache/models/normalized-object-factory.ts +++ b/src/app/core/cache/models/normalized-object-factory.ts @@ -1,6 +1,6 @@ -import { NormalizedEntityType } from './entities/normalized-entity-type.model'; -import { NormalizedRelationshipType } from './entities/normalized-relationship-type.model'; -import { NormalizedRelationship } from './entities/normalized-relationship.model'; +import { NormalizedItemType } from './items/normalized-item-type.model'; +import { NormalizedRelationshipType } from './items/normalized-relationship-type.model'; +import { NormalizedRelationship } from './items/normalized-relationship.model'; import { NormalizedBitstream } from './normalized-bitstream.model'; import { NormalizedBundle } from './normalized-bundle.model'; import { NormalizedItem } from './normalized-item.model'; @@ -44,8 +44,8 @@ export class NormalizedObjectFactory { case ResourceType.RelationshipType: { return NormalizedRelationshipType } - case ResourceType.EntityType: { - return NormalizedEntityType + case ResourceType.ItemType: { + return NormalizedItemType } case ResourceType.EPerson: { return NormalizedEPerson diff --git a/src/app/core/shared/entities/entity-type.model.ts b/src/app/core/shared/item-relationships/item-type.model.ts similarity index 65% rename from src/app/core/shared/entities/entity-type.model.ts rename to src/app/core/shared/item-relationships/item-type.model.ts index 4532e05b19..e4f98ab653 100644 --- a/src/app/core/shared/entities/entity-type.model.ts +++ b/src/app/core/shared/item-relationships/item-type.model.ts @@ -2,11 +2,11 @@ import { CacheableObject } from '../../cache/object-cache.reducer'; import { ResourceType } from '../resource-type'; /** - * Describes a type of Entity + * Describes a type of Item */ -export class EntityType implements CacheableObject { +export class ItemType implements CacheableObject { /** - * The identifier of this EntityType + * The identifier of this ItemType */ id: string; @@ -21,7 +21,7 @@ export class EntityType implements CacheableObject { type: ResourceType; /** - * The universally unique identifier of this EntityType + * The universally unique identifier of this ItemType */ uuid: string; } diff --git a/src/app/core/shared/entities/relationship-type.model.ts b/src/app/core/shared/item-relationships/relationship-type.model.ts similarity index 80% rename from src/app/core/shared/entities/relationship-type.model.ts rename to src/app/core/shared/item-relationships/relationship-type.model.ts index 05dd0cd754..404d8cdb4b 100644 --- a/src/app/core/shared/entities/relationship-type.model.ts +++ b/src/app/core/shared/item-relationships/relationship-type.model.ts @@ -2,10 +2,10 @@ import { Observable } from 'rxjs'; import { CacheableObject } from '../../cache/object-cache.reducer'; import { RemoteData } from '../../data/remote-data'; import { ResourceType } from '../resource-type'; -import { EntityType } from './entity-type.model'; +import { ItemType } from './item-type.model'; /** - * Describes a type of Relationship between multiple possible Entities + * Describes a type of Relationship between multiple possible Items */ export class RelationshipType implements CacheableObject { /** @@ -64,12 +64,12 @@ export class RelationshipType implements CacheableObject { rightMinCardinality: number; /** - * The type of Entity found to the left of this RelationshipType + * The type of Item found to the left of this RelationshipType */ - leftType: Observable>; + leftType: Observable>; /** - * The type of Entity found to the right of this RelationshipType + * The type of Item found to the right of this RelationshipType */ - rightType: Observable>; + rightType: Observable>; } diff --git a/src/app/core/shared/entities/relationship.model.ts b/src/app/core/shared/item-relationships/relationship.model.ts similarity index 73% rename from src/app/core/shared/entities/relationship.model.ts rename to src/app/core/shared/item-relationships/relationship.model.ts index ef76b0a32a..df8f04cd8a 100644 --- a/src/app/core/shared/entities/relationship.model.ts +++ b/src/app/core/shared/item-relationships/relationship.model.ts @@ -5,7 +5,7 @@ import { ResourceType } from '../resource-type'; import { RelationshipType } from './relationship-type.model'; /** - * Describes a Relationship between two Entities + * Describes a Relationship between two Items */ export class Relationship implements CacheableObject { /** @@ -29,22 +29,22 @@ export class Relationship implements CacheableObject { id: string; /** - * The identifier of the Entity to the left side of this Relationship + * The identifier of the Item to the left side of this Relationship */ leftId: string; /** - * The identifier of the Entity to the right side of this Relationship + * The identifier of the Item to the right side of this Relationship */ rightId: string; /** - * The place of the Entity to the left side of this Relationship + * The place of the Item to the left side of this Relationship */ leftPlace: number; /** - * The place of the Entity to the right side of this Relationship + * The place of the Item to the right side of this Relationship */ rightPlace: number; diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index e8646777cf..7dadfafdd9 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -7,7 +7,7 @@ import { RemoteData } from '../data/remote-data'; import { Bitstream } from './bitstream.model'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { PaginatedList } from '../data/paginated-list'; -import { Relationship } from './entities/relationship.model'; +import { Relationship } from './item-relationships/relationship.model'; export class Item extends DSpaceObject { diff --git a/src/app/core/shared/resource-type.ts b/src/app/core/shared/resource-type.ts index 4251f68e35..c61989dcc3 100644 --- a/src/app/core/shared/resource-type.ts +++ b/src/app/core/shared/resource-type.ts @@ -11,5 +11,5 @@ export enum ResourceType { ResourcePolicy = 'resourcePolicy', Relationship = 'relationship', RelationshipType = 'relationshiptype', - EntityType = 'entitytype', + ItemType = 'entitytype', } diff --git a/src/app/shared/entities/entity-type-decorator.ts b/src/app/shared/items/item-type-decorator.ts similarity index 62% rename from src/app/shared/entities/entity-type-decorator.ts rename to src/app/shared/items/item-type-decorator.ts index 9af226ea3d..509a1c754a 100644 --- a/src/app/shared/entities/entity-type-decorator.ts +++ b/src/app/shared/items/item-type-decorator.ts @@ -1,16 +1,16 @@ import { hasNoValue, hasValue } from '../empty.util'; import { ElementViewMode } from '../view-mode'; -export const DEFAULT_ENTITY_TYPE = 'Default'; +export const DEFAULT_ITEM_TYPE = 'Default'; const map = new Map(); /** - * Decorator used for rendering simple item pages for an Entity by type and viewMode + * Decorator used for rendering simple item pages by type and viewMode * @param type * @param viewMode */ -export function rendersEntityType(type: string, viewMode: ElementViewMode) { +export function rendersItemType(type: string, viewMode: ElementViewMode) { return function decorator(component: any) { if (hasNoValue(map.get(viewMode))) { map.set(viewMode, new Map()); @@ -23,14 +23,14 @@ export function rendersEntityType(type: string, viewMode: ElementViewMode) { } /** - * Get the component used for rendering an entity by type and viewMode + * Get the component used for rendering an item by type and viewMode * @param type * @param viewMode */ -export function getComponentByEntityType(type: string, viewMode: ElementViewMode) { +export function getComponentByItemType(type: string, viewMode: ElementViewMode) { let component = map.get(viewMode).get(type); if (hasNoValue(component)) { - component = map.get(viewMode).get(DEFAULT_ENTITY_TYPE); + component = map.get(viewMode).get(DEFAULT_ITEM_TYPE); } return component; } diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.html b/src/app/shared/items/switcher/item-type-switcher.component.html similarity index 100% rename from src/app/shared/entities/switcher/entity-type-switcher.component.html rename to src/app/shared/items/switcher/item-type-switcher.component.html diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.scss b/src/app/shared/items/switcher/item-type-switcher.component.scss similarity index 100% rename from src/app/shared/entities/switcher/entity-type-switcher.component.scss rename to src/app/shared/items/switcher/item-type-switcher.component.scss diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts similarity index 63% rename from src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts rename to src/app/shared/items/switcher/item-type-switcher.component.spec.ts index 053b03512a..67f5309793 100644 --- a/src/app/shared/entities/switcher/entity-type-switcher.component.spec.ts +++ b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts @@ -1,4 +1,4 @@ -import { EntityTypeSwitcherComponent } from './entity-type-switcher.component'; +import { ItemTypeSwitcherComponent } from './item-type-switcher.component'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { of as observableOf } from 'rxjs'; @@ -6,8 +6,8 @@ import { PageInfo } from '../../../core/shared/page-info.model'; import { Item } from '../../../core/shared/item.model'; import { PaginatedList } from '../../../core/data/paginated-list'; import { RemoteData } from '../../../core/data/remote-data'; -import * as decorator from '../entity-type-decorator'; -import { getComponentByEntityType } from '../entity-type-decorator'; +import * as decorator from '../item-type-decorator'; +import { getComponentByItemType } from '../item-type-decorator'; import { ElementViewMode } from '../../view-mode'; import createSpy = jasmine.createSpy; @@ -28,23 +28,23 @@ const mockItem: Item = Object.assign(new Item(), { }); const viewMode = ElementViewMode.Full; -describe('EntityTypeSwitcherComponent', () => { - let comp: EntityTypeSwitcherComponent; - let fixture: ComponentFixture; +describe('ItemTypeSwitcherComponent', () => { + let comp: ItemTypeSwitcherComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ EntityTypeSwitcherComponent ], + declarations: [ ItemTypeSwitcherComponent ], schemas: [ NO_ERRORS_SCHEMA ] }).compileComponents(); // compile template and css })); beforeEach(async(() => { - fixture = TestBed.createComponent(EntityTypeSwitcherComponent); + fixture = TestBed.createComponent(ItemTypeSwitcherComponent); comp = fixture.componentInstance; comp.object = mockItem; comp.viewMode = viewMode; - spyOnProperty(decorator, 'getComponentByEntityType').and.returnValue(createSpy('getComponentByEntityType')) + spyOnProperty(decorator, 'getComponentByItemType').and.returnValue(createSpy('getComponentByItemType')) })); describe('when calling getComponent', () => { @@ -52,8 +52,8 @@ describe('EntityTypeSwitcherComponent', () => { comp.getComponent(); }); - it('should call getComponentByEntityType with parameters type and viewMode', () => { - expect(decorator.getComponentByEntityType).toHaveBeenCalledWith(relationType, viewMode); + it('should call getComponentByItemType with parameters type and viewMode', () => { + expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode); }); }); diff --git a/src/app/shared/entities/switcher/entity-type-switcher.component.ts b/src/app/shared/items/switcher/item-type-switcher.component.ts similarity index 82% rename from src/app/shared/entities/switcher/entity-type-switcher.component.ts rename to src/app/shared/items/switcher/item-type-switcher.component.ts index 4b910ebebc..5599326e98 100644 --- a/src/app/shared/entities/switcher/entity-type-switcher.component.ts +++ b/src/app/shared/items/switcher/item-type-switcher.component.ts @@ -3,20 +3,20 @@ import { SearchResult } from '../../../+search-page/search-result.model'; import { Item } from '../../../core/shared/item.model'; import { hasValue } from '../../empty.util'; import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model'; -import { getComponentByEntityType } from '../entity-type-decorator'; +import { getComponentByItemType } from '../item-type-decorator'; import { ElementViewMode } from '../../view-mode'; export const ITEM: InjectionToken = new InjectionToken('item'); @Component({ - selector: 'ds-entity-type-switcher', - styleUrls: ['./entity-type-switcher.component.scss'], - templateUrl: './entity-type-switcher.component.html' + selector: 'ds-item-type-switcher', + styleUrls: ['./item-type-switcher.component.scss'], + templateUrl: './item-type-switcher.component.html' }) /** * Component for determining what component to use depending on the item's relationship type (relationship.type) */ -export class EntityTypeSwitcherComponent implements OnInit { +export class ItemTypeSwitcherComponent implements OnInit { /** * The item to determine the component for */ @@ -57,6 +57,6 @@ export class EntityTypeSwitcherComponent implements OnInit { } const type = item.findMetadata('relationship.type'); - return getComponentByEntityType(type, this.viewMode); + return getComponentByItemType(type, this.viewMode); } } diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.html b/src/app/shared/object-list/item-list-element/entity-list-element.component.html deleted file mode 100644 index 777f131a23..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts deleted file mode 100644 index 7f8278fcd3..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('JournalIssue', ElementViewMode.SetElement) -@Component({ - selector: 'ds-journal-issue-list-element', - styleUrls: ['./journal-issue-list-element.component.scss'], - templateUrl: './journal-issue-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Journal Issue - */ -export class JournalIssueListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts deleted file mode 100644 index e68b7e462d..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('JournalVolume', ElementViewMode.SetElement) -@Component({ - selector: 'ds-journal-volume-list-element', - styleUrls: ['./journal-volume-list-element.component.scss'], - templateUrl: './journal-volume-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Journal Volume - */ -export class JournalVolumeListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts deleted file mode 100644 index 23d030c77f..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('Journal', ElementViewMode.SetElement) -@Component({ - selector: 'ds-journal-list-element', - styleUrls: ['./journal-list-element.component.scss'], - templateUrl: './journal-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Journal - */ -export class JournalListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts deleted file mode 100644 index 72bd143971..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('OrgUnit', ElementViewMode.SetElement) -@Component({ - selector: 'ds-orgunit-list-element', - styleUrls: ['./orgunit-list-element.component.scss'], - templateUrl: './orgunit-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Organisation Unit - */ -export class OrgUnitListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts deleted file mode 100644 index 32e378c8c7..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('Person', ElementViewMode.SetElement) -@Component({ - selector: 'ds-person-list-element', - styleUrls: ['./person-list-element.component.scss'], - templateUrl: './person-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Person - */ -export class PersonListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts deleted file mode 100644 index ffffd70a33..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Component } from '@angular/core'; -import { rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('Project', ElementViewMode.SetElement) -@Component({ - selector: 'ds-project-list-element', - styleUrls: ['./project-list-element.component.scss'], - templateUrl: './project-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Project - */ -export class ProjectListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts deleted file mode 100644 index 5e98a5657e..0000000000 --- a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; -import { DEFAULT_ENTITY_TYPE, rendersEntityType } from '../../../../entities/entity-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; -import { EntitySearchResultComponent } from '../entity-search-result-component'; - -@rendersEntityType('Publication', ElementViewMode.SetElement) -@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.SetElement) -@Component({ - selector: 'ds-publication-list-element', - styleUrls: ['./publication-list-element.component.scss'], - templateUrl: './publication-list-element.component.html' -}) -/** - * The component for displaying a list element for an item with entity type Publication - */ -export class PublicationListElementComponent extends EntitySearchResultComponent { -} diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.html b/src/app/shared/object-list/item-list-element/item-list-element.component.html new file mode 100644 index 0000000000..8cdf8cac6a --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts similarity index 62% rename from src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts index b32db3df06..11fdae7e6d 100644 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.spec.ts @@ -1,13 +1,12 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { EntityListElementComponent } from './entity-list-element.component'; +import { ItemListElementComponent } from './item-list-element.component'; import { Item } from '../../../core/shared/item.model'; -import { Observable } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { PaginatedList } from '../../../core/data/paginated-list'; import { PageInfo } from '../../../core/shared/page-info.model'; import { By } from '@angular/platform-browser'; -import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity.component.spec'; +import { createRelationshipsObservable } from '../../../+item-page/simple/item-types/shared/item.component.spec'; import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { @@ -16,32 +15,32 @@ const mockItem: Item = Object.assign(new Item(), { relationships: createRelationshipsObservable() }); -describe('EntityListElementComponent', () => { - let comp: EntityListElementComponent; - let fixture: ComponentFixture; +describe('ItemListElementComponent', () => { + let comp: ItemListElementComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [], - declarations: [EntityListElementComponent], + declarations: [ItemListElementComponent], providers: [ { provide: 'objectElementProvider', useValue: mockItem } ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(EntityListElementComponent, { + }).overrideComponent(ItemListElementComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(EntityListElementComponent); + fixture = TestBed.createComponent(ItemListElementComponent); comp = fixture.componentInstance; fixture.detectChanges(); })); - it('should call an entity-type-switcher component and pass the item', () => { - const entityTypeSwitcher = fixture.debugElement.query(By.css('ds-entity-type-switcher')).componentInstance; - expect(entityTypeSwitcher.object).toBe(mockItem); + it('should call an item-type-switcher component and pass the item', () => { + const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).componentInstance; + expect(itemTypeSwitcher.object).toBe(mockItem); }); }); diff --git a/src/app/shared/object-list/item-list-element/entity-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.ts similarity index 59% rename from src/app/shared/object-list/item-list-element/entity-list-element.component.ts rename to src/app/shared/object-list/item-list-element/item-list-element.component.ts index e71a868579..a9677c9777 100644 --- a/src/app/shared/object-list/item-list-element/entity-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.ts @@ -7,16 +7,16 @@ import { AbstractListableElementComponent } from '../../object-collection/shared import { SetViewMode } from '../../view-mode'; @Component({ - selector: 'ds-entity-list-element', - styleUrls: ['./entity-list-element.component.scss'], - templateUrl: './entity-list-element.component.html' + selector: 'ds-item-list-element', + styleUrls: ['./item-list-element.component.scss'], + templateUrl: './item-list-element.component.html' }) /** - * The component used to list entities depending on type - * Uses entity-type-switcher to determine which components to use for displaying the list + * The component used to list items depending on type + * Uses item-type-switcher to determine which components to use for displaying the list */ @renderElementsFor(Item, SetViewMode.List) -export class EntityListElementComponent extends AbstractListableElementComponent { +export class ItemListElementComponent extends AbstractListableElementComponent { ElementViewMode = viewMode.ElementViewMode; } diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts b/src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts similarity index 77% rename from src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts rename to src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts index ed53c45395..17e7b7f173 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result-component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts @@ -1,19 +1,19 @@ import { Component, Inject } from '@angular/core'; import { Item } from '../../../../core/shared/item.model'; import { hasValue } from '../../../empty.util'; -import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../items/switcher/item-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { SearchResultListElementComponent } from '../../search-result-list-element/search-result-list-element.component'; /** - * A generic component for displaying entity list elements + * A generic component for displaying item list elements */ @Component({ - selector: 'ds-entity-search-result', + selector: 'ds-item-search-result', template: '' }) -export class EntitySearchResultComponent extends SearchResultListElementComponent { +export class ItemSearchResultComponent extends SearchResultListElementComponent { item: Item; constructor( diff --git a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts similarity index 75% rename from src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts index e9a871985b..be163f7a6c 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/entity-search-result.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts @@ -2,15 +2,14 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { EntitySearchResultComponent } from './entity-search-result-component'; +import { ItemSearchResultComponent } from './item-search-result-component'; import { Item } from '../../../../core/shared/item.model'; -import { Observable } from 'rxjs'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { ITEM } from '../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../items/switcher/item-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; -import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/item-types/shared/item.component.spec'; import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { @@ -23,27 +22,27 @@ const mockSearchResult = { hitHighlights: [] } as ItemSearchResult; -describe('EntitySearchResultComponent', () => { - let comp: EntitySearchResultComponent; - let fixture: ComponentFixture; +describe('ItemSearchResultComponent', () => { + let comp: ItemSearchResultComponent; + let fixture: ComponentFixture; describe('when injecting an Item', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [EntitySearchResultComponent, TruncatePipe], + declarations: [ItemSearchResultComponent, TruncatePipe], providers: [ {provide: TruncatableService, useValue: {}}, {provide: ITEM, useValue: mockItem} ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(EntitySearchResultComponent, { + }).overrideComponent(ItemSearchResultComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(EntitySearchResultComponent); + fixture = TestBed.createComponent(ItemSearchResultComponent); comp = fixture.componentInstance; })); @@ -57,20 +56,20 @@ describe('EntitySearchResultComponent', () => { describe('when injecting an ItemSearchResult', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [EntitySearchResultComponent, TruncatePipe], + declarations: [ItemSearchResultComponent, TruncatePipe], providers: [ {provide: TruncatableService, useValue: {}}, {provide: ITEM, useValue: mockSearchResult} ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(EntitySearchResultComponent, { + }).overrideComponent(ItemSearchResultComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(EntitySearchResultComponent); + fixture = TestBed.createComponent(ItemSearchResultComponent); comp = fixture.componentInstance; })); diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts similarity index 96% rename from src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts index 07be07d051..4f829a6818 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { JournalIssueListElementComponent } from './journal-issue-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts new file mode 100644 index 0000000000..3b47d1ab51 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('JournalIssue', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-issue-list-element', + styleUrls: ['./journal-issue-list-element.component.scss'], + templateUrl: './journal-issue-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Journal Issue + */ +export class JournalIssueListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts similarity index 96% rename from src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts index 7388d12f25..93071289b8 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { JournalVolumeListElementComponent } from './journal-volume-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts new file mode 100644 index 0000000000..39595122e1 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('JournalVolume', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-volume-list-element', + styleUrls: ['./journal-volume-list-element.component.scss'], + templateUrl: './journal-volume-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Journal Volume + */ +export class JournalVolumeListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts similarity index 95% rename from src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts index 8fd8d870fa..abdeb9c00f 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/journal/journal-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { JournalListElementComponent } from './journal-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts new file mode 100644 index 0000000000..113594630e --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('Journal', ElementViewMode.SetElement) +@Component({ + selector: 'ds-journal-list-element', + styleUrls: ['./journal-list-element.component.scss'], + templateUrl: './journal-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Journal + */ +export class JournalListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts similarity index 95% rename from src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts index 678e1e42bc..14b86d720a 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { OrgUnitListElementComponent } from './orgunit-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts new file mode 100644 index 0000000000..6fe95e6917 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('OrgUnit', ElementViewMode.SetElement) +@Component({ + selector: 'ds-orgunit-list-element', + styleUrls: ['./orgunit-list-element.component.scss'], + templateUrl: './orgunit-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Organisation Unit + */ +export class OrgUnitListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts similarity index 95% rename from src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts index 3005c852af..836588c565 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/person/person-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { PersonListElementComponent } from './person-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts new file mode 100644 index 0000000000..5fd3c63f27 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('Person', ElementViewMode.SetElement) +@Component({ + selector: 'ds-person-list-element', + styleUrls: ['./person-list-element.component.scss'], + templateUrl: './person-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Person + */ +export class PersonListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts similarity index 95% rename from src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts index b4437ceaea..cd0cce95f2 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/project/project-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { ProjectListElementComponent } from './project-list-element.component'; import { of as observableOf } from 'rxjs'; diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts new file mode 100644 index 0000000000..ad3cb824ea --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('Project', ElementViewMode.SetElement) +@Component({ + selector: 'ds-project-list-element', + styleUrls: ['./project-list-element.component.scss'], + templateUrl: './project-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Project + */ +export class ProjectListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.html rename to src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.scss b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.scss rename to src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.scss diff --git a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts similarity index 97% rename from src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts index 9a2fdbcc0b..6f596d1938 100644 --- a/src/app/shared/object-list/item-list-element/entity-types/publication/publication-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts @@ -1,12 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { Observable } from 'rxjs'; import { PublicationListElementComponent } from './publication-list-element.component'; import { Item } from '../../../../../core/shared/item.model'; import { TruncatePipe } from '../../../../utils/truncate.pipe'; import { TruncatableService } from '../../../../truncatable/truncatable.service'; -import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component'; +import { ITEM } from '../../../../items/switcher/item-type-switcher.component'; import { of as observableOf } from 'rxjs'; let publicationListElementComponent: PublicationListElementComponent; diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts new file mode 100644 index 0000000000..777cfc6013 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { DEFAULT_ITEM_TYPE, rendersItemType } from '../../../../items/item-type-decorator'; +import { ElementViewMode } from '../../../../view-mode'; +import { ItemSearchResultComponent } from '../item-search-result-component'; + +@rendersItemType('Publication', ElementViewMode.SetElement) +@rendersItemType(DEFAULT_ITEM_TYPE, ElementViewMode.SetElement) +@Component({ + selector: 'ds-publication-list-element', + styleUrls: ['./publication-list-element.component.scss'], + templateUrl: './publication-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Publication + */ +export class PublicationListElementComponent extends ItemSearchResultComponent { +} diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html index 777f131a23..8cdf8cac6a 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html @@ -1 +1 @@ - + diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index b1f511868a..28fbbc419a 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -9,7 +9,7 @@ import { ItemSearchResultListElementComponent } from './item-search-result-list- import { By } from '@angular/platform-browser'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../utils/truncate.pipe'; -import { createRelationshipsObservable } from '../../../../+item-page/simple/entity-types/shared/entity.component.spec'; +import { createRelationshipsObservable } from '../../../../+item-page/simple/item-types/shared/item.component.spec'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; const mockItem: Item = Object.assign(new Item(), { @@ -81,9 +81,9 @@ describe('ItemSearchResultListElementComponent', () => { fixture.detectChanges(); })); - it('should call an entity-type-switcher component and pass the item', () => { - const entityTypeSwitcher = fixture.debugElement.query(By.css('ds-entity-type-switcher')).componentInstance; - expect(entityTypeSwitcher.object).toBe(mockItem); + it('should call an item-type-switcher component and pass the item', () => { + const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).componentInstance; + expect(itemTypeSwitcher.object).toBe(mockItem); }); }); diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 106b330744..410425b865 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -13,15 +13,15 @@ import { import { TranslateModule } from '@ngx-translate/core'; import { NgxPaginationModule } from 'ngx-pagination'; -import { EntityTypeSwitcherComponent } from './entities/switcher/entity-type-switcher.component'; -import { EntitySearchResultComponent } from './object-list/item-list-element/entity-types/entity-search-result-component'; -import { PublicationListElementComponent } from './object-list/item-list-element/entity-types/publication/publication-list-element.component'; -import { OrgUnitListElementComponent } from './object-list/item-list-element/entity-types/orgunit/orgunit-list-element.component'; -import { PersonListElementComponent } from './object-list/item-list-element/entity-types/person/person-list-element.component'; -import { ProjectListElementComponent } from './object-list/item-list-element/entity-types/project/project-list-element.component'; -import { JournalListElementComponent } from './object-list/item-list-element/entity-types/journal/journal-list-element.component'; -import { JournalVolumeListElementComponent } from './object-list/item-list-element/entity-types/journal-volume/journal-volume-list-element.component'; -import { JournalIssueListElementComponent } from './object-list/item-list-element/entity-types/journal-issue/journal-issue-list-element.component'; +import { ItemTypeSwitcherComponent } from './items/switcher/item-type-switcher.component'; +import { ItemSearchResultComponent } from './object-list/item-list-element/item-types/item-search-result-component'; +import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component'; +import { OrgUnitListElementComponent } from './object-list/item-list-element/item-types/orgunit/orgunit-list-element.component'; +import { PersonListElementComponent } from './object-list/item-list-element/item-types/person/person-list-element.component'; +import { ProjectListElementComponent } from './object-list/item-list-element/item-types/project/project-list-element.component'; +import { JournalListElementComponent } from './object-list/item-list-element/item-types/journal/journal-list-element.component'; +import { JournalVolumeListElementComponent } from './object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component'; +import { JournalIssueListElementComponent } from './object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component'; import { FileUploadModule } from 'ng2-file-upload'; @@ -34,7 +34,7 @@ import { ConsolePipe } from './utils/console.pipe'; import { CollectionListElementComponent } from './object-list/collection-list-element/collection-list-element.component'; import { CommunityListElementComponent } from './object-list/community-list-element/community-list-element.component'; -import { EntityListElementComponent } from './object-list/item-list-element/entity-list-element.component'; +import { ItemListElementComponent } from './object-list/item-list-element/item-list-element.component'; import { SearchResultListElementComponent } from './object-list/search-result-list-element/search-result-list-element.component'; import { WrapperListElementComponent } from './object-list/wrapper-list-element/wrapper-list-element.component'; import { ObjectListComponent } from './object-list/object-list.component'; @@ -167,14 +167,14 @@ const COMPONENTS = [ TruncatableComponent, TruncatablePartComponent, InputSuggestionsComponent, - EntitySearchResultComponent, - EntityTypeSwitcherComponent, + ItemSearchResultComponent, + ItemTypeSwitcherComponent, BrowseByComponent ]; const ENTRY_COMPONENTS = [ // put shared entry components (components that are created dynamically) here - EntityListElementComponent, + ItemListElementComponent, CollectionListElementComponent, CommunityListElementComponent, SearchResultListElementComponent, From 95e9e9166a1d907532584d85e9147e8137f97d24 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 16 Jan 2019 16:34:10 +0100 Subject: [PATCH 080/205] rename ItemSearchResultComponent to prevent confusion with ItemSearchResultListElementComponent --- .../journal-issue-list-element.component.ts | 4 ++-- .../journal-volume-list-element.component.ts | 4 ++-- .../journal/journal-list-element.component.ts | 4 ++-- .../orgunit/orgunit-list-element.component.ts | 4 ++-- .../person/person-list-element.component.ts | 4 ++-- .../project/project-list-element.component.ts | 4 ++-- .../publication-list-element.component.ts | 4 ++-- ...arch-result-list-element.component.spec.ts} | 18 +++++++++--------- ...em-search-result-list-element.component.ts} | 2 +- src/app/shared/shared.module.ts | 4 ++-- 10 files changed, 26 insertions(+), 26 deletions(-) rename src/app/shared/object-list/item-list-element/item-types/{item-search-result.component.spec.ts => typed-item-search-result-list-element.component.spec.ts} (78%) rename src/app/shared/object-list/item-list-element/item-types/{item-search-result-component.ts => typed-item-search-result-list-element.component.ts} (90%) diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts index 3b47d1ab51..4f18822773 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('JournalIssue', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Journal Issue */ -export class JournalIssueListElementComponent extends ItemSearchResultComponent { +export class JournalIssueListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts index 39595122e1..a95dca88f9 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('JournalVolume', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Journal Volume */ -export class JournalVolumeListElementComponent extends ItemSearchResultComponent { +export class JournalVolumeListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts index 113594630e..28be0d8149 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('Journal', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Journal */ -export class JournalListElementComponent extends ItemSearchResultComponent { +export class JournalListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts index 6fe95e6917..e3433f7e76 100644 --- a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('OrgUnit', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Organisation Unit */ -export class OrgUnitListElementComponent extends ItemSearchResultComponent { +export class OrgUnitListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts index 5fd3c63f27..b0369458f9 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('Person', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Person */ -export class PersonListElementComponent extends ItemSearchResultComponent { +export class PersonListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts index ad3cb824ea..6b130e4786 100644 --- a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('Project', ElementViewMode.SetElement) @Component({ @@ -12,5 +12,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Project */ -export class ProjectListElementComponent extends ItemSearchResultComponent { +export class ProjectListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts index 777cfc6013..4575ab5cca 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; import { DEFAULT_ITEM_TYPE, rendersItemType } from '../../../../items/item-type-decorator'; import { ElementViewMode } from '../../../../view-mode'; -import { ItemSearchResultComponent } from '../item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; @rendersItemType('Publication', ElementViewMode.SetElement) @rendersItemType(DEFAULT_ITEM_TYPE, ElementViewMode.SetElement) @@ -13,5 +13,5 @@ import { ItemSearchResultComponent } from '../item-search-result-component'; /** * The component for displaying a list element for an item of the type Publication */ -export class PublicationListElementComponent extends ItemSearchResultComponent { +export class PublicationListElementComponent extends TypedItemSearchResultListElementComponent { } diff --git a/src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts similarity index 78% rename from src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts rename to src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts index be163f7a6c..e97f632033 100644 --- a/src/app/shared/object-list/item-list-element/item-types/item-search-result.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts @@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { TruncatableService } from '../../../truncatable/truncatable.service'; import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ItemSearchResultComponent } from './item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from './typed-item-search-result-list-element.component'; import { Item } from '../../../../core/shared/item.model'; import { RemoteData } from '../../../../core/data/remote-data'; import { PaginatedList } from '../../../../core/data/paginated-list'; @@ -23,26 +23,26 @@ const mockSearchResult = { } as ItemSearchResult; describe('ItemSearchResultComponent', () => { - let comp: ItemSearchResultComponent; - let fixture: ComponentFixture; + let comp: TypedItemSearchResultListElementComponent; + let fixture: ComponentFixture; describe('when injecting an Item', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ItemSearchResultComponent, TruncatePipe], + declarations: [TypedItemSearchResultListElementComponent, TruncatePipe], providers: [ {provide: TruncatableService, useValue: {}}, {provide: ITEM, useValue: mockItem} ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ItemSearchResultComponent, { + }).overrideComponent(TypedItemSearchResultListElementComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(ItemSearchResultComponent); + fixture = TestBed.createComponent(TypedItemSearchResultListElementComponent); comp = fixture.componentInstance; })); @@ -56,20 +56,20 @@ describe('ItemSearchResultComponent', () => { describe('when injecting an ItemSearchResult', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ItemSearchResultComponent, TruncatePipe], + declarations: [TypedItemSearchResultListElementComponent, TruncatePipe], providers: [ {provide: TruncatableService, useValue: {}}, {provide: ITEM, useValue: mockSearchResult} ], schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(ItemSearchResultComponent, { + }).overrideComponent(TypedItemSearchResultListElementComponent, { set: {changeDetection: ChangeDetectionStrategy.Default} }).compileComponents(); })); beforeEach(async(() => { - fixture = TestBed.createComponent(ItemSearchResultComponent); + fixture = TestBed.createComponent(TypedItemSearchResultListElementComponent); comp = fixture.componentInstance; })); diff --git a/src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts similarity index 90% rename from src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts rename to src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts index 17e7b7f173..57b339da53 100644 --- a/src/app/shared/object-list/item-list-element/item-types/item-search-result-component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.ts @@ -13,7 +13,7 @@ import { SearchResultListElementComponent } from '../../search-result-list-eleme selector: 'ds-item-search-result', template: '' }) -export class ItemSearchResultComponent extends SearchResultListElementComponent { +export class TypedItemSearchResultListElementComponent extends SearchResultListElementComponent { item: Item; constructor( diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 410425b865..e467839b57 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -14,7 +14,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { NgxPaginationModule } from 'ngx-pagination'; import { ItemTypeSwitcherComponent } from './items/switcher/item-type-switcher.component'; -import { ItemSearchResultComponent } from './object-list/item-list-element/item-types/item-search-result-component'; +import { TypedItemSearchResultListElementComponent } from './object-list/item-list-element/item-types/typed-item-search-result-list-element.component'; import { PublicationListElementComponent } from './object-list/item-list-element/item-types/publication/publication-list-element.component'; import { OrgUnitListElementComponent } from './object-list/item-list-element/item-types/orgunit/orgunit-list-element.component'; import { PersonListElementComponent } from './object-list/item-list-element/item-types/person/person-list-element.component'; @@ -167,7 +167,7 @@ const COMPONENTS = [ TruncatableComponent, TruncatablePartComponent, InputSuggestionsComponent, - ItemSearchResultComponent, + TypedItemSearchResultListElementComponent, ItemTypeSwitcherComponent, BrowseByComponent ]; From 8ae8498ab115d66035e4c9ba7b551fd4979f0ead Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Wed, 16 Jan 2019 17:47:53 +0100 Subject: [PATCH 081/205] rename 'entity' to 'item' in en.json --- resources/i18n/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 667d5132ba..e3126b16b8 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -207,7 +207,7 @@ "f.dateIssued.max": "End date", "f.subject": "Subject", "f.has_content_in_original_bundle": "Has files", - "f.entityType": "Entity Type" + "f.entityType": "Item Type" }, "filter": { "show-more": "Show more", @@ -237,8 +237,8 @@ "head": "Has files" }, "entityType": { - "placeholder": "Entity Type", - "head": "Entity Type" + "placeholder": "Item Type", + "head": "Item Type" } } } From 57999ad3822e44746895702189efa073f4243b01 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Mon, 21 Jan 2019 18:27:07 +0100 Subject: [PATCH 082/205] add the ability to mix text based metadata fields, authority controlled fields and typed items in the same section --- resources/i18n/en.json | 6 +- .../metadata-values.component.spec.ts | 3 +- src/app/+item-page/item-page.module.ts | 4 +- .../simple/item-page.component.html | 2 +- .../+item-page/simple/item-page.component.ts | 5 +- .../journal-issue/journal-issue.component.ts | 4 +- .../journal-volume.component.ts | 4 +- .../item-types/journal/journal.component.ts | 4 +- .../item-types/orgunit/orgunit.component.ts | 4 +- .../item-types/person/person.component.ts | 4 +- .../item-types/project/project.component.ts | 4 +- .../publication/publication.component.html | 8 +- .../publication/publication.component.ts | 14 +- .../item-types/shared/item.component.spec.ts | 124 +++++++++++++++++- .../item-types/shared/item.component.ts | 67 +++++++++- ...etadata-representation-list.component.html | 5 + ...data-representation-list.component.spec.ts | 40 ++++++ .../metadata-representation-list.component.ts | 31 +++++ .../related-items/related-items-component.ts | 5 +- .../related-items.component.html | 2 +- .../core/metadata/metadata.service.spec.ts | 3 +- src/app/core/shared/dspace-object.model.ts | 21 ++- ...item-metadata-representation.model.spec.ts | 36 +++++ .../item-metadata-representation.model.ts | 48 +++++++ .../metadata-representation.model.ts | 31 +++++ .../metadatum-representation.model.spec.ts | 54 ++++++++ .../metadatum-representation.model.ts | 38 ++++++ src/app/core/shared/metadatum.model.ts | 13 ++ src/app/shared/items/item-type-decorator.ts | 48 +++++-- .../item-type-switcher.component.spec.ts | 41 +++++- .../switcher/item-type-switcher.component.ts | 13 +- .../item-list-element.component.html | 2 +- .../item-list-element.component.ts | 4 +- .../journal-issue-list-element.component.ts | 4 +- .../journal-volume-list-element.component.ts | 4 +- .../journal/journal-list-element.component.ts | 4 +- .../orgunit/orgunit-list-element.component.ts | 4 +- .../person/person-list-element.component.html | 1 - .../person/person-list-element.component.ts | 4 +- ...erson-metadata-list-element.component.html | 15 +++ .../person-metadata-list-element.component.ts | 16 +++ .../project/project-list-element.component.ts | 4 +- .../publication-list-element.component.ts | 6 +- .../item-metadata-list-element.component.html | 2 + ...em-metadata-list-element.component.spec.ts | 38 ++++++ .../item-metadata-list-element.component.ts | 25 ++++ ...a-representation-list-element.component.ts | 15 +++ ...-text-metadata-list-element.component.html | 3 + ...xt-metadata-list-element.component.spec.ts | 39 ++++++ ...in-text-metadata-list-element.component.ts | 19 +++ ...-search-result-list-element.component.html | 2 +- ...em-search-result-list-element.component.ts | 4 +- src/app/shared/shared.module.ts | 18 ++- src/app/shared/view-mode.ts | 12 +- 54 files changed, 833 insertions(+), 98 deletions(-) create mode 100644 src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html create mode 100644 src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts create mode 100644 src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts create mode 100644 src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts create mode 100644 src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts create mode 100644 src/app/core/shared/metadata-representation/metadata-representation.model.ts create mode 100644 src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts create mode 100644 src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts create mode 100644 src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html create mode 100644 src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts create mode 100644 src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html create mode 100644 src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts create mode 100644 src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts create mode 100644 src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html create mode 100644 src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index e3126b16b8..667d5132ba 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -207,7 +207,7 @@ "f.dateIssued.max": "End date", "f.subject": "Subject", "f.has_content_in_original_bundle": "Has files", - "f.entityType": "Item Type" + "f.entityType": "Entity Type" }, "filter": { "show-more": "Show more", @@ -237,8 +237,8 @@ "head": "Has files" }, "entityType": { - "placeholder": "Item Type", - "head": "Item Type" + "placeholder": "Entity Type", + "head": "Entity Type" } } } diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts index 9447e1c48e..9682386b96 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts @@ -4,6 +4,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader'; import { MetadataValuesComponent } from './metadata-values.component'; import { By } from '@angular/platform-browser'; +import { Metadatum } from '../../../core/shared/metadatum.model'; let comp: MetadataValuesComponent; let fixture: ComponentFixture; @@ -23,7 +24,7 @@ const mockMetadata = [ key: 'journal.identifier.description', language: 'en_US', value: 'desc' - }]; + }] as Metadatum[]; const mockSeperator = '
'; const mockLabel = 'fake.message'; diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index b4e5ba1cd8..41d78f6393 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -29,6 +29,7 @@ import { JournalComponent } from './simple/item-types/journal/journal.component' import { JournalVolumeComponent } from './simple/item-types/journal-volume/journal-volume.component'; import { JournalIssueComponent } from './simple/item-types/journal-issue/journal-issue.component'; import { ItemComponent } from './simple/item-types/shared/item.component'; +import { MetadataRepresentationListComponent } from './simple/metadata-representation-list/metadata-representation-list.component'; @NgModule({ imports: [ @@ -61,7 +62,8 @@ import { ItemComponent } from './simple/item-types/shared/item.component'; GenericItemPageFieldComponent, JournalComponent, JournalIssueComponent, - JournalVolumeComponent + JournalVolumeComponent, + MetadataRepresentationListComponent ], entryComponents: [ PublicationComponent, diff --git a/src/app/+item-page/simple/item-page.component.html b/src/app/+item-page/simple/item-page.component.html index e59d37fb9d..b6de496dc4 100644 --- a/src/app/+item-page/simple/item-page.component.html +++ b/src/app/+item-page/simple/item-page.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/+item-page/simple/item-page.component.ts b/src/app/+item-page/simple/item-page.component.ts index 1cee863070..ac23add738 100644 --- a/src/app/+item-page/simple/item-page.component.ts +++ b/src/app/+item-page/simple/item-page.component.ts @@ -15,6 +15,8 @@ import { fadeInOut } from '../../shared/animations/fade'; import { hasValue } from '../../shared/empty.util'; import * as viewMode from '../../shared/view-mode'; +export const VIEW_MODE_FULL = 'full'; + /** * This component renders a simple item page. * The route parameter 'id' is used to request the item it represents. @@ -46,9 +48,8 @@ export class ItemPageComponent implements OnInit { /** * The view-mode we're currently on - * @type {ElementViewMode} */ - ElementViewMode = viewMode.ElementViewMode; + viewMode = VIEW_MODE_FULL; constructor( private route: ActivatedRoute, diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts index c663b15256..9240616c59 100644 --- a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.ts @@ -4,11 +4,11 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('JournalIssue', ElementViewMode.Full) +@rendersItemType('JournalIssue', VIEW_MODE_FULL) @Component({ selector: 'ds-journal-issue', styleUrls: ['./journal-issue.component.scss'], diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts index ebb6919234..0372fe5d30 100644 --- a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.ts @@ -4,11 +4,11 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('JournalVolume', ElementViewMode.Full) +@rendersItemType('JournalVolume', VIEW_MODE_FULL) @Component({ selector: 'ds-journal-volume', styleUrls: ['./journal-volume.component.scss'], diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.ts b/src/app/+item-page/simple/item-types/journal/journal.component.ts index 3af725062c..12ba91eb6b 100644 --- a/src/app/+item-page/simple/item-types/journal/journal.component.ts +++ b/src/app/+item-page/simple/item-types/journal/journal.component.ts @@ -4,11 +4,11 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('Journal', ElementViewMode.Full) +@rendersItemType('Journal', VIEW_MODE_FULL) @Component({ selector: 'ds-journal', styleUrls: ['./journal.component.scss'], diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts index f1979b0961..1a6ab4ba36 100644 --- a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.ts @@ -3,12 +3,12 @@ import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('OrgUnit', ElementViewMode.Full) +@rendersItemType('OrgUnit', VIEW_MODE_FULL) @Component({ selector: 'ds-orgunit', styleUrls: ['./orgunit.component.scss'], diff --git a/src/app/+item-page/simple/item-types/person/person.component.ts b/src/app/+item-page/simple/item-types/person/person.component.ts index bb98799da5..3cf5a230bf 100644 --- a/src/app/+item-page/simple/item-types/person/person.component.ts +++ b/src/app/+item-page/simple/item-types/person/person.component.ts @@ -4,12 +4,12 @@ import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('Person', ElementViewMode.Full) +@rendersItemType('Person', VIEW_MODE_FULL) @Component({ selector: 'ds-person', styleUrls: ['./person.component.scss'], diff --git a/src/app/+item-page/simple/item-types/project/project.component.ts b/src/app/+item-page/simple/item-types/project/project.component.ts index dfbdacff86..4bdb6012f2 100644 --- a/src/app/+item-page/simple/item-types/project/project.component.ts +++ b/src/app/+item-page/simple/item-types/project/project.component.ts @@ -3,12 +3,12 @@ import { Observable } from 'rxjs'; import { ItemDataService } from '../../../../core/data/item-data.service'; import { Item } from '../../../../core/shared/item.model'; import { rendersItemType } from '../../../../shared/items/item-type-decorator'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { isNotEmpty } from '../../../../shared/empty.util'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('Project', ElementViewMode.Full) +@rendersItemType('Project', VIEW_MODE_FULL) @Component({ selector: 'ds-project', styleUrls: ['./project.component.scss'], diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.html b/src/app/+item-page/simple/item-types/publication/publication.component.html index e619e18b01..b1259be689 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.html +++ b/src/app/+item-page/simple/item-types/publication/publication.component.html @@ -9,10 +9,10 @@
- - + + diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.ts b/src/app/+item-page/simple/item-types/publication/publication.component.ts index b6178893d6..1ea50598c7 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.ts @@ -7,11 +7,12 @@ import { rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; -import { ElementViewMode } from '../../../../shared/view-mode'; import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component'; +import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; +import { VIEW_MODE_FULL } from '../../item-page.component'; -@rendersItemType('Publication', ElementViewMode.Full) -@rendersItemType(DEFAULT_ITEM_TYPE, ElementViewMode.Full) +@rendersItemType('Publication', VIEW_MODE_FULL) +@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_FULL) @Component({ selector: 'ds-publication', styleUrls: ['./publication.component.scss'], @@ -22,7 +23,7 @@ export class PublicationComponent extends ItemComponent implements OnInit { /** * The authors related to this publication */ - authors$: Observable; + authors$: Observable; /** * The projects related to this publication @@ -51,10 +52,7 @@ export class PublicationComponent extends ItemComponent implements OnInit { if (this.resolvedRelsAndTypes$) { - this.authors$ = this.resolvedRelsAndTypes$.pipe( - filterRelationsByTypeLabel('isAuthorOfPublication'), - relationsToItems(this.item.id, this.ids) - ); + this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', this.ids); this.projects$ = this.resolvedRelsAndTypes$.pipe( filterRelationsByTypeLabel('isProjectOfPublication'), diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 30f39957db..8c6cf15ee9 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -16,8 +16,18 @@ import { PaginatedList } from '../../../../core/data/paginated-list'; import { RemoteData } from '../../../../core/data/remote-data'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { PageInfo } from '../../../../core/shared/page-info.model'; -import { compareArraysUsing, compareArraysUsingIds } from './item.component'; +import { compareArraysUsing, compareArraysUsingIds, ItemComponent } from './item.component'; import { of as observableOf } from 'rxjs'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ItemPageComponent } from '../../item-page.component'; +import { VarDirective } from '../../../../shared/utils/var.directive'; +import { ActivatedRoute } from '@angular/router'; +import { MetadataService } from '../../../../core/metadata/metadata.service'; +import { of } from 'rxjs/internal/observable/of'; +import { Observable } from 'rxjs/internal/Observable'; +import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; +import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; +import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; /** * Create a generic test for an item-page-fields component using a mockItem and the type of component @@ -306,4 +316,116 @@ describe('ItemComponent', () => { }); }); + describe('when calling buildRepresentations', () => { + let comp: ItemComponent; + let fixture: ComponentFixture; + + const metadataField = 'dc.contributor.author'; + const mockItem = Object.assign(new Item(), { + id: '1', + uuid: '1', + metadata: [ + { + key: metadataField, + value: 'Second value', + place: 1 + }, + { + key: metadataField, + value: 'Third value', + place: 2, + authority: 'virtual::123' + }, + { + key: metadataField, + value: 'First value', + place: 0 + }, + { + key: metadataField, + value: 'Fourth value', + place: 3, + authority: '123' + } + ], + relationships: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [ + Object.assign(new Relationship(), { + uuid: '123', + id: '123', + leftId: '1', + rightId: '2', + relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType())) + }) + ]))) + }); + const relatedItem = Object.assign(new Item(), { + id: '2', + metadata: [ + { + key: 'dc.title', + value: 'related item' + } + ] + }); + const mockItemDataService = { + findById: (id) => { + if (id === relatedItem.id) { + return observableOf(new RemoteData(false, false, true, null, relatedItem)) + } + } + } as ItemDataService; + + let representations: Observable; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), BrowserAnimationsModule], + declarations: [ItemComponent, VarDirective], + providers: [ + {provide: ITEM, useValue: mockItem} + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + representations = comp.buildRepresentations('bogus', metadataField, mockItemDataService); + })); + + it('should contain exactly 4 metadata-representations', () => { + representations.subscribe((reps: MetadataRepresentation[]) => { + expect(reps.length).toEqual(4); + }); + }); + + it('should have all the representations in the correct order', () => { + representations.subscribe((reps: MetadataRepresentation[]) => { + expect(reps[0].getPrimaryValue()).toEqual('First value'); + expect(reps[1].getPrimaryValue()).toEqual('Second value'); + expect(reps[2].getPrimaryValue()).toEqual('related item'); + expect(reps[3].getPrimaryValue()).toEqual('Fourth value'); + }); + }); + + it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => { + representations.subscribe((reps: MetadataRepresentation[]) => { + expect(reps[0] instanceof MetadatumRepresentation).toEqual(true); + expect(reps[1] instanceof MetadatumRepresentation).toEqual(true); + expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true); + expect(reps[3] instanceof MetadatumRepresentation).toEqual(true); + }); + }); + }) + }); diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index 5527114ec3..c5aae65f13 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -7,9 +7,14 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { Relationship } from '../../../../core/shared/item-relationships/relationship.model'; import { Item } from '../../../../core/shared/item.model'; -import { getRemoteDataPayload } from '../../../../core/shared/operators'; -import { hasValue } from '../../../../shared/empty.util'; +import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; +import { hasNoValue, hasValue } from '../../../../shared/empty.util'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; +import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; +import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; +import { Metadatum } from '../../../../core/shared/metadatum.model'; +import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; +import { of } from 'rxjs/internal/observable/of'; /** * Operator for comparing arrays using a mapping function @@ -82,6 +87,41 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) => distinctUntilChanged(compareArraysUsingIds()), ); +/** + * Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata + * @param thisId The id of the parent item + * @param itemType The type of relation this list resembles (for creating representations) + * @param metadata The list of original Metadatum objects + * @param ids The ItemDataService to use for fetching Items from the Rest API + */ +export const relationsToRepresentations = (thisId: string, itemType: string, metadata: Metadatum[], ids: ItemDataService) => + (source: Observable): Observable => + source.pipe( + flatMap((rels: Relationship[]) => + observableZip( + ...metadata.map((metadatum: Metadatum) => { + const prefix = 'virtual::'; + if (hasValue(metadatum.authority) && metadatum.authority.startsWith(prefix)) { + const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.authority.substring(metadatum.authority.indexOf(prefix) + prefix.length)); + if (matchingRels.length > 0) { + const matchingRel = matchingRels[0]; + let queryId = matchingRel.leftId; + if (matchingRel.leftId === thisId) { + queryId = matchingRel.rightId; + } + return ids.findById(queryId).pipe( + getSucceededRemoteData(), + map((d: RemoteData) => Object.assign(new ItemMetadataRepresentation(itemType), d.payload)) + ); + } + } else { + return of(Object.assign(new MetadatumRepresentation(itemType), metadatum)); + } + }) + ) + ) + ); + @Component({ selector: 'ds-item', template: '' @@ -93,7 +133,7 @@ export class ItemComponent implements OnInit { /** * Resolved relationships and types together in one observable */ - resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]> + resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>; constructor( @Inject(ITEM) public item: Item @@ -125,4 +165,25 @@ export class ItemComponent implements OnInit { } } + /** + * Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a + * certain type. + * @param itemType The type of item we're building representations of. Used for matching templates. + * @param metadataField The metadata field that resembles the item type. + * @param itemDataService ItemDataService to turn relations into items. + */ + buildRepresentations(itemType: string, metadataField: string, itemDataService: ItemDataService): Observable { + const metadata = this.item.findMetadataSortedByPlace(metadataField); + const relsCurrentPage$ = this.item.relationships.pipe( + getSucceededRemoteData(), + getRemoteDataPayload(), + map((pl: PaginatedList) => pl.page), + distinctUntilChanged(compareArraysUsingIds()) + ); + + return relsCurrentPage$.pipe( + relationsToRepresentations(this.item.id, itemType, metadata, itemDataService) + ); + } + } diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html new file mode 100644 index 0000000000..48eabf8451 --- /dev/null +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -0,0 +1,5 @@ + + + + diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts new file mode 100644 index 0000000000..77ba30778e --- /dev/null +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.spec.ts @@ -0,0 +1,40 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { MetadataRepresentationListComponent } from './metadata-representation-list.component'; +import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; +import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; + +const itemType = 'type'; +const metadataRepresentation1 = new MetadatumRepresentation(itemType); +const metadataRepresentation2 = new ItemMetadataRepresentation(itemType); +const representations = [metadataRepresentation1, metadataRepresentation2]; + +describe('MetadataRepresentationListComponent', () => { + let comp: MetadataRepresentationListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [MetadataRepresentationListComponent], + providers: [], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MetadataRepresentationListComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(MetadataRepresentationListComponent); + comp = fixture.componentInstance; + comp.representations = representations; + fixture.detectChanges(); + })); + + it(`should load ${representations.length} item-type-switcher components`, () => { + const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher')); + expect(fields.length).toBe(representations.length); + }); + +}); diff --git a/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts new file mode 100644 index 0000000000..b821557f4a --- /dev/null +++ b/src/app/+item-page/simple/metadata-representation-list/metadata-representation-list.component.ts @@ -0,0 +1,31 @@ +import { Component, Input } from '@angular/core'; +import * as viewMode from '../../../shared/view-mode'; +import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; + +export const VIEW_MODE_METADATA = 'metadata'; + +@Component({ + selector: 'ds-metadata-representation-list', + templateUrl: './metadata-representation-list.component.html' +}) +/** + * This component is used for displaying metadata + * It expects a list of MetadataRepresentation objects and a label to put on top of the list + */ +export class MetadataRepresentationListComponent { + /** + * A list of metadata-representations to display + */ + @Input() representations: MetadataRepresentation[]; + + /** + * An i18n label to use as a title for the list + */ + @Input() label: string; + + /** + * The view-mode we're currently on + * @type {ElementViewMode} + */ + viewMode = VIEW_MODE_METADATA; +} diff --git a/src/app/+item-page/simple/related-items/related-items-component.ts b/src/app/+item-page/simple/related-items/related-items-component.ts index 6ad2f0eeb3..ce8ca58b29 100644 --- a/src/app/+item-page/simple/related-items/related-items-component.ts +++ b/src/app/+item-page/simple/related-items/related-items-component.ts @@ -1,6 +1,7 @@ import { Component, Input } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; -import * as viewMode from '../../../shared/view-mode'; + +export const VIEW_MODE_ELEMENT = 'element'; @Component({ selector: 'ds-related-items', @@ -26,5 +27,5 @@ export class RelatedItemsComponent { * The view-mode we're currently on * @type {ElementViewMode} */ - ElementViewMode = viewMode.ElementViewMode + viewMode = VIEW_MODE_ELEMENT; } diff --git a/src/app/+item-page/simple/related-items/related-items.component.html b/src/app/+item-page/simple/related-items/related-items.component.html index bd8b7eb5f3..4b284ad63c 100644 --- a/src/app/+item-page/simple/related-items/related-items.component.html +++ b/src/app/+item-page/simple/related-items/related-items.component.html @@ -1,5 +1,5 @@ + [object]="item" [viewMode]="viewMode"> diff --git a/src/app/core/metadata/metadata.service.spec.ts b/src/app/core/metadata/metadata.service.spec.ts index 006707d710..189517e9f5 100644 --- a/src/app/core/metadata/metadata.service.spec.ts +++ b/src/app/core/metadata/metadata.service.spec.ts @@ -33,6 +33,7 @@ import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader'; import { BrowseService } from '../browse/browse.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { EmptyError } from 'rxjs/internal-compatibility'; +import { Metadatum } from '../shared/metadatum.model'; /* tslint:disable:max-classes-per-file */ @Component({ @@ -223,7 +224,7 @@ describe('MetadataService', () => { key: 'dc.publisher', language: 'en_US', value: 'Mock Publisher' - }); + } as Metadatum); return publishedMockItem; } diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index 3a40d142aa..916274eda6 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -1,5 +1,5 @@ import { Metadatum } from './metadatum.model' -import { isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { hasNoValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CacheableObject } from '../cache/object-cache.reducer'; import { RemoteData } from '../data/remote-data'; import { ResourceType } from './resource-type'; @@ -91,4 +91,23 @@ export class DSpaceObject implements CacheableObject, ListableObject { }); } + /** + * Find metadata on a specific field and order all of them using their "place" property. + * @param key + */ + findMetadataSortedByPlace(key: string): Metadatum[] { + return this.filterMetadata([key]).sort((a: Metadatum, b: Metadatum) => { + if (hasNoValue(a.place) && hasNoValue(b.place)) { + return 0; + } + if (hasNoValue(a.place)) { + return -1; + } + if (hasNoValue(b.place)) { + return 1; + } + return a.place - b.place; + }); + } + } diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts new file mode 100644 index 0000000000..4afe5a783f --- /dev/null +++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts @@ -0,0 +1,36 @@ +import { MetadataRepresentationType } from '../metadata-representation.model'; +import { ItemMetadataRepresentation, ItemTypeToPrimaryValue } from './item-metadata-representation.model'; +import { Item } from '../../item.model'; +import { Metadatum } from '../../metadatum.model'; + +describe('ItemMetadataRepresentation', () => { + const valuePrefix = 'Test value for '; + const item = new Item(); + let itemMetadataRepresentation: ItemMetadataRepresentation; + item.metadata = Object.keys(ItemTypeToPrimaryValue).map((key: string) => { + return Object.assign(new Metadatum(), { + key: ItemTypeToPrimaryValue[key], + value: `${valuePrefix}${ItemTypeToPrimaryValue[key]}` + }); + }); + + for (const itemType of Object.keys(ItemTypeToPrimaryValue)) { + describe(`when creating an ItemMetadataRepresentation with item-type "${itemType}"`, () => { + beforeEach(() => { + itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(itemType), item); + }); + + it('should have a representation type of item', () => { + expect(itemMetadataRepresentation.representationType).toEqual(MetadataRepresentationType.Item); + }); + + it('should return the correct value when calling getPrimaryValue', () => { + expect(itemMetadataRepresentation.getPrimaryValue()).toEqual(`${valuePrefix}${ItemTypeToPrimaryValue[itemType]}`); + }); + + it('should return the correct item type', () => { + expect(itemMetadataRepresentation.itemType).toEqual(itemType); + }); + }); + } +}); diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts new file mode 100644 index 0000000000..9ff72d5cf6 --- /dev/null +++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts @@ -0,0 +1,48 @@ +import { Item } from '../../item.model'; +import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model'; +import { hasValue } from '../../../../shared/empty.util'; + +/** + * An object to convert item types into the metadata field it should render for the item's primary value + */ +export const ItemTypeToPrimaryValue = { + Default: 'dc.title', + Person: 'dc.contributor.author' +}; + +/** + * This class defines the way the item it extends should be represented as metadata + */ +export class ItemMetadataRepresentation extends Item implements MetadataRepresentation { + + /** + * The type of item this item can be represented as + */ + itemType: string; + + constructor(itemType: string) { + super(); + this.itemType = itemType; + } + + /** + * Fetch the way this item should be rendered as in a list + */ + get representationType(): MetadataRepresentationType { + return MetadataRepresentationType.Item; + } + + /** + * Get the value to display, depending on the itemType + */ + getPrimaryValue(): string { + let metadata; + if (hasValue(ItemTypeToPrimaryValue[this.itemType])) { + metadata = ItemTypeToPrimaryValue[this.itemType]; + } else { + metadata = ItemTypeToPrimaryValue.Default; + } + return this.findMetadata(metadata); + } + +} diff --git a/src/app/core/shared/metadata-representation/metadata-representation.model.ts b/src/app/core/shared/metadata-representation/metadata-representation.model.ts new file mode 100644 index 0000000000..770c462e8d --- /dev/null +++ b/src/app/core/shared/metadata-representation/metadata-representation.model.ts @@ -0,0 +1,31 @@ +/** + * An Enum defining the representation type of metadata + */ +export enum MetadataRepresentationType { + None = 'none', + Item = 'item', + AuthorityControlled = 'authority_controlled', + PlainText = 'plain_text' +} + +/** + * An interface containing information about how we should represent certain metadata + */ +export interface MetadataRepresentation { + /** + * The type of item this metadata is representing + * e.g. 'Person' + * This can be used for template matching + */ + itemType: string; + + /** + * How we should render the metadata in a list + */ + representationType: MetadataRepresentationType, + + /** + * Fetches the primary value to be displayed + */ + getPrimaryValue(): string +} diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts new file mode 100644 index 0000000000..7356a79bbd --- /dev/null +++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts @@ -0,0 +1,54 @@ +import { Metadatum } from '../../metadatum.model'; +import { MetadatumRepresentation } from './metadatum-representation.model'; +import { MetadataRepresentationType } from '../metadata-representation.model'; + +describe('MetadatumRepresentation', () => { + const itemType = 'Person'; + const normalMetadatum = Object.assign(new Metadatum(), { + key: 'dc.contributor.author', + value: 'Test Author' + }); + const authorityMetadatum = Object.assign(new Metadatum(), { + key: 'dc.contributor.author', + value: 'Test Authority Author', + authority: '1234' + }); + + let metadatumRepresentation: MetadatumRepresentation; + + describe('when creating a MetadatumRepresentation based on a standard Metadatum object', () => { + beforeEach(() => { + metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), normalMetadatum); + }); + + it('should have a representation type of plain text', () => { + expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.PlainText); + }); + + it('should return the correct value when calling getPrimaryValue', () => { + expect(metadatumRepresentation.getPrimaryValue()).toEqual(normalMetadatum.value); + }); + + it('should return the correct item type', () => { + expect(metadatumRepresentation.itemType).toEqual(itemType); + }); + }); + + describe('when creating a MetadatumRepresentation based on an authority controlled Metadatum object', () => { + beforeEach(() => { + metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), authorityMetadatum); + }); + + it('should have a representation type of plain text', () => { + expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.AuthorityControlled); + }); + + it('should return the correct value when calling getPrimaryValue', () => { + expect(metadatumRepresentation.getPrimaryValue()).toEqual(authorityMetadatum.value); + }); + + it('should return the correct item type', () => { + expect(metadatumRepresentation.itemType).toEqual(itemType); + }); + }); +}); diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts new file mode 100644 index 0000000000..6a8de97733 --- /dev/null +++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts @@ -0,0 +1,38 @@ +import { Metadatum } from '../../metadatum.model'; +import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model'; +import { hasValue } from '../../../../shared/empty.util'; + +/** + * This class defines the way the metadatum it extends should be represented + */ +export class MetadatumRepresentation extends Metadatum implements MetadataRepresentation { + + /** + * The type of item this metadatum can be represented as + */ + itemType: string; + + constructor(itemType: string) { + super(); + this.itemType = itemType; + } + + /** + * Fetch the way this metadatum should be rendered as in a list + */ + get representationType(): MetadataRepresentationType { + if (hasValue(this.authority)) { + return MetadataRepresentationType.AuthorityControlled; + } else { + return MetadataRepresentationType.PlainText; + } + } + + /** + * Get the value to display + */ + getPrimaryValue(): string { + return this.value; + } + +} diff --git a/src/app/core/shared/metadatum.model.ts b/src/app/core/shared/metadatum.model.ts index a3c5830608..c4ce8c3101 100644 --- a/src/app/core/shared/metadatum.model.ts +++ b/src/app/core/shared/metadatum.model.ts @@ -20,4 +20,17 @@ export class Metadatum { @autoserialize value: string; + /** + * The place of this Metadatum within his list of metadata + * This is used to render metadata in a specific custom order + */ + @autoserialize + place: number; + + /** + * The authority key used for authority-controlled metadata + */ + @autoserialize + authority: string; + } diff --git a/src/app/shared/items/item-type-decorator.ts b/src/app/shared/items/item-type-decorator.ts index 509a1c754a..c8a1a2df97 100644 --- a/src/app/shared/items/item-type-decorator.ts +++ b/src/app/shared/items/item-type-decorator.ts @@ -1,36 +1,60 @@ import { hasNoValue, hasValue } from '../empty.util'; -import { ElementViewMode } from '../view-mode'; +import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model'; +import { VIEW_MODE_ELEMENT } from '../../+item-page/simple/related-items/related-items-component'; export const DEFAULT_ITEM_TYPE = 'Default'; +export const DEFAULT_VIEW_MODE = VIEW_MODE_ELEMENT; +export const NO_REPRESENTATION_TYPE = MetadataRepresentationType.None; +export const DEFAULT_REPRESENTATION_TYPE = MetadataRepresentationType.PlainText; const map = new Map(); /** - * Decorator used for rendering simple item pages by type and viewMode + * Decorator used for rendering simple item pages by type and viewMode (and optionally a representationType) * @param type * @param viewMode + * @param representationType */ -export function rendersItemType(type: string, viewMode: ElementViewMode) { +export function rendersItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) { return function decorator(component: any) { if (hasNoValue(map.get(viewMode))) { map.set(viewMode, new Map()); } - if (hasValue(map.get(viewMode).get(type))) { - throw new Error(`There can't be more than one component to render Items of type "${type}" in view mode "${viewMode}"`); + if (hasNoValue(map.get(viewMode).get(type))) { + map.get(viewMode).set(type, new Map()); } - map.get(viewMode).set(type, component); + if (hasNoValue(representationType)) { + representationType = NO_REPRESENTATION_TYPE; + } + if (hasValue(map.get(viewMode).get(type).get(representationType))) { + throw new Error(`There can't be more than one component to render Metadata of type "${type}" in view mode "${viewMode}" with representation type "${representationType}"`); + } + map.get(viewMode).get(type).set(representationType, component); }; } /** - * Get the component used for rendering an item by type and viewMode + * Get the component used for rendering an item by type and viewMode (and optionally a representationType) * @param type * @param viewMode + * @param representationType */ -export function getComponentByItemType(type: string, viewMode: ElementViewMode) { - let component = map.get(viewMode).get(type); - if (hasNoValue(component)) { - component = map.get(viewMode).get(DEFAULT_ITEM_TYPE); +export function getComponentByItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) { + if (hasNoValue(representationType)) { + representationType = NO_REPRESENTATION_TYPE; } - return component; + if (hasNoValue(map.get(viewMode))) { + viewMode = DEFAULT_VIEW_MODE; + } + if (hasNoValue(map.get(viewMode).get(type))) { + type = DEFAULT_ITEM_TYPE; + } + let representationComponent = map.get(viewMode).get(type).get(representationType); + if (hasNoValue(representationComponent)) { + representationComponent = map.get(viewMode).get(type).get(DEFAULT_REPRESENTATION_TYPE); + } + if (hasNoValue(representationComponent)) { + representationComponent = map.get(viewMode).get(type).get(NO_REPRESENTATION_TYPE); + } + return representationComponent; } diff --git a/src/app/shared/items/switcher/item-type-switcher.component.spec.ts b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts index 67f5309793..cb657b1625 100644 --- a/src/app/shared/items/switcher/item-type-switcher.component.spec.ts +++ b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts @@ -8,8 +8,10 @@ import { PaginatedList } from '../../../core/data/paginated-list'; import { RemoteData } from '../../../core/data/remote-data'; import * as decorator from '../item-type-decorator'; import { getComponentByItemType } from '../item-type-decorator'; -import { ElementViewMode } from '../../view-mode'; +import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import createSpy = jasmine.createSpy; +import { VIEW_MODE_FULL } from '../../../+item-page/simple/item-page.component'; +import { VIEW_MODE_METADATA } from '../../../+item-page/simple/metadata-representation-list/metadata-representation-list.component'; const relationType = 'type'; const mockItem: Item = Object.assign(new Item(), { @@ -26,7 +28,8 @@ const mockItem: Item = Object.assign(new Item(), { value: relationType }] }); -const viewMode = ElementViewMode.Full; +const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(relationType), mockItem); +let viewMode = VIEW_MODE_FULL; describe('ItemTypeSwitcherComponent', () => { let comp: ItemTypeSwitcherComponent; @@ -47,13 +50,39 @@ describe('ItemTypeSwitcherComponent', () => { spyOnProperty(decorator, 'getComponentByItemType').and.returnValue(createSpy('getComponentByItemType')) })); - describe('when calling getComponent', () => { + describe('when the injected object is of type Item', () => { beforeEach(() => { - comp.getComponent(); + viewMode = VIEW_MODE_FULL; + comp.object = mockItem; + comp.viewMode = viewMode; }); - it('should call getComponentByItemType with parameters type and viewMode', () => { - expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode); + describe('when calling getComponent', () => { + beforeEach(() => { + comp.getComponent(); + }); + + it('should call getComponentByItemType with parameters type and viewMode', () => { + expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode); + }); + }); + }); + + describe('when the injected object is of type MetadataRepresentation', () => { + beforeEach(() => { + viewMode = VIEW_MODE_METADATA; + comp.object = mockItemMetadataRepresentation; + comp.viewMode = viewMode; + }); + + describe('when calling getComponent', () => { + beforeEach(() => { + comp.getComponent(); + }); + + it('should call getComponentByItemType with parameters type, viewMode and representationType', () => { + expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode, mockItemMetadataRepresentation.representationType); + }); }); }); diff --git a/src/app/shared/items/switcher/item-type-switcher.component.ts b/src/app/shared/items/switcher/item-type-switcher.component.ts index 5599326e98..2e03cb556c 100644 --- a/src/app/shared/items/switcher/item-type-switcher.component.ts +++ b/src/app/shared/items/switcher/item-type-switcher.component.ts @@ -4,7 +4,7 @@ import { Item } from '../../../core/shared/item.model'; import { hasValue } from '../../empty.util'; import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model'; import { getComponentByItemType } from '../item-type-decorator'; -import { ElementViewMode } from '../../view-mode'; +import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; export const ITEM: InjectionToken = new InjectionToken('item'); @@ -18,14 +18,14 @@ export const ITEM: InjectionToken = new InjectionToken('item'); */ export class ItemTypeSwitcherComponent implements OnInit { /** - * The item to determine the component for + * The item or metadata to determine the component for */ - @Input() object: Item | SearchResult; + @Input() object: Item | SearchResult | MetadataRepresentation; /** * The preferred view-mode to display */ - @Input() viewMode: ElementViewMode; + @Input() viewMode: string; /** * The object injector used to inject the item into the child component @@ -48,6 +48,11 @@ export class ItemTypeSwitcherComponent implements OnInit { * @returns {string} */ getComponent(): string { + if (hasValue((this.object as any).representationType)) { + const metadataRepresentation = this.object as MetadataRepresentation; + return getComponentByItemType(metadataRepresentation.itemType, this.viewMode, metadataRepresentation.representationType); + } + let item: Item; if (hasValue((this.object as any).dspaceObject)) { const searchResult = this.object as ItemSearchResult; diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.html b/src/app/shared/object-list/item-list-element/item-list-element.component.html index 8cdf8cac6a..d433c7acf2 100644 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.html @@ -1 +1 @@ - + diff --git a/src/app/shared/object-list/item-list-element/item-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-list-element.component.ts index a9677c9777..4d34e75d61 100644 --- a/src/app/shared/object-list/item-list-element/item-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-list-element.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; import { Item } from '../../../core/shared/item.model'; -import * as viewMode from '../../../shared/view-mode'; import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator'; import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component'; import { SetViewMode } from '../../view-mode'; +import { VIEW_MODE_ELEMENT } from '../../../+item-page/simple/related-items/related-items-component'; @Component({ selector: 'ds-item-list-element', @@ -18,5 +18,5 @@ import { SetViewMode } from '../../view-mode'; */ @renderElementsFor(Item, SetViewMode.List) export class ItemListElementComponent extends AbstractListableElementComponent { - ElementViewMode = viewMode.ElementViewMode; + viewMode = VIEW_MODE_ELEMENT; } diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts index 4f18822773..3b1b9eb1c9 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('JournalIssue', ElementViewMode.SetElement) +@rendersItemType('JournalIssue', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-journal-issue-list-element', styleUrls: ['./journal-issue-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts index a95dca88f9..49a11b2cd9 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('JournalVolume', ElementViewMode.SetElement) +@rendersItemType('JournalVolume', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-journal-volume-list-element', styleUrls: ['./journal-volume-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts index 28be0d8149..45926c681c 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('Journal', ElementViewMode.SetElement) +@rendersItemType('Journal', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-journal-list-element', styleUrls: ['./journal-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts index e3433f7e76..699cb6dfab 100644 --- a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('OrgUnit', ElementViewMode.SetElement) +@rendersItemType('OrgUnit', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-orgunit-list-element', styleUrls: ['./orgunit-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html index 803674b56e..2d89fda483 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html @@ -13,4 +13,3 @@ - diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts index b0369458f9..e12cc60813 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('Person', ElementViewMode.SetElement) +@rendersItemType('Person', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-person-list-element', styleUrls: ['./person-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html new file mode 100644 index 0000000000..92e57d7ef4 --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts new file mode 100644 index 0000000000..1f3d736d1c --- /dev/null +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.ts @@ -0,0 +1,16 @@ +import { rendersItemType } from '../../../../items/item-type-decorator'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; +import { Component } from '@angular/core'; +import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { MetadataRepresentationType } from '../../../../../core/shared/metadata-representation/metadata-representation.model'; + +@rendersItemType('Person', VIEW_MODE_ELEMENT, MetadataRepresentationType.Item) +@Component({ + selector: 'ds-person-metadata-list-element', + templateUrl: './person-metadata-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type Person + */ +export class PersonMetadataListElementComponent extends TypedItemSearchResultListElementComponent { +} diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts index 6b130e4786..e688056bc6 100644 --- a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.ts @@ -1,9 +1,9 @@ import { Component } from '@angular/core'; import { rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('Project', ElementViewMode.SetElement) +@rendersItemType('Project', VIEW_MODE_ELEMENT) @Component({ selector: 'ds-project-list-element', styleUrls: ['./project-list-element.component.scss'], diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts index 4575ab5cca..58a0568931 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.ts @@ -1,10 +1,10 @@ import { Component } from '@angular/core'; import { DEFAULT_ITEM_TYPE, rendersItemType } from '../../../../items/item-type-decorator'; -import { ElementViewMode } from '../../../../view-mode'; import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component'; -@rendersItemType('Publication', ElementViewMode.SetElement) -@rendersItemType(DEFAULT_ITEM_TYPE, ElementViewMode.SetElement) +@rendersItemType('Publication', VIEW_MODE_ELEMENT) +@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_ELEMENT) @Component({ selector: 'ds-publication-list-element', styleUrls: ['./publication-list-element.component.scss'], diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html new file mode 100644 index 0000000000..764fdc1064 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts new file mode 100644 index 0000000000..90de549800 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.spec.ts @@ -0,0 +1,38 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ITEM } from '../../../items/switcher/item-type-switcher.component'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ItemMetadataListElementComponent } from './item-metadata-list-element.component'; +import { By } from '@angular/platform-browser'; +import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; + +const mockItemMetadataRepresentation = new ItemMetadataRepresentation('type'); + +describe('ItemMetadataListElementComponent', () => { + let comp: ItemMetadataListElementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [ItemMetadataListElementComponent], + providers: [ + { provide: ITEM, useValue: mockItemMetadataRepresentation } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemMetadataListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemMetadataListElementComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should call an item-type-switcher component and pass the item-metadata-representation', () => { + const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).nativeElement; + expect(itemTypeSwitcher.object).toBe(mockItemMetadataRepresentation); + }); + +}); diff --git a/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts new file mode 100644 index 0000000000..a5900cc763 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/item/item-metadata-list-element.component.ts @@ -0,0 +1,25 @@ +import * as viewMode from '../../../view-mode'; +import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model'; +import { Component } from '@angular/core'; +import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component'; +import { DEFAULT_ITEM_TYPE, rendersItemType } from '../../../items/item-type-decorator'; +import { VIEW_MODE_METADATA } from '../../../../+item-page/simple/metadata-representation-list/metadata-representation-list.component'; +import { VIEW_MODE_ELEMENT } from '../../../../+item-page/simple/related-items/related-items-component'; + +@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_METADATA, MetadataRepresentationType.Item) +@Component({ + selector: 'ds-item-metadata-list-element', + templateUrl: './item-metadata-list-element.component.html' +}) +/** + * A component for displaying MetadataRepresentation objects in the form of items + * It will send the MetadataRepresentation object along with ElementViewMode.SetElement to the ItemTypeSwitcherComponent, + * which will in his turn decide how to render the item as metadata. + */ +export class ItemMetadataListElementComponent extends MetadataRepresentationListElementComponent { + /** + * The view-mode we're currently on + * @type {ElementViewMode} + */ + viewMode = VIEW_MODE_ELEMENT; +} diff --git a/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts new file mode 100644 index 0000000000..2488db50b1 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/metadata-representation-list-element.component.ts @@ -0,0 +1,15 @@ +import { Component, Inject } from '@angular/core'; +import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model'; +import { ITEM } from '../../items/switcher/item-type-switcher.component'; + +@Component({ + selector: 'ds-metadata-representation-list-element', + template: '' +}) +/** + * An abstract class for displaying a single MetadataRepresentation + */ +export class MetadataRepresentationListElementComponent { + constructor(@Inject(ITEM) public metadataRepresentation: MetadataRepresentation) { + } +} diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html new file mode 100644 index 0000000000..2533cf834b --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html @@ -0,0 +1,3 @@ +
+ {{metadataRepresentation.getPrimaryValue()}} +
diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts new file mode 100644 index 0000000000..42b9abde16 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.spec.ts @@ -0,0 +1,39 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { PlainTextMetadataListElementComponent } from './plain-text-metadata-list-element.component'; +import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; +import { ITEM } from '../../../items/switcher/item-type-switcher.component'; + +const mockMetadataRepresentation = Object.assign(new MetadatumRepresentation('type'), { + key: 'dc.contributor.author', + value: 'Test Author' +}); + +describe('PlainTextMetadataListElementComponent', () => { + let comp: PlainTextMetadataListElementComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [], + declarations: [PlainTextMetadataListElementComponent], + providers: [ + { provide: ITEM, useValue: mockMetadataRepresentation } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PlainTextMetadataListElementComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PlainTextMetadataListElementComponent); + comp = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should contain the value as plain text', () => { + expect(fixture.debugElement.nativeElement.textContent).toContain(mockMetadataRepresentation.value); + }); + +}); diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts new file mode 100644 index 0000000000..e7cf235c75 --- /dev/null +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts @@ -0,0 +1,19 @@ +import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model'; +import { Component } from '@angular/core'; +import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component'; +import { DEFAULT_ITEM_TYPE, rendersItemType } from '../../../items/item-type-decorator'; +import { VIEW_MODE_METADATA } from '../../../../+item-page/simple/metadata-representation-list/metadata-representation-list.component'; + +@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_METADATA, MetadataRepresentationType.PlainText) +// For now, authority controlled fields are rendered the same way as plain text fields +@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_METADATA, MetadataRepresentationType.AuthorityControlled) +@Component({ + selector: 'ds-plain-text-metadata-list-element', + templateUrl: './plain-text-metadata-list-element.component.html' +}) +/** + * A component for displaying MetadataRepresentation objects in the form of plain text + * It will simply use the value retrieved from MetadataRepresentation.getPrimaryValue() to display as plain text + */ +export class PlainTextMetadataListElementComponent extends MetadataRepresentationListElementComponent { +} diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html index 8cdf8cac6a..d433c7acf2 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.html @@ -1 +1 @@ - + diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts index b9ac52ca40..a164e5a65b 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.ts @@ -4,9 +4,9 @@ import { focusBackground } from '../../../animations/focus'; import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; -import * as viewMode from '../../../../shared/view-mode'; import { SetViewMode } from '../../../view-mode'; import { SearchResultListElementComponent } from '../search-result-list-element.component'; +import { VIEW_MODE_ELEMENT } from '../../../../+item-page/simple/related-items/related-items-component'; @Component({ selector: 'ds-item-search-result-list-element', @@ -18,5 +18,5 @@ import { SearchResultListElementComponent } from '../search-result-list-element. @renderElementsFor(ItemSearchResult, SetViewMode.List) export class ItemSearchResultListElementComponent extends SearchResultListElementComponent { - ElementViewMode = viewMode.ElementViewMode; + viewMode = VIEW_MODE_ELEMENT; } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index e467839b57..1c40e86680 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -94,6 +94,11 @@ import { CapitalizePipe } from './utils/capitalize.pipe'; import { ObjectKeysPipe } from './utils/object-keys-pipe'; import { MomentModule } from 'ngx-moment'; import { NouisliderModule } from 'ng2-nouislider'; +import { PlainTextMetadataListElementComponent } from './object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component'; +import { ItemMetadataListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-list-element.component'; +import { TooltipModule } from 'ngx-bootstrap'; +import { PersonMetadataListElementComponent } from './object-list/item-list-element/item-types/person/person-metadata-list-element.component'; +import { MetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/metadata-representation-list-element.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -117,6 +122,10 @@ const MODULES = [ TextMaskModule ]; +const ROOT_MODULES = [ + TooltipModule.forRoot() +]; + const PIPES = [ // put shared pipes here EnumKeysPipe, @@ -184,12 +193,16 @@ const ENTRY_COMPONENTS = [ SearchResultGridElementComponent, PublicationListElementComponent, PersonListElementComponent, + PersonMetadataListElementComponent, OrgUnitListElementComponent, ProjectListElementComponent, JournalListElementComponent, JournalVolumeListElementComponent, JournalIssueListElementComponent, - BrowseEntryListElementComponent + BrowseEntryListElementComponent, + PlainTextMetadataListElementComponent, + ItemMetadataListElementComponent, + MetadataRepresentationListElementComponent ]; const PROVIDERS = [ @@ -206,7 +219,8 @@ const DIRECTIVES = [ @NgModule({ imports: [ - ...MODULES + ...MODULES, + ...ROOT_MODULES ], declarations: [ ...PIPES, diff --git a/src/app/shared/view-mode.ts b/src/app/shared/view-mode.ts index 2825f20c50..d36764a6e8 100644 --- a/src/app/shared/view-mode.ts +++ b/src/app/shared/view-mode.ts @@ -8,17 +8,7 @@ export enum SetViewMode { Grid = 'grid' } -/** - * Enum used for defining the view-mode of a single element - * Full Display the full element - * SetElement Display the element as part of a set - */ -export enum ElementViewMode { - Full, - SetElement -} - /** * ViewMode refers to either a SetViewMode or ElementViewMode */ -export type ViewMode = SetViewMode | ElementViewMode; +export type ViewMode = SetViewMode; From 9982254ca26de0d6ab3f6dfe82e5051eaba6b222 Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 22 Jan 2019 16:13:49 +0100 Subject: [PATCH 083/205] roll back a merge mistake --- resources/i18n/en.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 1b2e6a5ca2..e296326b72 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -334,7 +334,7 @@ "f.dateIssued.max": "End date", "f.subject": "Subject", "f.has_content_in_original_bundle": "Has files", - "f.entityType": "Entity Type" + "f.entityType": "Item Type" }, "filter": { "show-more": "Show more", @@ -364,8 +364,8 @@ "head": "Has files" }, "entityType": { - "placeholder": "Entity Type", - "head": "Entity Type" + "placeholder": "Item Type", + "head": "Item Type" } } } From 11b09a22697b119d38e34f15f3d8515abbc5c11f Mon Sep 17 00:00:00 2001 From: Art Lowel Date: Tue, 29 Jan 2019 18:23:01 +0100 Subject: [PATCH 084/205] rename getPrimaryValue to getValue, improve doccomments and move virtual metadata code to the Metadatum model --- .../item-types/shared/item.component.spec.ts | 8 +-- .../item-types/shared/item.component.ts | 9 +-- ...item-metadata-representation.model.spec.ts | 14 ++-- .../item-metadata-representation.model.ts | 14 ++-- .../metadata-representation.model.ts | 4 +- .../metadatum-representation.model.spec.ts | 6 +- .../metadatum-representation.model.ts | 2 +- src/app/core/shared/metadatum.model.spec.ts | 67 +++++++++++++++++++ src/app/core/shared/metadatum.model.ts | 28 ++++++++ ...-text-metadata-list-element.component.html | 2 +- ...in-text-metadata-list-element.component.ts | 2 +- 11 files changed, 126 insertions(+), 30 deletions(-) create mode 100644 src/app/core/shared/metadatum.model.spec.ts diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 8c6cf15ee9..28bd8e33e3 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -411,10 +411,10 @@ describe('ItemComponent', () => { it('should have all the representations in the correct order', () => { representations.subscribe((reps: MetadataRepresentation[]) => { - expect(reps[0].getPrimaryValue()).toEqual('First value'); - expect(reps[1].getPrimaryValue()).toEqual('Second value'); - expect(reps[2].getPrimaryValue()).toEqual('related item'); - expect(reps[3].getPrimaryValue()).toEqual('Fourth value'); + expect(reps[0].getValue()).toEqual('First value'); + expect(reps[1].getValue()).toEqual('Second value'); + expect(reps[2].getValue()).toEqual('related item'); + expect(reps[3].getValue()).toEqual('Fourth value'); }); }); diff --git a/src/app/+item-page/simple/item-types/shared/item.component.ts b/src/app/+item-page/simple/item-types/shared/item.component.ts index c5aae65f13..3cea15c251 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.ts @@ -99,10 +99,11 @@ export const relationsToRepresentations = (thisId: string, itemType: string, met source.pipe( flatMap((rels: Relationship[]) => observableZip( - ...metadata.map((metadatum: Metadatum) => { - const prefix = 'virtual::'; - if (hasValue(metadatum.authority) && metadatum.authority.startsWith(prefix)) { - const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.authority.substring(metadatum.authority.indexOf(prefix) + prefix.length)); + ...metadata + .map((metadatum: any) => Object.assign(new Metadatum(), metadatum)) + .map((metadatum: Metadatum) => { + if (metadatum.isVirtual) { + const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue); if (matchingRels.length > 0) { const matchingRel = matchingRels[0]; let queryId = matchingRel.leftId; diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts index 4afe5a783f..cd153153de 100644 --- a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts +++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts @@ -1,5 +1,5 @@ import { MetadataRepresentationType } from '../metadata-representation.model'; -import { ItemMetadataRepresentation, ItemTypeToPrimaryValue } from './item-metadata-representation.model'; +import { ItemMetadataRepresentation, ItemTypeToValue } from './item-metadata-representation.model'; import { Item } from '../../item.model'; import { Metadatum } from '../../metadatum.model'; @@ -7,14 +7,14 @@ describe('ItemMetadataRepresentation', () => { const valuePrefix = 'Test value for '; const item = new Item(); let itemMetadataRepresentation: ItemMetadataRepresentation; - item.metadata = Object.keys(ItemTypeToPrimaryValue).map((key: string) => { + item.metadata = Object.keys(ItemTypeToValue).map((key: string) => { return Object.assign(new Metadatum(), { - key: ItemTypeToPrimaryValue[key], - value: `${valuePrefix}${ItemTypeToPrimaryValue[key]}` + key: ItemTypeToValue[key], + value: `${valuePrefix}${ItemTypeToValue[key]}` }); }); - for (const itemType of Object.keys(ItemTypeToPrimaryValue)) { + for (const itemType of Object.keys(ItemTypeToValue)) { describe(`when creating an ItemMetadataRepresentation with item-type "${itemType}"`, () => { beforeEach(() => { itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(itemType), item); @@ -24,8 +24,8 @@ describe('ItemMetadataRepresentation', () => { expect(itemMetadataRepresentation.representationType).toEqual(MetadataRepresentationType.Item); }); - it('should return the correct value when calling getPrimaryValue', () => { - expect(itemMetadataRepresentation.getPrimaryValue()).toEqual(`${valuePrefix}${ItemTypeToPrimaryValue[itemType]}`); + it('should return the correct value when calling getValue', () => { + expect(itemMetadataRepresentation.getValue()).toEqual(`${valuePrefix}${ItemTypeToValue[itemType]}`); }); it('should return the correct item type', () => { diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts index 9ff72d5cf6..2d4f3fdf75 100644 --- a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts +++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.ts @@ -3,15 +3,15 @@ import { MetadataRepresentation, MetadataRepresentationType } from '../metadata- import { hasValue } from '../../../../shared/empty.util'; /** - * An object to convert item types into the metadata field it should render for the item's primary value + * An object to convert item types into the metadata field it should render for the item's value */ -export const ItemTypeToPrimaryValue = { +export const ItemTypeToValue = { Default: 'dc.title', Person: 'dc.contributor.author' }; /** - * This class defines the way the item it extends should be represented as metadata + * This class determines which fields to use when rendering an Item as a metadata value. */ export class ItemMetadataRepresentation extends Item implements MetadataRepresentation { @@ -35,12 +35,12 @@ export class ItemMetadataRepresentation extends Item implements MetadataRepresen /** * Get the value to display, depending on the itemType */ - getPrimaryValue(): string { + getValue(): string { let metadata; - if (hasValue(ItemTypeToPrimaryValue[this.itemType])) { - metadata = ItemTypeToPrimaryValue[this.itemType]; + if (hasValue(ItemTypeToValue[this.itemType])) { + metadata = ItemTypeToValue[this.itemType]; } else { - metadata = ItemTypeToPrimaryValue.Default; + metadata = ItemTypeToValue.Default; } return this.findMetadata(metadata); } diff --git a/src/app/core/shared/metadata-representation/metadata-representation.model.ts b/src/app/core/shared/metadata-representation/metadata-representation.model.ts index 770c462e8d..58e5bf906f 100644 --- a/src/app/core/shared/metadata-representation/metadata-representation.model.ts +++ b/src/app/core/shared/metadata-representation/metadata-representation.model.ts @@ -25,7 +25,7 @@ export interface MetadataRepresentation { representationType: MetadataRepresentationType, /** - * Fetches the primary value to be displayed + * Fetches the value to be displayed */ - getPrimaryValue(): string + getValue(): string } diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts index 7356a79bbd..c55ff7f9f3 100644 --- a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts +++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts @@ -26,7 +26,7 @@ describe('MetadatumRepresentation', () => { }); it('should return the correct value when calling getPrimaryValue', () => { - expect(metadatumRepresentation.getPrimaryValue()).toEqual(normalMetadatum.value); + expect(metadatumRepresentation.getValue()).toEqual(normalMetadatum.value); }); it('should return the correct item type', () => { @@ -43,8 +43,8 @@ describe('MetadatumRepresentation', () => { expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.AuthorityControlled); }); - it('should return the correct value when calling getPrimaryValue', () => { - expect(metadatumRepresentation.getPrimaryValue()).toEqual(authorityMetadatum.value); + it('should return the correct value when calling getValue', () => { + expect(metadatumRepresentation.getValue()).toEqual(authorityMetadatum.value); }); it('should return the correct item type', () => { diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts index 6a8de97733..08ae041706 100644 --- a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts +++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.ts @@ -31,7 +31,7 @@ export class MetadatumRepresentation extends Metadatum implements MetadataRepres /** * Get the value to display */ - getPrimaryValue(): string { + getValue(): string { return this.value; } diff --git a/src/app/core/shared/metadatum.model.spec.ts b/src/app/core/shared/metadatum.model.spec.ts new file mode 100644 index 0000000000..0b552eed56 --- /dev/null +++ b/src/app/core/shared/metadatum.model.spec.ts @@ -0,0 +1,67 @@ +import { Metadatum } from './metadatum.model'; + +describe('Metadatum', () => { + let metadatum: Metadatum ; + + beforeEach(() => { + metadatum = new Metadatum(); + }); + + describe('isVirtual', () => { + describe('when the metadatum has no authority key', () => { + beforeEach(() => { + metadatum.authority = undefined; + }); + + it('should return false', () => { + expect(metadatum.isVirtual).toBe(false); + }); + }); + + describe('when the metadatum has an authority key', () => { + describe('but it doesn\'t start with the virtual prefix', () => { + beforeEach(() => { + metadatum.authority = 'value'; + }); + + it('should return false', () => { + expect(metadatum.isVirtual).toBe(false); + }); + }); + + describe('and it starts with the virtual prefix', () => { + beforeEach(() => { + metadatum.authority = 'virtual::value'; + }); + + it('should return true', () => { + expect(metadatum.isVirtual).toBe(true); + }); + }); + + }); + + }); + + describe('virtualValue', () => { + describe('when the metadatum isn\'t virtual', () => { + beforeEach(() => { + metadatum.authority = 'value'; + }); + + it('should return undefined', () => { + expect(metadatum.virtualValue).toBeUndefined(); + }); + }); + + describe('when the metadatum is virtual', () => { + beforeEach(() => { + metadatum.authority = 'virtual::value'; + }); + + it('should return everything in the authority key after virtual::', () => { + expect(metadatum.virtualValue).toBe('value'); + }); + }); + }); +}); diff --git a/src/app/core/shared/metadatum.model.ts b/src/app/core/shared/metadatum.model.ts index c4ce8c3101..f8cac4fd51 100644 --- a/src/app/core/shared/metadatum.model.ts +++ b/src/app/core/shared/metadatum.model.ts @@ -1,4 +1,7 @@ import { autoserialize } from 'cerialize'; +import { hasValue } from '../../shared/empty.util'; + +const VIRTUAL_METADATA_PREFIX = 'virtual::'; export class Metadatum { @@ -33,4 +36,29 @@ export class Metadatum { @autoserialize authority: string; + /** + * The authority confidence value + */ + @autoserialize + confidence: number; + + /** + * Returns true if this Metadatum's authority key starts with 'virtual::' + */ + get isVirtual(): boolean { + return hasValue(this.authority) && this.authority.startsWith(VIRTUAL_METADATA_PREFIX); + } + + /** + * If this is a virtual Metadatum, it returns everything in the authority key after 'virtual::'. + * Returns undefined otherwise. + */ + get virtualValue(): string { + if (this.isVirtual) { + return this.authority.substring(this.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length); + } else { + return undefined; + } + } + } diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html index 2533cf834b..3e017e1ae8 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.html @@ -1,3 +1,3 @@
- {{metadataRepresentation.getPrimaryValue()}} + {{metadataRepresentation.getValue()}}
diff --git a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts index e7cf235c75..e86210a961 100644 --- a/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts +++ b/src/app/shared/object-list/metadata-representation-list-element/plain-text/plain-text-metadata-list-element.component.ts @@ -13,7 +13,7 @@ import { VIEW_MODE_METADATA } from '../../../../+item-page/simple/metadata-repre }) /** * A component for displaying MetadataRepresentation objects in the form of plain text - * It will simply use the value retrieved from MetadataRepresentation.getPrimaryValue() to display as plain text + * It will simply use the value retrieved from MetadataRepresentation.getValue() to display as plain text */ export class PlainTextMetadataListElementComponent extends MetadataRepresentationListElementComponent { } From e7f5372f8dd5a0f530c219becd35d4307fb3d1c7 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Feb 2019 11:42:31 +0100 Subject: [PATCH 085/205] Fixed an issue with license in collection page --- src/app/+collection-page/collection-page.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/+collection-page/collection-page.component.html b/src/app/+collection-page/collection-page.component.html index 6e411cb29d..6265b223d8 100644 --- a/src/app/+collection-page/collection-page.component.html +++ b/src/app/+collection-page/collection-page.component.html @@ -32,7 +32,7 @@
From f35b1ca62078abdfb63b962007b7a9716bb49292 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Feb 2019 11:41:51 +0100 Subject: [PATCH 086/205] restored name property to NormalizedEPerson class --- src/app/core/eperson/models/normalized-eperson.model.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/core/eperson/models/normalized-eperson.model.ts b/src/app/core/eperson/models/normalized-eperson.model.ts index eb4fa4abe0..4f2a5edcd4 100644 --- a/src/app/core/eperson/models/normalized-eperson.model.ts +++ b/src/app/core/eperson/models/normalized-eperson.model.ts @@ -14,6 +14,9 @@ export class NormalizedEPerson extends NormalizedDSpaceObject implements Cacheab @autoserialize public handle: string; + @autoserialize + public name: string; + @autoserializeAs(NormalizedGroup) groups: Group[]; From 22ebbd5ca388073afb350342e06015fbd9338407 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Feb 2019 15:51:45 +0100 Subject: [PATCH 087/205] Fixed an issue with save of repeatable scrollable-dropdown fields --- ...dynamic-scrollable-dropdown.component.html | 3 +- .../dynamic-scrollable-dropdown.component.ts | 8 ++++++ .../sections/form/section-form.component.ts | 28 ++++++++++--------- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html index 1b4e9d2cd3..cfe50def98 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component.html @@ -15,7 +15,8 @@ class="ds-form-input-btn btn btn-outline-primary" id="scrollableDropdownMenuButton_{{model.id}}" ngbDropdownToggle - [disabled]="model.readOnly"> + [disabled]="model.readOnly" + (click)="onToggle(sdRef); $event.stopPropagation();"> \ No newline at end of file +
diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index dcc01f2b46..385f83eae2 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -1,9 +1,10 @@ - -import { take } from 'rxjs/operators'; import { Component, Input, OnInit } from '@angular/core'; + +import { Observable } from 'rxjs'; +import { take } from 'rxjs/operators'; + import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { SearchFilterService } from './search-filter.service'; -import { Observable } from 'rxjs'; import { slide } from '../../../shared/animations/slide'; import { isNotEmpty } from '../../../shared/empty.util'; diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts index 6cb04c6c1f..af0676ffe2 100644 --- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.ts @@ -22,6 +22,7 @@ import * as moment from 'moment'; import { RouteService } from '../../../../shared/services/route.service'; import { hasValue } from '../../../../shared/empty.util'; import { SearchConfigurationService } from '../../../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component'; /** * This component renders a simple item page. @@ -67,13 +68,13 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple constructor(protected searchService: SearchService, protected filterService: SearchFilterService, - protected searchConfigService: SearchConfigurationService, protected router: Router, protected rdbs: RemoteDataBuildService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(PLATFORM_ID) private platformId: any, private route: RouteService) { - super(searchService, filterService, searchConfigService, rdbs, router, filterConfig); + super(searchService, filterService, rdbs, router, searchConfigService, filterConfig); } diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 0522c1fba0..310d6502c7 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

{{"search.filters.head" | translate}}

-
-
- +
+
+
-{{"search.filters.reset" | translate}} \ No newline at end of file +{{"search.filters.reset" | translate}} diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index f16faff1f3..d6116843be 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,14 +1,17 @@ -import { Observable, of as observableOf } from 'rxjs'; +import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit } from '@angular/core'; + +import { BehaviorSubject, Observable, of as observableOf, Subscription } from 'rxjs'; +import { filter, first, map, mergeMap, startWith, switchMap, tap } from 'rxjs/operators'; -import { filter, map, mergeMap, startWith, switchMap } from 'rxjs/operators'; -import { Component } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; import { SearchFilterConfig } from '../search-service/search-filter-config.model'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; -import { isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { SearchFilterService } from './search-filter/search-filter.service'; import { getSucceededRemoteData } from '../../core/shared/operators'; +import { PaginatedSearchOptions } from '../paginated-search-options.model'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-filters', @@ -19,11 +22,12 @@ import { getSucceededRemoteData } from '../../core/shared/operators'; /** * This component represents the part of the search sidebar that contains filters. */ -export class SearchFiltersComponent { +export class SearchFiltersComponent implements OnDestroy, OnInit { + /** - * An observable containing configuration about which filters are shown and how they are shown + * An Array containing configuration about which filters are shown and how they are shown */ - filters: Observable>; + filters: SearchFilterConfig[] = []; /** * List of all filters that are currently active with their value set to null. @@ -31,15 +35,44 @@ export class SearchFiltersComponent { */ clearParams; + /** + * A boolean representing load state of filters configuration + */ + isLoadingFilters$: BehaviorSubject = new BehaviorSubject(true); + + /** + * The current paginated search options + */ + searchOptions$: Observable; + + private sub: Subscription; + /** * Initialize instance variables + * @param {ChangeDetectorRef} cdr * @param {SearchService} searchService * @param {SearchConfigurationService} searchConfigService * @param {SearchFilterService} filterService */ - constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) { - this.filters = searchService.getConfig().pipe(getSucceededRemoteData()); - this.clearParams = searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { + constructor( + private cdr: ChangeDetectorRef, + private searchService: SearchService, + @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService, + private filterService: SearchFilterService) { + } + + ngOnInit(): void { + this.searchOptions$ = this.searchConfigService.searchOptions; + + this.sub = this.searchOptions$.pipe( + tap(() => this.setLoading()), + switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData()))) + .subscribe((filtersRD: RemoteData) => { + this.filters = filtersRD.payload; + this.isLoadingFilters$.next(false); + }); + + this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { Object.keys(filters).forEach((f) => filters[f] = null); return filters; })); @@ -58,12 +91,13 @@ export class SearchFiltersComponent { * @returns {Observable} Emits true whenever a given filter config should be shown */ isActive(filterConfig: SearchFilterConfig): Observable { + console.log('isActive', filterConfig); return this.filterService.getSelectedValuesForFilter(filterConfig).pipe( mergeMap((isActive) => { if (isNotEmpty(isActive)) { return observableOf(true); } else { - return this.searchConfigService.searchOptions.pipe( + return this.searchOptions$.pipe( switchMap((options) => { return this.searchService.getFacetValuesFor(filterConfig, 1, options).pipe( filter((RD) => !RD.isLoading), @@ -73,6 +107,20 @@ export class SearchFiltersComponent { } )) } - }),startWith(true),); + }), + first(), + startWith(true),); } + + private setLoading() { + this.isLoadingFilters$.next(true); + this.cdr.detectChanges(); + } + + ngOnDestroy(): void { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + } + } diff --git a/src/app/+search-page/search-labels/search-labels.component.ts b/src/app/+search-page/search-labels/search-labels.component.ts index 08e07cce3d..fd82de326c 100644 --- a/src/app/+search-page/search-labels/search-labels.component.ts +++ b/src/app/+search-page/search-labels/search-labels.component.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { Observable } from 'rxjs'; import { Params } from '@angular/router'; import { map } from 'rxjs/operators'; import { hasValue, isNotEmpty } from '../../shared/empty.util'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-labels', @@ -24,7 +25,9 @@ export class SearchLabelsComponent { /** * Initialize the instance variable */ - constructor(private searchService: SearchService, private searchConfigService: SearchConfigurationService) { + constructor( + private searchService: SearchService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) { this.appliedFilters = this.searchConfigService.getCurrentFrontendFilters(); } diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index 123cf950f8..e56cec1724 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -8,12 +8,14 @@ import { DSpaceObjectType } from '../core/shared/dspace-object-type.model'; * This model class represents all parameters needed to request information about a certain search request */ export class SearchOptions { + configuration?: string; scope?: string; query?: string; dsoType?: DSpaceObjectType; filters?: SearchFilter[]; - constructor(options: {scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) { + constructor(options: {configuration?: string, scope?: string, query?: string, dsoType?: DSpaceObjectType, filters?: SearchFilter[]}) { + this.configuration = options.configuration; this.scope = options.scope; this.query = options.query; this.dsoType = options.dsoType; @@ -28,6 +30,9 @@ export class SearchOptions { */ toRestUrl(url: string, args: string[] = []): string { + if (isNotEmpty(this.configuration)) { + args.push(`configuration=${this.configuration}`); + } if (isNotEmpty(this.query)) { args.push(`query=${this.query}`); } diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 816e3d67bf..333faacc47 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, Subscription } from 'rxjs'; import { switchMap, } from 'rxjs/operators'; import { PaginatedList } from '../core/data/paginated-list'; @@ -7,13 +7,13 @@ import { DSpaceObject } from '../core/shared/dspace-object.model'; import { pushInOut } from '../shared/animations/push'; import { HostWindowService } from '../shared/host-window.service'; import { PaginatedSearchOptions } from './paginated-search-options.model'; -import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { SearchResult } from './search-result.model'; import { SearchService } from './search-service/search.service'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { hasValue } from '../shared/empty.util'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { getSucceededRemoteData } from '../core/shared/operators'; +import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component'; /** * This component renders a simple item page. @@ -26,7 +26,13 @@ import { getSucceededRemoteData } from '../core/shared/operators'; styleUrls: ['./search-page.component.scss'], templateUrl: './search-page.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - animations: [pushInOut] + animations: [pushInOut], + providers: [ + { + provide: SEARCH_CONFIG_SERVICE, + useClass: SearchConfigurationService + } + ] }) /** @@ -62,8 +68,7 @@ export class SearchPageComponent implements OnInit { constructor(private service: SearchService, private sidebarService: SearchSidebarService, private windowService: HostWindowService, - private filterService: SearchFilterService, - private searchConfigService: SearchConfigurationService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService) { this.isXsOrSm$ = this.windowService.isXsOrSm(); } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 0c8a4ee306..ff23f92b2c 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -28,11 +28,37 @@ import { SearchFacetFilterWrapperComponent } from './search-filters/search-filte import { SearchBooleanFilterComponent } from './search-filters/search-filter/search-boolean-filter/search-boolean-filter.component'; import { SearchHierarchyFilterComponent } from './search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; import { SearchConfigurationService } from './search-service/search-configuration.service'; +import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component'; const effects = [ SearchSidebarEffects ]; +const components = [ + SearchPageComponent, + SearchResultsComponent, + SearchSidebarComponent, + SearchSettingsComponent, + ItemSearchResultListElementComponent, + CollectionSearchResultListElementComponent, + CommunitySearchResultListElementComponent, + ItemSearchResultGridElementComponent, + CollectionSearchResultGridElementComponent, + CommunitySearchResultGridElementComponent, + CommunitySearchResultListElementComponent, + SearchFiltersComponent, + SearchFilterComponent, + SearchFacetFilterComponent, + SearchLabelsComponent, + SearchFacetFilterComponent, + SearchFacetFilterWrapperComponent, + SearchRangeFilterComponent, + SearchTextFilterComponent, + SearchHierarchyFilterComponent, + SearchBooleanFilterComponent, + SearchSwitchConfigurationComponent +]; + @NgModule({ imports: [ SearchPageRoutingModule, @@ -41,29 +67,7 @@ const effects = [ EffectsModule.forFeature(effects), CoreModule.forRoot() ], - declarations: [ - SearchPageComponent, - SearchResultsComponent, - SearchSidebarComponent, - SearchSettingsComponent, - ItemSearchResultListElementComponent, - CollectionSearchResultListElementComponent, - CommunitySearchResultListElementComponent, - ItemSearchResultGridElementComponent, - CollectionSearchResultGridElementComponent, - CommunitySearchResultGridElementComponent, - CommunitySearchResultListElementComponent, - SearchFiltersComponent, - SearchFilterComponent, - SearchFacetFilterComponent, - SearchLabelsComponent, - SearchFacetFilterComponent, - SearchFacetFilterWrapperComponent, - SearchRangeFilterComponent, - SearchTextFilterComponent, - SearchHierarchyFilterComponent, - SearchBooleanFilterComponent, - ], + declarations: components, providers: [ SearchService, SearchSidebarService, @@ -82,7 +86,8 @@ const effects = [ SearchTextFilterComponent, SearchHierarchyFilterComponent, SearchBooleanFilterComponent, - ] + ], + exports: components }) /** diff --git a/src/app/+search-page/search-service/facet-value.model.ts b/src/app/+search-page/search-service/facet-value.model.ts index a597528d50..d5102ec68d 100644 --- a/src/app/+search-page/search-service/facet-value.model.ts +++ b/src/app/+search-page/search-service/facet-value.model.ts @@ -6,7 +6,13 @@ import { autoserialize, autoserializeAs } from 'cerialize'; */ export class FacetValue { /** - * The display value of the facet value + * The display label of the facet value + */ + @autoserialize + label: string; + + /** + * The value of the facet value */ @autoserializeAs(String, 'label') value: string; diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index 292f26724d..31ba839eb5 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -1,3 +1,6 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { ActivatedRoute, Params } from '@angular/router'; + import { BehaviorSubject, combineLatest as observableCombineLatest, @@ -7,12 +10,11 @@ import { Subscription } from 'rxjs'; import { filter, map } from 'rxjs/operators'; + import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; import { SearchOptions } from '../search-options.model'; -import { ActivatedRoute, Params } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; -import { Injectable, OnDestroy } from '@angular/core'; import { RouteService } from '../../shared/services/route.service'; import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; import { RemoteData } from '../../core/data/remote-data'; @@ -28,7 +30,7 @@ export class SearchConfigurationService implements OnDestroy { /** * Default pagination settings */ - private defaultPagination = Object.assign(new PaginationComponentOptions(), { + protected defaultPagination = Object.assign(new PaginationComponentOptions(), { id: 'search-page-configuration', pageSize: 10, currentPage: 1 @@ -37,17 +39,22 @@ export class SearchConfigurationService implements OnDestroy { /** * Default sort settings */ - private defaultSort = new SortOptions('score', SortDirection.DESC); + protected defaultSort = new SortOptions('score', SortDirection.DESC); + + /** + * Default configuration parameter setting + */ + protected defaultConfiguration = 'default'; /** * Default scope setting */ - private defaultScope = ''; + protected defaultScope = ''; /** * Default query setting */ - private defaultQuery = ''; + protected defaultQuery = ''; /** * Emits the current default values @@ -74,8 +81,8 @@ export class SearchConfigurationService implements OnDestroy { * @param {RouteService} routeService * @param {ActivatedRoute} route */ - constructor(private routeService: RouteService, - private route: ActivatedRoute) { + constructor(protected routeService: RouteService, + protected route: ActivatedRoute) { this.defaults .pipe(getSucceededRemoteData()) .subscribe((defRD) => { @@ -85,10 +92,20 @@ export class SearchConfigurationService implements OnDestroy { this.subs.push(this.subscribeToSearchOptions(defs)); this.subs.push(this.subscribeToPaginatedSearchOptions(defs)); + } ) } + /** + * @returns {Observable} Emits the current configuration string + */ + getCurrentConfiguration(defaultConfiguration: string) { + return this.routeService.getQueryParameterValue('configuration').pipe(map((configuration) => { + return configuration || defaultConfiguration; + })); + } + /** * @returns {Observable} Emits the current scope's identifier */ @@ -188,6 +205,7 @@ export class SearchConfigurationService implements OnDestroy { */ subscribeToSearchOptions(defaults: SearchOptions): Subscription { return observableMerge( + this.getConfigurationPart(defaults.configuration), this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), @@ -208,6 +226,7 @@ export class SearchConfigurationService implements OnDestroy { return observableMerge( this.getPaginationPart(defaults.pagination), this.getSortPart(defaults.sort), + this.getConfigurationPart(defaults.configuration), this.getScopePart(defaults.scope), this.getQueryPart(defaults.query), this.getDSOTypePart(), @@ -226,6 +245,7 @@ export class SearchConfigurationService implements OnDestroy { if (hasNoValue(this._defaults)) { const options = new PaginatedSearchOptions({ pagination: this.defaultPagination, + configuration: this.defaultConfiguration, sort: this.defaultSort, scope: this.defaultScope, query: this.defaultQuery @@ -242,6 +262,16 @@ export class SearchConfigurationService implements OnDestroy { this.subs.forEach((sub) => { sub.unsubscribe(); }); + this.subs = []; + } + + /** + * @returns {Observable} Emits the current configuration settings as a partial SearchOptions object + */ + private getConfigurationPart(defaultConfiguration: string): Observable { + return this.getCurrentConfiguration(defaultConfiguration).pipe(map((configuration) => { + return { configuration } + })); } /** diff --git a/src/app/+search-page/search-service/search-query-response.model.ts b/src/app/+search-page/search-service/search-query-response.model.ts index ac1d8b7df3..bca6e644fc 100644 --- a/src/app/+search-page/search-service/search-query-response.model.ts +++ b/src/app/+search-page/search-service/search-query-response.model.ts @@ -34,7 +34,7 @@ export class SearchQueryResponse { * The sort parameters used in the search request */ @autoserialize - configurationName: string; + configuration: string; /** * The sort parameters used in the search request diff --git a/src/app/+search-page/search-service/search-result-element-decorator.ts b/src/app/+search-page/search-service/search-result-element-decorator.ts index 348cf7f592..59446480a3 100644 --- a/src/app/+search-page/search-service/search-result-element-decorator.ts +++ b/src/app/+search-page/search-service/search-result-element-decorator.ts @@ -1,5 +1,6 @@ import { GenericConstructor } from '../../core/shared/generic-constructor'; import { ListableObject } from '../../shared/object-collection/shared/listable-object.model'; +import { isNull } from '../../shared/empty.util'; /** * Contains the mapping between a search result component and a DSpaceObject @@ -11,12 +12,19 @@ const searchResultMap = new Map(); * @param {GenericConstructor} domainConstructor The constructor of the DSpaceObject * @returns Decorator function that performs the actual mapping on initialization of the component */ -export function searchResultFor(domainConstructor: GenericConstructor) { +export function searchResultFor(domainConstructor: GenericConstructor, configuration: string = null) { return function decorator(searchResult: any) { if (!searchResult) { return; } - searchResultMap.set(domainConstructor, searchResult); + if (isNull(configuration)) { + searchResultMap.set(domainConstructor, searchResult); + } else { + if (!searchResultMap.get(configuration)) { + searchResultMap.set(configuration, new Map()); + } + searchResultMap.get(configuration).set(domainConstructor, searchResult); + } }; } @@ -25,6 +33,10 @@ export function searchResultFor(domainConstructor: GenericConstructor} domainConstructor The DSpaceObject's constructor for which the search result component is requested * @returns The component's constructor that matches the given DSpaceObject */ -export function getSearchResultFor(domainConstructor: GenericConstructor) { - return searchResultMap.get(domainConstructor); +export function getSearchResultFor(domainConstructor: GenericConstructor, configuration: string = null) { + if (isNull(configuration) || configuration === 'default') { + return searchResultMap.get(domainConstructor); + } else { + return searchResultMap.get(configuration).get(domainConstructor); + } } diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 275b0b3340..1d5ff06193 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -7,7 +7,7 @@ import { Router, UrlSegmentGroup } from '@angular/router'; -import { map, switchMap, tap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, first, map, switchMap, take, tap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { FacetConfigSuccessResponse, @@ -23,12 +23,12 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; import { - configureRequest, + configureRequest, filterSuccessfulResponses, getResponseFromEntry, getSucceededRemoteData } from '../../core/shared/operators'; import { URLCombiner } from '../../core/url-combiner/url-combiner'; -import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { NormalizedSearchResult } from '../normalized-search-result.model'; import { SearchOptions } from '../search-options.model'; import { SearchResult } from '../search-result.model'; @@ -63,6 +63,16 @@ export class SearchService implements OnDestroy { */ private facetLinkPathPrefix = 'discover/facets/'; + /** + * When true, a new search request is always dispatched + */ + private forceBypassCache = false; + + /** + * The ResponseParsingService constructor name + */ + private parser: GenericConstructor = SearchResponseParsingService; + /** * Subscription to unsubscribe from */ @@ -78,6 +88,19 @@ export class SearchService implements OnDestroy { ) { } + /** + * Method to set service options + * @param {GenericConstructor} parser The configuration necessary to perform this search + * @param {boolean} forceBypassCache When true, a new search request is always dispatched + * @returns {Observable>>>} Emits a paginated list with all search results found + */ + setServiceOptions(parser: GenericConstructor, forceBypassCache: boolean) { + if (parser) { + this.parser = parser; + } + this.forceBypassCache = forceBypassCache; + } + /** * Method to retrieve a paginated list of search results from the server * @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search @@ -90,13 +113,15 @@ export class SearchService implements OnDestroy { url = (searchOptions as PaginatedSearchOptions).toRestUrl(url); } const request = new GetRequest(this.requestService.generateRequestId(), url); + const getResponseParserFn: () => GenericConstructor = () => { + return this.parser; + }; + return Object.assign(request, { - getResponseParser(): GenericConstructor { - return SearchResponseParsingService; - } + getResponseParser: getResponseParserFn }); }), - configureRequest(this.requestService) + configureRequest(this.requestService, this.forceBypassCache), ); const requestEntryObs = requestObs.pipe( switchMap((request: RestRequest) => this.requestService.getByHref(request.href)) @@ -111,8 +136,11 @@ export class SearchService implements OnDestroy { // turn dspace href from search results to effective list of DSpaceObjects // Turn list of observable remote data DSO's into observable remote data object with list of DSO const dsoObs: Observable> = sqrObs.pipe( + // filter((sqr: SearchQueryResponse) => isNotUndefined(sqr)), map((sqr: SearchQueryResponse) => { - return sqr.objects.map((nsr: NormalizedSearchResult) => { + return sqr.objects + .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.dspaceObject)) + .map((nsr: NormalizedSearchResult) => { return this.rdb.buildSingle(nsr.dspaceObject); }) }), @@ -126,7 +154,7 @@ export class SearchService implements OnDestroy { let co = DSpaceObject; if (dsos.payload[index]) { const constructor: GenericConstructor = dsos.payload[index].constructor as GenericConstructor; - co = getSearchResultFor(constructor); + co = getSearchResultFor(constructor, searchOptions.configuration); return Object.assign(new co(), object, { dspaceObject: dsos.payload[index] }); @@ -134,6 +162,7 @@ export class SearchService implements OnDestroy { return undefined; } }); + // .filter((object) => isNotUndefined(object)); }) ); @@ -156,7 +185,7 @@ export class SearchService implements OnDestroy { * @param {string} scope UUID of the object for which config the filter config is requested, when no scope is provided the configuration for the whole repository is loaded * @returns {Observable>} The found filter configuration */ - getConfig(scope?: string): Observable> { + getConfig(scope?: string, configuration?: string): Observable> { const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix).pipe( map((url: string) => { const args: string[] = []; @@ -165,6 +194,10 @@ export class SearchService implements OnDestroy { args.push(`scope=${scope}`); } + if (isNotEmpty(configuration)) { + args.push(`configuration=${configuration}`); + } + if (isNotEmpty(args)) { url = new URLCombiner(url, `?${args.join('&')}`).toString(); } @@ -176,7 +209,7 @@ export class SearchService implements OnDestroy { } }); }), - configureRequest(this.requestService) + configureRequest(this.requestService, this.forceBypassCache) ); const requestEntryObs = requestObs.pipe( @@ -202,6 +235,7 @@ export class SearchService implements OnDestroy { * @returns {Observable>>} Emits the given page of facet values */ getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable>> { + console.log('getFacetValuesFor'); const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe( map((url: string) => { const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`]; @@ -219,7 +253,8 @@ export class SearchService implements OnDestroy { } }); }), - configureRequest(this.requestService) + configureRequest(this.requestService, this.forceBypassCache), + first() ); const requestEntryObs = requestObs.pipe( diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 7fc5645fcc..24b2ee4778 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -1,10 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { SortDirection, SortOptions } from '../../core/cache/models/sort-options.model'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { Observable } from 'rxjs'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-settings', @@ -30,7 +31,7 @@ export class SearchSettingsComponent implements OnInit { constructor(private service: SearchService, private route: ActivatedRoute, private router: Router, - private searchConfigurationService: SearchConfigurationService) { + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigurationService: SearchConfigurationService) { } /** diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index 5ff1e3c8fa..ac9c834443 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -10,6 +10,7 @@
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss index b5bd6dd30d..960a8dfa8c 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss @@ -8,6 +8,9 @@ ds-view-mode-switch { margin-bottom: $spacer; } + ds-search-switch-configuration { + margin-bottom: 2*$spacer !important; + } .sidebar-content > *:not(:last-child) { margin-bottom: 4*$spacer; display: block; diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.ts b/src/app/+search-page/search-sidebar/search-sidebar.component.ts index 8b68cda793..bdd90633b0 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.ts @@ -1,5 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model'; + /** * This component renders a simple item page. * The route parameter 'id' is used to request the item it represents. @@ -17,6 +19,11 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; */ export class SearchSidebarComponent { + /** + * The list of available configuration options + */ + @Input() configurationList: SearchConfigurationOption[]; + /** * The total amount of results */ diff --git a/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts new file mode 100644 index 0000000000..7f9b4acd96 --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts @@ -0,0 +1,4 @@ +export interface SearchConfigurationOption { + value: string; + label: string; +} diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html new file mode 100644 index 0000000000..5b1bdc1ddd --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html @@ -0,0 +1,13 @@ +
+
{{ 'search.switch-configuration.title' | translate}}
+ + + +
diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts new file mode 100644 index 0000000000..c7367c5f3f --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.spec.ts @@ -0,0 +1,103 @@ +// import { SearchService } from '../../search-service/search.service'; +// import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +// import { SearchSettingsComponent } from '../../search-settings/search-settings.component'; +// import { Observable } from 'rxjs/Observable'; +// import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; +// import { SortOptions } from '../../../core/cache/models/sort-options.model'; +// import { TranslateModule } from '@ngx-translate/core'; +// import { RouterTestingModule } from '@angular/router/testing'; +// import { ActivatedRoute } from '@angular/router'; +// import { SearchSidebarService } from '../../search-sidebar/search-sidebar.service'; +// import { NO_ERRORS_SCHEMA } from '@angular/core'; +// import { EnumKeysPipe } from '../../../shared/utils/enum-keys-pipe'; +// import { By } from '@angular/platform-browser'; +// +// describe('SearchSettingsComponent', () => { +// +// let comp: SearchSettingsComponent; +// let fixture: ComponentFixture; +// let searchServiceObject: SearchService; +// +// const pagination: PaginationComponentOptions = new PaginationComponentOptions(); +// pagination.id = 'search-results-pagination'; +// pagination.currentPage = 1; +// pagination.pageSize = 10; +// const sort: SortOptions = new SortOptions(); +// const mockResults = [ 'test', 'data' ]; +// const searchServiceStub = { +// searchOptions: { pagination: pagination, sort: sort }, +// search: () => mockResults +// }; +// const queryParam = 'test query'; +// const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; +// const activatedRouteStub = { +// queryParams: Observable.of({ +// query: queryParam, +// scope: scopeParam +// }) +// }; +// +// const sidebarService = { +// isCollapsed: Observable.of(true), +// collapse: () => this.isCollapsed = Observable.of(true), +// expand: () => this.isCollapsed = Observable.of(false) +// } +// +// beforeEach(async(() => { +// TestBed.configureTestingModule({ +// imports: [ TranslateModule.forRoot(), RouterTestingModule.withRoutes([]) ], +// declarations: [ SearchSettingsComponent, EnumKeysPipe ], +// providers: [ +// { provide: SearchService, useValue: searchServiceStub }, +// +// { provide: ActivatedRoute, useValue: activatedRouteStub }, +// { +// provide: SearchSidebarService, +// useValue: sidebarService +// }, +// ], +// schemas: [ NO_ERRORS_SCHEMA ] +// }).compileComponents(); +// })); +// +// beforeEach(() => { +// fixture = TestBed.createComponent(SearchSettingsComponent); +// comp = fixture.componentInstance; +// +// // SearchPageComponent test instance +// fixture.detectChanges(); +// searchServiceObject = (comp as any).service; +// spyOn(comp, 'reloadRPP'); +// spyOn(comp, 'reloadOrder'); +// spyOn(searchServiceObject, 'search').and.callThrough(); +// +// }); +// +// it('it should show the order settings with the respective selectable options', () => { +// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); +// expect(orderSetting).toBeDefined(); +// const childElements = orderSetting.query(By.css('.form-control')).children; +// expect(childElements.length).toEqual(2); +// +// }); +// +// it('it should show the size settings with the respective selectable options', () => { +// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); +// expect(pageSizeSetting).toBeDefined(); +// const childElements = pageSizeSetting.query(By.css('.form-control')).children; +// expect(childElements.length).toEqual(7); +// }); +// +// it('should have the proper order value selected by default', () => { +// const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); +// const childElementToBeSelected = orderSetting.query(By.css('.form-control option[value="0"][selected="selected"]')) +// expect(childElementToBeSelected).toBeDefined(); +// }); +// +// it('should have the proper rpp value selected by default', () => { +// const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); +// const childElementToBeSelected = pageSizeSetting.query(By.css('.form-control option[value="10"][selected="selected"]')) +// expect(childElementToBeSelected).toBeDefined(); +// }); +// +// }); diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts new file mode 100644 index 0000000000..d894b4f454 --- /dev/null +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.ts @@ -0,0 +1,54 @@ +import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { NavigationExtras, Router } from '@angular/router'; + +import { Subscription } from 'rxjs'; + +import { hasValue } from '../../shared/empty.util'; +import { MYDSPACE_ROUTE, SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { MyDSpaceConfigurationValueType } from '../../+my-dspace-page/my-dspace-configuration-value-type'; +import { SearchConfigurationOption } from './search-configuration-option.model'; + +@Component({ + selector: 'ds-search-switch-configuration', + styleUrls: ['./search-switch-configuration.component.scss'], + templateUrl: './search-switch-configuration.component.html', +}) +export class SearchSwitchConfigurationComponent implements OnDestroy, OnInit { + + /** + * The list of available configuration options + */ + @Input() configurationList: SearchConfigurationOption[] = []; + + public selectedOption: string; + + private sub: Subscription; + + constructor(private router: Router, + @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) { + } + + ngOnInit() { + this.searchConfigService.getCurrentConfiguration('default') + .subscribe((currentConfiguration) => this.selectedOption = currentConfiguration); + } + + onSelect(event: Event) { + const navigationExtras: NavigationExtras = { + queryParams: {configuration: this.selectedOption}, + }; + + this.router.navigate([MYDSPACE_ROUTE], navigationExtras); + } + + compare(item1: MyDSpaceConfigurationValueType, item2: MyDSpaceConfigurationValueType) { + return item1 === item2; + } + + ngOnDestroy() { + if (hasValue(this.sub)) { + this.sub.unsubscribe(); + } + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 03edd698fd..b0c9305a66 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -16,6 +16,7 @@ export function getItemModulePath() { { path: 'communities', loadChildren: './+community-page/community-page.module#CommunityPageModule' }, { path: 'collections', loadChildren: './+collection-page/collection-page.module#CollectionPageModule' }, { path: ITEM_MODULE_PATH, loadChildren: './+item-page/item-page.module#ItemPageModule' }, + { path: 'mydspace', loadChildren: './+my-dspace-page/my-dspace-page.module#MyDSpacePageModule', canActivate: [AuthenticatedGuard] }, { path: 'search', loadChildren: './+search-page/search-page.module#SearchPageModule' }, { path: 'browse', loadChildren: './+browse-by/browse-by.module#BrowseByModule' }, { path: 'admin', loadChildren: './+admin/admin.module#AdminModule', canActivate: [AuthenticatedGuard] }, diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index da1857b1c0..cdf7dd6de0 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs'; -import { filter, map, mergeMap, switchMap, take } from 'rxjs/operators'; +import { filter, find, first, map, mergeMap, switchMap, take } from 'rxjs/operators'; +import { remove } from 'lodash'; import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CacheableObject } from '../cache/object-cache.reducer'; @@ -123,7 +124,10 @@ export class RequestService { // TODO to review "forceBypassCache" param when https://github.com/DSpace/dspace-angular/issues/217 will be fixed configure(request: RestRequest, forceBypassCache: boolean = false): void { const isGetRequest = request.method === RestRequestMethod.GET; - if (!isGetRequest || !this.isCachedOrPending(request) || (forceBypassCache && !this.isPending(request))) { + if (forceBypassCache) { + this.clearRequestsOnTheirWayToTheStore(request); + } + if (!isGetRequest || (forceBypassCache && !this.isPending(request)) || !this.isCachedOrPending(request)) { this.dispatchRequest(request); if (isGetRequest) { this.trackRequestsOnTheirWayToTheStore(request); @@ -248,6 +252,19 @@ export class RequestService { }); } + /** + * This method will store the href of every GET request that gets configured in a local variable, and + * remove it as soon as it can be found in the store. + */ + private clearRequestsOnTheirWayToTheStore(request: GetRequest) { + this.store.pipe(select(this.entryFromUUIDSelector(request.uuid)), + find((re: RequestEntry) => hasValue(re))) + .subscribe((re: RequestEntry) => { + if (!re.responsePending) { + remove(this.requestsOnTheirWayToTheStore, (item) => item === request.href); + } + }); + } /** * Dispatch commit action to send all changes (for a certain method) to the server (buffer) * @param {RestRequestMethod} method RestRequestMethod for which the changes should be committed diff --git a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts index 41298b559f..452e5b30ca 100644 --- a/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts +++ b/src/app/shared/auth-nav-menu/user-menu/user-menu.component.ts @@ -37,9 +37,5 @@ export class UserMenuComponent implements OnInit { // set user this.user$ = this.store.pipe(select(getAuthenticatedUser)); - this.user$.subscribe((user) => { - console.log(user, user.name); - }) - } } diff --git a/src/app/shared/pagination/pagination-component-options.model.ts b/src/app/shared/pagination/pagination-component-options.model.ts index 30ed2becd2..07756f1e6f 100644 --- a/src/app/shared/pagination/pagination-component-options.model.ts +++ b/src/app/shared/pagination/pagination-component-options.model.ts @@ -12,11 +12,19 @@ export class PaginationComponentOptions extends NgbPaginationConfig { */ currentPage = 1; + /** + * Maximum number of pages to display. + */ + maxSize = 10; + /** * A number array that represents options for a context pagination limit. */ pageSizeOptions: number[] = [5, 10, 20, 40, 60, 80, 100]; + /** + * Number of items per page. + */ pageSize: number; } diff --git a/src/app/shared/roles/role.directive.ts b/src/app/shared/roles/role.directive.ts new file mode 100644 index 0000000000..d71e520e35 --- /dev/null +++ b/src/app/shared/roles/role.directive.ts @@ -0,0 +1,114 @@ +import { + ChangeDetectorRef, + Directive, + Input, + OnChanges, + OnDestroy, + SimpleChanges, + TemplateRef, + ViewContainerRef +} from '@angular/core'; + +import { combineLatest, Observable, Subscription } from 'rxjs'; +import { filter, first, map } from 'rxjs/operators'; + +import { hasValue } from '../empty.util'; +import { RoleService } from '../../core/roles/role.service'; +import { RoleType } from '../../core/roles/role-types'; + +@Directive({ + selector: '[dsShowOnlyForRole],[dsShowExceptForRole]' +}) +/** + * Structural Directive for showing or hiding a template based on current user role + */ +export class RoleDirective implements OnChanges, OnDestroy { + + /** + * The role or list of roles that can show template + */ + @Input() dsShowOnlyForRole: RoleType | RoleType[]; + + /** + * The role or list of roles that cannot show template + */ + @Input() dsShowExceptForRole: RoleType | RoleType[]; + + private subs: Subscription[] = []; + + constructor( + private roleService: RoleService, + private viewContainer: ViewContainerRef, + private changeDetector: ChangeDetectorRef, + private templateRef: TemplateRef + ) { + } + + ngOnChanges(changes: SimpleChanges): void { + const onlyChanges = changes.dsShowOnlyForRole; + const exceptChanges = changes.dsShowExceptForRole; + this.hasRoles(this.dsShowOnlyForRole); + if (changes.dsShowOnlyForRole) { + this.validateOnly() + } else if (changes.dsShowExceptForRole) { + this.validateExcept() + } + } + + ngOnDestroy(): void { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } + + /** + * Show template in view container + */ + private showTemplateBlockInView(): void { + this.viewContainer.clear(); + if (!this.templateRef) { + return; + } + + this.viewContainer.createEmbeddedView(this.templateRef); + this.changeDetector.markForCheck(); + } + + /** + * Validate the list of roles that can show template + */ + private validateOnly(): void { + this.subs.push(this.hasRoles(this.dsShowOnlyForRole).pipe(filter((hasRole) => hasRole)) + .subscribe((hasRole) => { + this.showTemplateBlockInView(); + })); + } + + /** + * Validate the list of roles that cannot show template + */ + private validateExcept(): void { + this.subs.push(this.hasRoles(this.dsShowExceptForRole).pipe(filter((hasRole) => !hasRole)) + .subscribe((hasRole) => { + this.showTemplateBlockInView(); + })); + } + + /** + * Check if current user role is included in the specified role list + * + * @param roles + * The role or the list of roles + * @returns {Observable} + * observable of true if current user role is included in the specified role list, observable of false otherwise + */ + private hasRoles(roles: RoleType | RoleType[]): Observable { + const toValidate: RoleType[] = (Array.isArray(roles)) ? roles : [roles]; + const checks: Array> = toValidate.map((role) => this.roleService.checkRole(role)); + + return combineLatest(checks).pipe( + map((permissions: boolean[]) => permissions.includes(true)), + first() + ) + } +} diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 21a90daed4..ea96bd8114 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -3,6 +3,7 @@ import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { Router } from '@angular/router'; import { hasValue, isNotEmpty } from '../empty.util'; import { QueryParamsHandling } from '@angular/router/src/config'; +import { MYDSPACE_ROUTE } from '../../+my-dspace-page/my-dspace-page.component'; /** * This component renders a simple item page. @@ -64,7 +65,7 @@ export class SearchFormComponent { updateSearch(data: any) { const newUrl = hasValue(this.currentUrl) ? this.currentUrl : '/search'; let handling: QueryParamsHandling = '' ; - if (this.currentUrl === '/search') { + if (this.currentUrl === '/search' || this.currentUrl === MYDSPACE_ROUTE) { handling = 'merge'; } this.router.navigate([newUrl], { diff --git a/src/app/shared/services/route.service.ts b/src/app/shared/services/route.service.ts index a55a967d3b..f500b18082 100644 --- a/src/app/shared/services/route.service.ts +++ b/src/app/shared/services/route.service.ts @@ -1,9 +1,10 @@ import { Injectable } from '@angular/core'; -import { ActivatedRoute, NavigationEnd, Params, Router, } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, Params, Router, RouterStateSnapshot, } from '@angular/router'; import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { select, Store } from '@ngrx/store'; +import { isEqual } from 'lodash'; import { AppState } from '../../app.reducer'; import { AddUrlToHistoryAction } from '../history/history.actions'; @@ -16,35 +17,35 @@ export class RouteService { } getQueryParameterValues(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => [...params.getAll(paramName)]), distinctUntilChanged() ); } getQueryParameterValue(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.get(paramName)), distinctUntilChanged() ); } hasQueryParam(paramName: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.has(paramName)), distinctUntilChanged() ); } hasQueryParamWithValue(paramName: string, paramValue: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((params) => params.getAll(paramName).indexOf(paramValue) > -1), distinctUntilChanged() ); } getQueryParamsWithPrefix(prefix: string): Observable { - return this.route.queryParamMap.pipe( + return this.getQueryParamMap().pipe( map((qparams) => { const params = {}; qparams.keys @@ -57,6 +58,19 @@ export class RouteService { distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))); } + public getQueryParamMap(): Observable { + return this.route.queryParamMap.pipe( + map((paramMap) => { + const snapshot: RouterStateSnapshot = this.router.routerState.snapshot; + // Due to an Angular bug, sometimes change of QueryParam is not detected so double checks with route snapshot + if (!isEqual(paramMap, snapshot.root.queryParamMap)) { + return snapshot.root.queryParamMap; + } else { + return paramMap; + } + })) + } + public saveRouting(): void { this.router.events .pipe(filter((event) => event instanceof NavigationEnd)) diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index b7250a6e18..1acff996fa 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -77,6 +77,22 @@ import { DsDatePickerComponent } from './form/builder/ds-dynamic-form-ui/models/ import { DsDynamicLookupComponent } from './form/builder/ds-dynamic-form-ui/models/lookup/dynamic-lookup.component'; import { MockAdminGuard } from './mocks/mock-admin-guard.service'; import { AlertsComponent } from './alerts/alerts.component'; +import { MyDSpaceResultListElementComponent } from './object-list/my-dspace-result-list-element/my-dspace-result-list-element.component'; +import { MessageBoardComponent } from './message-board/message-board.component'; +import { MessageComponent } from './message-board/message/message.component'; +import { MyDSpaceResultDetailElementComponent } from './object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component'; +import { ClaimedTaskActionsComponent } from './mydspace-actions/claimed-task/claimed-task-actions.component'; +import { PoolTaskActionsComponent } from './mydspace-actions/pool-task/pool-task-actions.component'; +import { ObjectDetailComponent } from './object-detail/object-detail.component'; +import { WrapperDetailElementComponent } from './object-detail/wrapper-detail-element/wrapper-detail-element.component'; +import { ItemDetailPreviewComponent } from './object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component'; +import { MyDSpaceItemStatusComponent } from './object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; +import { WorkspaceitemActionsComponent } from './mydspace-actions/workspaceitem/workspaceitem-actions.component'; +import { WorkflowitemActionsComponent } from './mydspace-actions/workflowitem/workflowitem-actions.component'; +import { ItemSubmitterComponent } from './object-collection/shared/mydspace-item-submitter/item-submitter.component'; +import { ItemActionsComponent } from './mydspace-actions/item/item-actions.component'; +import { ClaimedTaskActionsApproveComponent } from './mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component'; +import { ClaimedTaskActionsRejectComponent } from './mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component'; import { ObjNgFor } from './utils/object-ngfor.pipe'; import { BrowseByComponent } from './browse-by/browse-by.component'; import { BrowseEntryListElementComponent } from './object-list/browse-entry-list-element/browse-entry-list-element.component'; @@ -95,6 +111,20 @@ import { EditComColPageComponent } from './comcol-forms/edit-comcol-page/edit-co import { DeleteComColPageComponent } from './comcol-forms/delete-comcol-page/delete-comcol-page.component'; import { LangSwitchComponent } from './lang-switch/lang-switch.component'; import { ComcolPageBrowseByComponent } from './comcol-page-browse-by/comcol-page-browse-by.component'; +import { ItemListPreviewComponent } from './object-list/item-list-preview/item-list-preview.component'; +import { ItemPageAuthorFieldComponent } from '../+item-page/simple/field-components/specific-field/author/item-page-author-field.component'; +import { ItemPageDateFieldComponent } from '../+item-page/simple/field-components/specific-field/date/item-page-date-field.component'; +import { ItemPageAbstractFieldComponent } from '../+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component'; +import { ItemPageUriFieldComponent } from '../+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component'; +import { ItemPageTitleFieldComponent } from '../+item-page/simple/field-components/specific-field/title/item-page-title-field.component'; +import { ItemPageSpecificFieldComponent } from '../+item-page/simple/field-components/specific-field/item-page-specific-field.component'; +import { FileSectionComponent } from '../+item-page/simple/field-components/file-section/file-section.component'; +import { MetadataFieldWrapperComponent } from '../+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component'; +import { CollectionsComponent } from '../+item-page/field-components/collections/collections.component'; +import { MetadataValuesComponent } from '../+item-page/field-components/metadata-values/metadata-values.component'; +import { MetadataUriValuesComponent } from '../+item-page/field-components/metadata-uri-values/metadata-uri-values.component'; +import { RoleDirective } from './roles/role.directive'; +import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -136,6 +166,7 @@ const COMPONENTS = [ // put shared components here AlertsComponent, AuthNavMenuComponent, + UserMenuComponent, ChipsComponent, ComcolPageContentComponent, ComcolPageHeaderComponent, @@ -163,11 +194,15 @@ const COMPONENTS = [ LoadingComponent, LogInComponent, LogOutComponent, + MessageBoardComponent, + MessageComponent, NumberPickerComponent, ObjectListComponent, + ObjectDetailComponent, + ObjectGridComponent, AbstractListableElementComponent, WrapperListElementComponent, - ObjectGridComponent, + WrapperDetailElementComponent, WrapperGridElementComponent, ObjectCollectionComponent, PaginationComponent, @@ -176,6 +211,17 @@ const COMPONENTS = [ GridThumbnailComponent, UploaderComponent, WrapperListElementComponent, + ItemListPreviewComponent, + MyDSpaceItemStatusComponent, + ItemSubmitterComponent, + ItemDetailPreviewComponent, + ClaimedTaskActionsComponent, + ClaimedTaskActionsApproveComponent, + ClaimedTaskActionsRejectComponent, + ItemActionsComponent, + PoolTaskActionsComponent, + WorkflowitemActionsComponent, + WorkspaceitemActionsComponent, ViewModeSwitchComponent, TruncatableComponent, TruncatablePartComponent, @@ -188,12 +234,15 @@ const ENTRY_COMPONENTS = [ ItemListElementComponent, CollectionListElementComponent, CommunityListElementComponent, + MyDSpaceResultListElementComponent, SearchResultListElementComponent, ItemGridElementComponent, CollectionGridElementComponent, CommunityGridElementComponent, SearchResultGridElementComponent, BrowseEntryListElementComponent, + MyDSpaceResultDetailElementComponent, + SearchResultGridElementComponent, DsDynamicListComponent, DsDynamicLookupComponent, DsDynamicScrollableDropdownComponent, @@ -206,6 +255,20 @@ const ENTRY_COMPONENTS = [ DsDatePickerInlineComponent ]; +const SHARED_ITEM_PAGE_COMPONENTS = [ + CollectionsComponent, + FileSectionComponent, + ItemPageAuthorFieldComponent, + ItemPageDateFieldComponent, + ItemPageAbstractFieldComponent, + ItemPageUriFieldComponent, + ItemPageTitleFieldComponent, + ItemPageSpecificFieldComponent, + MetadataFieldWrapperComponent, + MetadataValuesComponent, + MetadataUriValuesComponent +]; + const PROVIDERS = [ TruncatableService, MockAdminGuard, @@ -220,7 +283,8 @@ const DIRECTIVES = [ DragClickDirective, DebounceDirective, ClickOutsideDirective, - AuthorityConfidenceStateDirective + AuthorityConfidenceStateDirective, + RoleDirective ]; @NgModule({ @@ -232,6 +296,7 @@ const DIRECTIVES = [ ...COMPONENTS, ...DIRECTIVES, ...ENTRY_COMPONENTS, + ...SHARED_ITEM_PAGE_COMPONENTS ], providers: [ ...PROVIDERS @@ -240,6 +305,7 @@ const DIRECTIVES = [ ...MODULES, ...PIPES, ...COMPONENTS, + ...SHARED_ITEM_PAGE_COMPONENTS, ...DIRECTIVES ], entryComponents: [ diff --git a/src/app/shared/truncatable/truncatable.component.html b/src/app/shared/truncatable/truncatable.component.html index c03e93c2ce..b524e5e754 100644 --- a/src/app/shared/truncatable/truncatable.component.html +++ b/src/app/shared/truncatable/truncatable.component.html @@ -1,3 +1,3 @@ -
+
-
\ No newline at end of file +
From 013d46429419de4924ad1643ff30c18a268e1a9a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Mon, 11 Mar 2019 20:07:11 +0100 Subject: [PATCH 096/205] Intermediate commit --- .../edit-collection-page.component.ts | 1 - .../edit-item-page.component.ts | 2 +- .../my-dspace-configuration.service.ts | 4 ++ .../my-dspace-page.component.html | 5 ++- .../my-dspace-page.component.ts | 14 +++--- .../+my-dspace-page/my-dspace-result.model.ts | 2 +- .../my-dspace-results.component.html | 2 +- .../my-dspace-results.component.ts | 6 ++- .../search-service/search.service.ts | 44 +++++++++---------- .../search-settings.component.ts | 4 +- .../search-sidebar.component.html | 2 +- .../search-sidebar.component.ts | 5 +++ src/app/core/auth/auth.service.ts | 22 ++-------- src/app/core/data/collection-data.service.ts | 1 - src/app/core/data/community-data.service.ts | 5 +-- .../data/default-change-analyzer.service.ts | 2 - .../data/mydspace-response-parsing.service.ts | 33 +++++++++++--- src/app/core/data/request.service.ts | 6 +-- .../models/normalized-eperson.model.ts | 3 -- .../message-response-parsing.service.ts | 4 +- .../core/tasks/claimed-task-data.service.ts | 5 +-- .../normalized-claimed-task-object.model.ts | 2 +- .../normalized-pool-task-object.model.ts | 2 +- .../models/normalized-task-object.model.ts | 3 +- src/app/core/tasks/pool-task-data.service.ts | 5 +-- src/app/core/tasks/tasks.service.ts | 5 +-- .../user-menu/user-menu.component.html | 4 +- .../user-menu/user-menu.component.ts | 7 +++ .../input-suggestions.component.ts | 2 +- .../message-board/message-board.component.ts | 19 +++----- .../claimed-task-actions.component.html | 2 +- .../claimed-task-actions.component.ts | 3 +- .../item/item-actions.component.html | 14 ++---- .../item/item-actions.component.ts | 17 +------ .../mydspace-actions-service.factory.ts | 3 +- .../mydspace-actions/mydspace-actions.ts | 5 +-- .../pool-task-actions.component.html | 4 +- .../pool-task/pool-task-actions.component.ts | 3 +- .../workflowitem-actions.component.ts | 4 +- .../workspaceitem-actions.component.html | 2 +- .../workspaceitem-actions.component.ts | 3 +- .../notifications/notifications.service.ts | 1 - ...-dspace-result-detail-element.component.ts | 2 +- ...-dspace-result-detail-element.component.ts | 10 ++--- ...-dspace-result-detail-element.component.ts | 10 ++--- .../item-list-preview.component.ts | 2 +- ...my-dspace-result-list-element.component.ts | 3 +- ...-dspace-result-list-element.component.html | 2 +- .../view-mode-switch.component.html | 20 +++++++-- .../view-mode-switch.component.ts | 17 ++++++- 50 files changed, 181 insertions(+), 167 deletions(-) diff --git a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts index a3978a5e43..ba70bd26c6 100644 --- a/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts +++ b/src/app/+collection-page/edit-collection-page/edit-collection-page.component.ts @@ -1,7 +1,6 @@ import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { EditComColPageComponent } from '../../shared/comcol-forms/edit-comcol-page/edit-comcol-page.component'; -import { NormalizedCollection } from '../../core/cache/models/normalized-collection.model'; import { Collection } from '../../core/shared/collection.model'; import { CollectionDataService } from '../../core/data/collection-data.service'; diff --git a/src/app/+item-page/edit-item-page/edit-item-page.component.ts b/src/app/+item-page/edit-item-page/edit-item-page.component.ts index 4ea47f08e7..eafc04ae0b 100644 --- a/src/app/+item-page/edit-item-page/edit-item-page.component.ts +++ b/src/app/+item-page/edit-item-page/edit-item-page.component.ts @@ -1,6 +1,6 @@ import { fadeIn, fadeInOut } from '../../shared/animations/fade'; import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; import { Observable } from 'rxjs'; diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts index 4a06582425..a580ba9920 100644 --- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts @@ -97,4 +97,8 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { ) } + public getCurrentView(): Observable { + return this.routeService.getQueryParameterValue('view'); + } + } diff --git a/src/app/+my-dspace-page/my-dspace-page.component.html b/src/app/+my-dspace-page/my-dspace-page.component.html index 6f15070303..b38edebd97 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.html +++ b/src/app/+my-dspace-page/my-dspace-page.component.html @@ -5,7 +5,8 @@ + [resultCount]="(resultsRD$ | async)?.payload.totalElements" + [viewModeList]="viewModeList">
- +
- +

{{'mydspace.results.no-results' | translate}}

diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts index 1058a3eefd..e6a086fd46 100644 --- a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts +++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts @@ -8,6 +8,7 @@ import { MyDSpaceResult } from '../my-dspace-result.model'; import { SearchOptions } from '../../+search-page/search-options.model'; import { PaginatedList } from '../../core/data/paginated-list'; import { ViewMode } from '../../core/shared/view-mode.model'; +import { isEmpty } from '../../shared/empty.util'; /** * This component renders a simple item page. @@ -28,6 +29,9 @@ export class MyDSpaceResultsComponent { @Input() sortConfig: SortOptions; @Input() viewMode: ViewMode; - public hasBorder = true; + hasBorder = true; + isLoading() { + return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading; + } } diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 1d5ff06193..e98fecd830 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -1,13 +1,7 @@ import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { Injectable, OnDestroy } from '@angular/core'; -import { - ActivatedRoute, - NavigationExtras, - PRIMARY_OUTLET, - Router, - UrlSegmentGroup -} from '@angular/router'; -import { distinctUntilChanged, filter, first, map, switchMap, take, tap } from 'rxjs/operators'; +import { ActivatedRoute, NavigationExtras, PRIMARY_OUTLET, Router, UrlSegmentGroup } from '@angular/router'; +import { first, map, switchMap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; import { FacetConfigSuccessResponse, @@ -22,11 +16,7 @@ import { RequestService } from '../../core/data/request.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { GenericConstructor } from '../../core/shared/generic-constructor'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; -import { - configureRequest, filterSuccessfulResponses, - getResponseFromEntry, - getSucceededRemoteData -} from '../../core/shared/operators'; +import { configureRequest, getResponseFromEntry, getSucceededRemoteData } from '../../core/shared/operators'; import { URLCombiner } from '../../core/url-combiner/url-combiner'; import { hasValue, isEmpty, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { NormalizedSearchResult } from '../normalized-search-result.model'; @@ -47,6 +37,7 @@ import { CommunityDataService } from '../../core/data/community-data.service'; import { ViewMode } from '../../core/shared/view-mode.model'; import { ResourceType } from '../../core/shared/resource-type'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; +import { RouteService } from '../../shared/services/route.service'; /** * Service that performs all general actions that have to do with the search page @@ -80,6 +71,7 @@ export class SearchService implements OnDestroy { constructor(private router: Router, private route: ActivatedRoute, + private routeService: RouteService, protected requestService: RequestService, private rdb: RemoteDataBuildService, private halService: HALEndpointService, @@ -235,7 +227,6 @@ export class SearchService implements OnDestroy { * @returns {Observable>>} Emits the given page of facet values */ getFacetValuesFor(filterConfig: SearchFilterConfig, valuePage: number, searchOptions?: SearchOptions, filterQuery?: string): Observable>> { - console.log('getFacetValuesFor'); const requestObs = this.halService.getEndpoint(this.facetLinkPathPrefix + filterConfig.name).pipe( map((url: string) => { const args: string[] = [`page=${valuePage - 1}`, `size=${filterConfig.pageSize}`]; @@ -323,9 +314,9 @@ export class SearchService implements OnDestroy { * @returns {Observable} The current view mode */ getViewMode(): Observable { - return this.route.queryParams.pipe(map((params) => { - if (isNotEmpty(params.view) && hasValue(params.view)) { - return params.view; + return this.routeService.getQueryParamMap().pipe(map((params) => { + if (isNotEmpty(params.get('view')) && hasValue(params.get('view'))) { + return params.get('view'); } else { return ViewMode.List; } @@ -337,12 +328,21 @@ export class SearchService implements OnDestroy { * @param {ViewMode} viewMode Mode to switch to */ setViewMode(viewMode: ViewMode) { - const navigationExtras: NavigationExtras = { - queryParams: { view: viewMode }, - queryParamsHandling: 'merge' - }; + this.routeService.getQueryParameterValue('pageSize').pipe(first()) + .subscribe((pageSize) => { + let queryParams = { view: viewMode }; + if (viewMode === ViewMode.Detail) { + queryParams = Object.assign(queryParams, {pageSize: '1'}); + } else if (pageSize === '1') { + queryParams = Object.assign(queryParams, {pageSize: '10'}); + } + const navigationExtras: NavigationExtras = { + queryParams: queryParams, + queryParamsHandling: 'merge' + }; - this.router.navigate([this.getSearchLink()], navigationExtras); + this.router.navigate([this.getSearchLink()], navigationExtras); + }) } /** diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 24b2ee4778..d26545b7f1 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -54,7 +54,7 @@ export class SearchSettingsComponent implements OnInit { }, queryParamsHandling: 'merge' }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } /** @@ -71,6 +71,6 @@ export class SearchSettingsComponent implements OnInit { }, queryParamsHandling: 'merge' }; - this.router.navigate([ '/search' ], navigationExtras); + this.router.navigate([ this.service.getSearchLink() ], navigationExtras); } } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index ac9c834443..3934f8cdac 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -8,7 +8,7 @@
- + diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.ts b/src/app/shared/view-mode-switch/view-mode-switch.component.ts index 07c47435ff..b011fce6a0 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.ts +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.ts @@ -1,7 +1,10 @@ +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; + import { Subscription } from 'rxjs'; -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { SearchService } from './../../+search-page/search-service/search.service'; + +import { SearchService } from '../../+search-page/search-service/search.service'; import { ViewMode } from '../../core/shared/view-mode.model'; +import { isEmpty } from '../empty.util'; /** * Component to switch between list and grid views. @@ -12,6 +15,8 @@ import { ViewMode } from '../../core/shared/view-mode.model'; templateUrl: './view-mode-switch.component.html' }) export class ViewModeSwitchComponent implements OnInit, OnDestroy { + @Input() viewModeList: ViewMode[]; + currentMode: ViewMode = ViewMode.List; viewModeEnum = ViewMode; private sub: Subscription; @@ -20,6 +25,10 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy { } ngOnInit(): void { + if (isEmpty(this.viewModeList)) { + this.viewModeList = [ViewMode.List, ViewMode.Grid]; + } + this.sub = this.searchService.getViewMode().subscribe((viewMode: ViewMode) => { this.currentMode = viewMode; }); @@ -34,4 +43,8 @@ export class ViewModeSwitchComponent implements OnInit, OnDestroy { this.sub.unsubscribe(); } } + + isToShow(viewMode: ViewMode) { + return this.viewModeList && this.viewModeList.includes(viewMode); + } } From a9397f0b11642e93627391221a11f956839cc33b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 15 Mar 2019 10:14:42 +0100 Subject: [PATCH 097/205] 61133: Post-merge refactoring --- .../journal-issue.component.html | 2 +- .../journal-volume.component.html | 2 +- .../item-types/journal/journal.component.html | 2 +- .../item-types/orgunit/orgunit.component.html | 2 +- .../item-types/person/person.component.html | 2 +- .../item-types/project/project.component.html | 2 +- src/app/core/shared/dspace-object.model.ts | 10 +++---- src/app/core/shared/metadata.models.ts | 27 ++++++------------- .../journal-issue-list-element.component.html | 10 +++---- ...journal-volume-list-element.component.html | 10 +++---- .../journal-list-element.component.html | 2 +- .../orgunit-list-element.component.html | 6 ++--- .../person/person-list-element.component.html | 6 ++--- ...erson-metadata-list-element.component.html | 6 ++--- .../project-list-element.component.html | 6 ++--- .../publication-list-element.component.html | 18 ++++++------- 16 files changed, 51 insertions(+), 62 deletions(-) diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html index e17a902297..0b7fa2d57b 100644 --- a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html index a956e40fdd..c4ef7102bd 100644 --- a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.html b/src/app/+item-page/simple/item-types/journal/journal.component.html index 157d6133bf..a872b79507 100644 --- a/src/app/+item-page/simple/item-types/journal/journal.component.html +++ b/src/app/+item-page/simple/item-types/journal/journal.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html index 7eacf66347..ba11fce201 100644 --- a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/+item-page/simple/item-types/person/person.component.html b/src/app/+item-page/simple/item-types/person/person.component.html index 30153353de..d98991a421 100644 --- a/src/app/+item-page/simple/item-types/person/person.component.html +++ b/src/app/+item-page/simple/item-types/person/person.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/+item-page/simple/item-types/project/project.component.html b/src/app/+item-page/simple/item-types/project/project.component.html index 17aa8b2065..5cba278252 100644 --- a/src/app/+item-page/simple/item-types/project/project.component.html +++ b/src/app/+item-page/simple/item-types/project/project.component.html @@ -1,5 +1,5 @@

- +

diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index 085988d745..1c83d298e2 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -119,21 +119,21 @@ export class DSpaceObject implements CacheableObject, ListableObject { } /** - * Find metadata on a specific field and order all of them using their "place" property. + * Find metadata on a specific field and order all of them using their "order" property. * @param key */ findMetadataSortedByPlace(key: string): MetadataValue[] { return this.allMetadata([key]).sort((a: MetadataValue, b: MetadataValue) => { - if (hasNoValue(a.place) && hasNoValue(b.place)) { + if (hasNoValue(a.order) && hasNoValue(b.order)) { return 0; } - if (hasNoValue(a.place)) { + if (hasNoValue(a.order)) { return -1; } - if (hasNoValue(b.place)) { + if (hasNoValue(b.order)) { return 1; } - return a.place - b.place; + return a.order - b.order; }); } diff --git a/src/app/core/shared/metadata.models.ts b/src/app/core/shared/metadata.models.ts index a83277b882..0df139a5cd 100644 --- a/src/app/core/shared/metadata.models.ts +++ b/src/app/core/shared/metadata.models.ts @@ -25,21 +25,17 @@ export class MetadataValue { value: string; /** - * The place of this Metadatum within his list of metadata + * The place of this MetadataValue within his list of metadata * This is used to render metadata in a specific custom order */ @autoserialize - place: number; + order: number; - /** - * The authority key used for authority-controlled metadata - */ + /** The authority key used for authority-controlled metadata */ @autoserialize authority: string; - /** - * The authority confidence value - */ + /** The authority confidence value */ @autoserialize confidence: number; @@ -91,23 +87,16 @@ export class MetadatumViewModel { /** The string value. */ value: string; - /** The order. */ - order: number; - /** - * The place of this Metadatum within his list of metadata + * The place of this MetadataValue within his list of metadata * This is used to render metadata in a specific custom order */ - place: number; + order: number; - /** - * The authority key used for authority-controlled metadata - */ + /** The authority key used for authority-controlled metadata */ authority: string; - /** - * The authority confidence value - */ + /** The authority confidence value */ confidence: number; } diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html index 3f73460f04..65a10ec1b7 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.html @@ -1,17 +1,17 @@ + [innerHTML]="firstMetadataValue('dc.title')"> - - + - - + - diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html index ec7acf1087..7d7f0cf731 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.html @@ -1,18 +1,18 @@ + [innerHTML]="firstMetadataValue('dc.title')"> - - + - - + () diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html index fb5284d398..c254d74f57 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html @@ -1,7 +1,7 @@ + [innerHTML]="firstMetadataValue('dc.title')"> + [innerHTML]="firstMetadataValue('orgunit.identifier.name')"> - + [innerHTML]="firstMetadataValue('orgunit.identifier.description')"> diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html index 2d89fda483..52b69453ce 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.html @@ -1,12 +1,12 @@ + [innerHTML]="firstMetadataValue('dc.contributor.author')"> - - + diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html index 92e57d7ef4..3dfe17debc 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-metadata-list-element.component.html @@ -1,8 +1,8 @@ - - + @@ -10,6 +10,6 @@ diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html index e1d7814f40..6f0faa90ef 100644 --- a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.html @@ -1,12 +1,12 @@ + [innerHTML]="firstMetadataValue('project.identifier.name')"> - - + diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html index 2d737d6355..3062e6110f 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html @@ -1,24 +1,24 @@ + [innerHTML]="firstMetadataValue('dc.title')"> - (, ) - (, ) + - + -
+
+ [innerHTML]="firstMetadataValue('dc.description.abstract')">
From e6d401d1952d05befdfb045a3d3f8ea168eaf8b0 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 15 Mar 2019 10:50:34 +0100 Subject: [PATCH 098/205] 61133: Post-merge test fixes --- .../metadata-values.component.spec.ts | 9 +-- .../search-fixed-filter.service.spec.ts | 13 +--- ...item-metadata-representation.model.spec.ts | 13 ++-- .../metadatum-representation.model.spec.ts | 6 +- src/app/core/shared/metadatum.model.spec.ts | 67 ------------------- 5 files changed, 15 insertions(+), 93 deletions(-) delete mode 100644 src/app/core/shared/metadatum.model.spec.ts diff --git a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts index 9682386b96..cad2edb98a 100644 --- a/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-values/metadata-values.component.spec.ts @@ -4,27 +4,24 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader'; import { MetadataValuesComponent } from './metadata-values.component'; import { By } from '@angular/platform-browser'; -import { Metadatum } from '../../../core/shared/metadatum.model'; +import { MetadataValue } from '../../../core/shared/metadata.models'; let comp: MetadataValuesComponent; let fixture: ComponentFixture; const mockMetadata = [ { - key: 'journal.identifier.issn', language: 'en_US', value: '1234' }, { - key: 'journal.publisher', language: 'en_US', value: 'a publisher' }, { - key: 'journal.identifier.description', language: 'en_US', value: 'desc' - }] as Metadatum[]; + }] as MetadataValue[]; const mockSeperator = '
'; const mockLabel = 'fake.message'; @@ -47,7 +44,7 @@ describe('MetadataValuesComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(MetadataValuesComponent); comp = fixture.componentInstance; - comp.values = mockMetadata; + comp.mdValues = mockMetadata; comp.separator = mockSeperator; comp.label = mockLabel; fixture.detectChanges(); diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts index 06e12d444e..119fca594b 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts @@ -1,11 +1,7 @@ import { SearchFixedFilterService } from './search-fixed-filter.service'; -import { ResponseCacheEntry } from '../../../core/cache/response-cache.reducer'; import { RouteService } from '../../../shared/services/route.service'; import { RequestService } from '../../../core/data/request.service'; -import { ResponseCacheService } from '../../../core/cache/response-cache.service'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; -import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response-cache.models'; -import { PageInfo } from '../../../core/shared/page-info.model'; import { of as observableOf } from 'rxjs'; describe('SearchFixedFilterService', () => { @@ -20,17 +16,12 @@ describe('SearchFixedFilterService', () => { /* tslint:enable:no-empty */ generateRequestId: () => 'fake-id' }) as RequestService; - const responseCacheStub = Object.assign(new ResponseCacheService(undefined), { - get: () => observableOf(Object.assign(new ResponseCacheEntry(), { - response: new FilteredDiscoveryQueryResponse(filterQuery, '200', new PageInfo()) - })) - }); - const halServiceStub = Object.assign(new HALEndpointService(responseCacheStub, requestServiceStub, undefined), { + const halServiceStub = Object.assign(new HALEndpointService(requestServiceStub, undefined), { getEndpoint: () => observableOf('fake-url') }); beforeEach(() => { - service = new SearchFixedFilterService(routeServiceStub, requestServiceStub, responseCacheStub, halServiceStub); + service = new SearchFixedFilterService(routeServiceStub, requestServiceStub, halServiceStub); }); describe('when getQueryByFilterName is called with a filterName', () => { diff --git a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts index cd153153de..d8f0e1c612 100644 --- a/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts +++ b/src/app/core/shared/metadata-representation/item/item-metadata-representation.model.spec.ts @@ -1,18 +1,19 @@ import { MetadataRepresentationType } from '../metadata-representation.model'; import { ItemMetadataRepresentation, ItemTypeToValue } from './item-metadata-representation.model'; import { Item } from '../../item.model'; -import { Metadatum } from '../../metadatum.model'; +import { MetadataMap, MetadataValue } from '../../metadata.models'; describe('ItemMetadataRepresentation', () => { const valuePrefix = 'Test value for '; const item = new Item(); let itemMetadataRepresentation: ItemMetadataRepresentation; - item.metadata = Object.keys(ItemTypeToValue).map((key: string) => { - return Object.assign(new Metadatum(), { - key: ItemTypeToValue[key], + const metadataMap = new MetadataMap(); + for (const key of Object.keys(ItemTypeToValue)) { + metadataMap[ItemTypeToValue[key]] = [Object.assign(new MetadataValue(), { value: `${valuePrefix}${ItemTypeToValue[key]}` - }); - }); + })]; + } + item.metadata = metadataMap; for (const itemType of Object.keys(ItemTypeToValue)) { describe(`when creating an ItemMetadataRepresentation with item-type "${itemType}"`, () => { diff --git a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts index c55ff7f9f3..ea48d345c5 100644 --- a/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts +++ b/src/app/core/shared/metadata-representation/metadatum/metadatum-representation.model.spec.ts @@ -1,14 +1,14 @@ -import { Metadatum } from '../../metadatum.model'; import { MetadatumRepresentation } from './metadatum-representation.model'; import { MetadataRepresentationType } from '../metadata-representation.model'; +import { MetadataValue } from '../../metadata.models'; describe('MetadatumRepresentation', () => { const itemType = 'Person'; - const normalMetadatum = Object.assign(new Metadatum(), { + const normalMetadatum = Object.assign(new MetadataValue(), { key: 'dc.contributor.author', value: 'Test Author' }); - const authorityMetadatum = Object.assign(new Metadatum(), { + const authorityMetadatum = Object.assign(new MetadataValue(), { key: 'dc.contributor.author', value: 'Test Authority Author', authority: '1234' diff --git a/src/app/core/shared/metadatum.model.spec.ts b/src/app/core/shared/metadatum.model.spec.ts deleted file mode 100644 index 0b552eed56..0000000000 --- a/src/app/core/shared/metadatum.model.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Metadatum } from './metadatum.model'; - -describe('Metadatum', () => { - let metadatum: Metadatum ; - - beforeEach(() => { - metadatum = new Metadatum(); - }); - - describe('isVirtual', () => { - describe('when the metadatum has no authority key', () => { - beforeEach(() => { - metadatum.authority = undefined; - }); - - it('should return false', () => { - expect(metadatum.isVirtual).toBe(false); - }); - }); - - describe('when the metadatum has an authority key', () => { - describe('but it doesn\'t start with the virtual prefix', () => { - beforeEach(() => { - metadatum.authority = 'value'; - }); - - it('should return false', () => { - expect(metadatum.isVirtual).toBe(false); - }); - }); - - describe('and it starts with the virtual prefix', () => { - beforeEach(() => { - metadatum.authority = 'virtual::value'; - }); - - it('should return true', () => { - expect(metadatum.isVirtual).toBe(true); - }); - }); - - }); - - }); - - describe('virtualValue', () => { - describe('when the metadatum isn\'t virtual', () => { - beforeEach(() => { - metadatum.authority = 'value'; - }); - - it('should return undefined', () => { - expect(metadatum.virtualValue).toBeUndefined(); - }); - }); - - describe('when the metadatum is virtual', () => { - beforeEach(() => { - metadatum.authority = 'virtual::value'; - }); - - it('should return everything in the authority key after virtual::', () => { - expect(metadatum.virtualValue).toBe('value'); - }); - }); - }); -}); From 4da66b5f356c71e5d2db774345895e09cf6381b6 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 15 Mar 2019 12:41:43 +0100 Subject: [PATCH 099/205] 61133: Post-merge refactoring + order renamed to place --- src/app/+collection-page/collection-page.component.ts | 4 ++-- src/app/core/shared/dspace-object.model.ts | 10 +++++----- src/app/core/shared/metadata.models.ts | 4 ++-- .../journal/journal-list-element.component.html | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/app/+collection-page/collection-page.component.ts b/src/app/+collection-page/collection-page.component.ts index c1ef2522d1..5b158f4a3c 100644 --- a/src/app/+collection-page/collection-page.component.ts +++ b/src/app/+collection-page/collection-page.component.ts @@ -51,7 +51,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy { this.paginationConfig.id = 'collection-page-pagination'; this.paginationConfig.pageSize = 5; this.paginationConfig.currentPage = 1; - this.sortConfig = new SortOptions('dc.date.accessioned', SortDirection.DESC); + this.sortConfig = new SortOptions('dc.date.issued', SortDirection.DESC); } ngOnInit(): void { @@ -76,7 +76,7 @@ export class CollectionPageComponent implements OnInit, OnDestroy { ); const sort = Object.assign({}, this.sortConfig, - { direction: sortDirection, field: params.sortField } + { direction: sortDirection, field: this.sortConfig.field } ); this.collectionRD$.subscribe((rd: RemoteData) => { this.collectionId = rd.payload.id; diff --git a/src/app/core/shared/dspace-object.model.ts b/src/app/core/shared/dspace-object.model.ts index 1c83d298e2..085988d745 100644 --- a/src/app/core/shared/dspace-object.model.ts +++ b/src/app/core/shared/dspace-object.model.ts @@ -119,21 +119,21 @@ export class DSpaceObject implements CacheableObject, ListableObject { } /** - * Find metadata on a specific field and order all of them using their "order" property. + * Find metadata on a specific field and order all of them using their "place" property. * @param key */ findMetadataSortedByPlace(key: string): MetadataValue[] { return this.allMetadata([key]).sort((a: MetadataValue, b: MetadataValue) => { - if (hasNoValue(a.order) && hasNoValue(b.order)) { + if (hasNoValue(a.place) && hasNoValue(b.place)) { return 0; } - if (hasNoValue(a.order)) { + if (hasNoValue(a.place)) { return -1; } - if (hasNoValue(b.order)) { + if (hasNoValue(b.place)) { return 1; } - return a.order - b.order; + return a.place - b.place; }); } diff --git a/src/app/core/shared/metadata.models.ts b/src/app/core/shared/metadata.models.ts index 0df139a5cd..c843b0ec74 100644 --- a/src/app/core/shared/metadata.models.ts +++ b/src/app/core/shared/metadata.models.ts @@ -29,7 +29,7 @@ export class MetadataValue { * This is used to render metadata in a specific custom order */ @autoserialize - order: number; + place: number; /** The authority key used for authority-controlled metadata */ @autoserialize @@ -91,7 +91,7 @@ export class MetadatumViewModel { * The place of this MetadataValue within his list of metadata * This is used to render metadata in a specific custom order */ - order: number; + place: number; /** The authority key used for authority-controlled metadata */ authority: string; diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html index c254d74f57..32c8074503 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.html @@ -4,9 +4,9 @@ [innerHTML]="firstMetadataValue('dc.title')"> - - + From 5d091e69255ee74dccc2be0bb92f627507a03ec2 Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 15 Mar 2019 13:57:19 +0100 Subject: [PATCH 100/205] 61133: Post-merge test fixing and refactoring --- .../metadata-uri-values.component.spec.ts | 8 +-- .../full/full-item-page.component.spec.ts | 16 +++-- .../item-page-field.component.spec.ts | 15 ++-- .../search-fixed-filter.service.spec.ts | 7 +- .../search-fixed-filter.service.ts | 7 +- src/app/core/shared/metadata.utils.spec.ts | 2 +- .../item-type-switcher.component.spec.ts | 25 ++++--- ...urnal-issue-list-element.component.spec.ts | 50 +++++++------ ...rnal-volume-list-element.component.spec.ts | 50 +++++++------ .../journal-list-element.component.spec.ts | 39 +++++----- .../orgunit-list-element.component.spec.ts | 39 +++++----- .../person-list-element.component.spec.ts | 39 +++++----- .../project-list-element.component.spec.ts | 39 +++++----- .../publication-list-element.component.html | 6 +- ...publication-list-element.component.spec.ts | 72 ++++++++++--------- 15 files changed, 233 insertions(+), 181 deletions(-) diff --git a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts index d12a73a885..2b32ece3c3 100644 --- a/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts +++ b/src/app/+item-page/field-components/metadata-uri-values/metadata-uri-values.component.spec.ts @@ -5,21 +5,21 @@ import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader import { By } from '@angular/platform-browser'; import { MetadataUriValuesComponent } from './metadata-uri-values.component'; import { isNotEmpty } from '../../../shared/empty.util'; +import { MetadataValue } from '../../../core/shared/metadata.models'; let comp: MetadataUriValuesComponent; let fixture: ComponentFixture; const mockMetadata = [ { - key: 'dc.identifier.uri', language: 'en_US', value: 'http://fakelink.org' }, { - key: 'dc.identifier.uri', language: 'en_US', value: 'http://another.fakelink.org' - }]; + } +] as MetadataValue[]; const mockSeperator = '
'; const mockLabel = 'fake.message'; const mockLinkText = 'fake link text'; @@ -43,7 +43,7 @@ describe('MetadataUriValuesComponent', () => { beforeEach(async(() => { fixture = TestBed.createComponent(MetadataUriValuesComponent); comp = fixture.componentInstance; - comp.values = mockMetadata; + comp.mdValues = mockMetadata; comp.separator = mockSeperator; comp.label = mockLabel; fixture.detectChanges(); diff --git a/src/app/+item-page/full/full-item-page.component.spec.ts b/src/app/+item-page/full/full-item-page.component.spec.ts index 3377dec6db..15dd001964 100644 --- a/src/app/+item-page/full/full-item-page.component.spec.ts +++ b/src/app/+item-page/full/full-item-page.component.spec.ts @@ -20,12 +20,14 @@ import { By } from '@angular/platform-browser'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'test item' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'test item' + } + ] + } }); const routeStub = Object.assign(new ActivatedRouteStub(), { data: observableOf({ item: new RemoteData(false, false, true, null, mockItem) }) @@ -69,7 +71,7 @@ describe('FullItemPageComponent', () => { it('should display the item\'s metadata', () => { const table = fixture.debugElement.query(By.css('table')); - for (const metadatum of mockItem.metadata) { + for (const metadatum of mockItem.allMetadata([])) { expect(table.nativeElement.innerHTML).toContain(metadatum.value); } }) diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts index a0d24387cf..b1b6d3095e 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts @@ -10,6 +10,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { ItemPageFieldComponent } from './item-page-field.component'; import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component'; import { of as observableOf } from 'rxjs'; +import { MetadataMap } from '../../../../core/shared/metadata.models'; let comp: ItemPageFieldComponent; let fixture: ComponentFixture; @@ -50,13 +51,13 @@ describe('ItemPageFieldComponent', () => { }); export function mockItemWithMetadataFieldAndValue(field: string, value: string): Item { - return Object.assign(new Item(), { + const item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: field, - language: 'en_US', - value: value - }] + metadata: new MetadataMap() }); + item.metadata[field] = [{ + language: 'en_US', + value: value + }]; + return item; } diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts index 119fca594b..2957b32c7f 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.spec.ts @@ -3,6 +3,8 @@ import { RouteService } from '../../../shared/services/route.service'; import { RequestService } from '../../../core/data/request.service'; import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { of as observableOf } from 'rxjs'; +import { RequestEntry } from '../../../core/data/request.reducer'; +import { FilteredDiscoveryQueryResponse, RestResponse } from '../../../core/cache/response.models'; describe('SearchFixedFilterService', () => { let service: SearchFixedFilterService; @@ -14,7 +16,10 @@ describe('SearchFixedFilterService', () => { /* tslint:disable:no-empty */ configure: () => {}, /* tslint:enable:no-empty */ - generateRequestId: () => 'fake-id' + generateRequestId: () => 'fake-id', + getByUUID: () => observableOf(Object.assign(new RequestEntry(), { + response: new FilteredDiscoveryQueryResponse(filterQuery, '200') + })) }) as RequestService; const halServiceStub = Object.assign(new HALEndpointService(requestServiceStub, undefined), { getEndpoint: () => observableOf('fake-url') diff --git a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts index 24688b798c..7d59e5a446 100644 --- a/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-fixed-filter.service.ts @@ -8,7 +8,7 @@ import { ResponseParsingService } from '../../../core/data/parsing.service'; import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { FilteredDiscoveryPageResponseParsingService } from '../../../core/data/filtered-discovery-page-response-parsing.service'; import { hasValue } from '../../../shared/empty.util'; -import { configureRequest } from '../../../core/shared/operators'; +import { configureRequest, getResponseFromEntry } from '../../../core/shared/operators'; import { RouteService } from '../../../shared/services/route.service'; import { FilteredDiscoveryQueryResponse } from '../../../core/cache/response.models'; @@ -33,7 +33,7 @@ export class SearchFixedFilterService { getQueryByFilterName(filterName: string): Observable { if (hasValue(filterName)) { const requestUuid = this.requestService.generateRequestId(); - const requestObs = this.halService.getEndpoint(this.queryByFilterPath).pipe( + this.halService.getEndpoint(this.queryByFilterPath).pipe( map((url: string) => { url += ('/' + filterName); const request = new GetRequest(requestUuid, url); @@ -44,10 +44,11 @@ export class SearchFixedFilterService { }); }), configureRequest(this.requestService) - ); + ).subscribe(); // get search results from response cache const filterQuery: Observable = this.requestService.getByUUID(requestUuid).pipe( + getResponseFromEntry(), map((response: FilteredDiscoveryQueryResponse) => response.filterQuery )); diff --git a/src/app/core/shared/metadata.utils.spec.ts b/src/app/core/shared/metadata.utils.spec.ts index 7fbea14b13..1e1d7f86d5 100644 --- a/src/app/core/shared/metadata.utils.spec.ts +++ b/src/app/core/shared/metadata.utils.spec.ts @@ -9,7 +9,7 @@ import { import { Metadata } from './metadata.utils'; const mdValue = (value: string, language?: string): MetadataValue => { - return { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language }; + return Object.assign(new MetadataValue(), { uuid: uuidv4(), value: value, language: isUndefined(language) ? null : language, place: 0, authority: undefined, confidence: undefined }); }; const dcDescription = mdValue('Some description'); diff --git a/src/app/shared/items/switcher/item-type-switcher.component.spec.ts b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts index cb657b1625..bc6950a446 100644 --- a/src/app/shared/items/switcher/item-type-switcher.component.spec.ts +++ b/src/app/shared/items/switcher/item-type-switcher.component.spec.ts @@ -16,17 +16,20 @@ import { VIEW_MODE_METADATA } from '../../../+item-page/simple/metadata-represen const relationType = 'type'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'test item' - }, - { - key: 'relationship.type', - language: 'en_US', - value: relationType - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'test item' + } + ], + 'relationship.type': [ + { + language: 'en_US', + value: relationType + } + ] + } }); const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(relationType), mockItem); let viewMode = VIEW_MODE_FULL; diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts index 4f829a6818..05de6c814b 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-issue/journal-issue-list-element.component.spec.ts @@ -13,31 +13,37 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'journalvolume.identifier.volume', - language: 'en_US', - value: '1234' - }, - { - key: 'journalissue.identifier.number', - language: 'en_US', - value: '5678' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'journalvolume.identifier.volume': [ + { + language: 'en_US', + value: '1234' + } + ], + 'journalissue.identifier.number': [ + { + language: 'en_US', + value: '5678' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('JournalIssueListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts index 93071289b8..4cdfb0d732 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal-volume/journal-volume-list-element.component.spec.ts @@ -13,31 +13,37 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'journal.title', - language: 'en_US', - value: 'This is just another journal title' - }, - { - key: 'journalvolume.identifier.volume', - language: 'en_US', - value: '1234' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'journal.title': [ + { + language: 'en_US', + value: 'This is just another journal title' + } + ], + 'journalvolume.identifier.volume': [ + { + language: 'en_US', + value: '1234' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('JournalVolumeListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts index abdeb9c00f..fc7ef06fa0 100644 --- a/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/journal/journal-list-element.component.spec.ts @@ -13,26 +13,31 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'journal.identifier.issn', - language: 'en_US', - value: '1234' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'journal.identifier.issn': [ + { + language: 'en_US', + value: '1234' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('JournalListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts index 14b86d720a..8e74c389e9 100644 --- a/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/orgunit/orgunit-list-element.component.spec.ts @@ -13,26 +13,31 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'orgunit.identifier.description', - language: 'en_US', - value: 'A description about the OrgUnit' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'orgunit.identifier.description': [ + { + language: 'en_US', + value: 'A description about the OrgUnit' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('OrgUnitListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts index 836588c565..67dc4e92ac 100644 --- a/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/person/person-list-element.component.spec.ts @@ -13,26 +13,31 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'person.identifier.jobtitle', - language: 'en_US', - value: 'Developer' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'person.identifier.jobtitle': [ + { + language: 'en_US', + value: 'Developer' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('PersonListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts index cd0cce95f2..1dd3c42042 100644 --- a/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/project/project-list-element.component.spec.ts @@ -13,26 +13,31 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'project.identifier.status', - language: 'en_US', - value: 'A status about the project' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'project.identifier.status': [ + { + language: 'en_US', + value: 'A status about the project' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('ProjectListElementComponent', () => { diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html index 3062e6110f..aff19aec1d 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.html @@ -4,9 +4,9 @@ [innerHTML]="firstMetadataValue('dc.title')"> - ((, ) @@ -16,7 +16,7 @@ -
+
diff --git a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts index 6f596d1938..732fd0d4e4 100644 --- a/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/publication/publication-list-element.component.spec.ts @@ -13,41 +13,49 @@ let fixture: ComponentFixture; const mockItemWithMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }, - { - key: 'dc.contributor.author', - language: 'en_US', - value: 'Smith, Donald' - }, - { - key: 'dc.publisher', - language: 'en_US', - value: 'a publisher' - }, - { - key: 'dc.date.issued', - language: null, - value: '2015-06-26' - }, - { - key: 'dc.description.abstract', - language: 'en_US', - value: 'This is the abstract' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'dc.date.issued': [ + { + language: 'en_US', + value: '2015-06-26' + } + ], + 'dc.description.abstract': [ + { + language: 'en_US', + value: 'This is the abstract' + } + ] + } }); const mockItemWithoutMetadata: Item = Object.assign(new Item(), { bitstreams: observableOf({}), - metadata: [ - { - key: 'dc.title', - language: 'en_US', - value: 'This is just another title' - }] + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ] + } }); describe('PublicationListElementComponent', () => { From ef1616818b0cb8bb3e456225454909d179c37a8e Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Fri, 15 Mar 2019 14:50:43 +0100 Subject: [PATCH 101/205] 61133: Post-merge test fixes --- .../item-page-field.component.spec.ts | 4 +- .../journal-issue.component.spec.ts | 47 ++++++----- .../journal-volume.component.spec.ts | 36 +++++---- .../journal/journal.component.spec.ts | 42 +++++----- .../orgunit/orgunit.component.spec.ts | 58 ++++++++------ .../person/person.component.spec.ts | 80 ++++++++++--------- .../project/project.component.spec.ts | 58 ++++++++------ .../publication/publication.component.spec.ts | 3 +- .../item-types/shared/item.component.spec.ts | 70 ++++++++-------- .../search-service/search.service.spec.ts | 22 ++--- ...very-page-response-parsing.service.spec.ts | 2 +- ...arch-result-list-element.component.spec.ts | 3 +- ...arch-result-list-element.component.spec.ts | 7 +- 13 files changed, 234 insertions(+), 198 deletions(-) diff --git a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts index b1b6d3095e..ea6e722c66 100644 --- a/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts +++ b/src/app/+item-page/simple/field-components/specific-field/item-page-field.component.spec.ts @@ -10,7 +10,7 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { ItemPageFieldComponent } from './item-page-field.component'; import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component'; import { of as observableOf } from 'rxjs'; -import { MetadataMap } from '../../../../core/shared/metadata.models'; +import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models'; let comp: ItemPageFieldComponent; let fixture: ComponentFixture; @@ -58,6 +58,6 @@ export function mockItemWithMetadataFieldAndValue(field: string, value: string): item.metadata[field] = [{ language: 'en_US', value: value - }]; + }] as MetadataValue[]; return item; } diff --git a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts index a648250b03..24b18af96e 100644 --- a/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal-issue/journal-issue.component.spec.ts @@ -8,27 +8,32 @@ import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'journalissue.identifier.number', - language: 'en_US', - value: '1234' - }, - { - key: 'journalissue.issuedate', - language: 'en_US', - value: '2018' - }, - { - key: 'journalissue.identifier.description', - language: 'en_US', - value: 'desc' - }, - { - key: 'journalissue.identifier.keyword', - language: 'en_US', - value: 'keyword' - }], + metadata: { + 'journalissue.identifier.number': [ + { + language: 'en_US', + value: '1234' + } + ], + 'journalissue.issuedate': [ + { + language: 'en_US', + value: '2018' + } + ], + 'journalissue.identifier.description': [ + { + language: 'en_US', + value: 'desc' + } + ], + 'journalissue.identifier.keyword': [ + { + language: 'en_US', + value: 'keyword' + } + ] + }, relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts index 5442048a50..a6f32e9b5f 100644 --- a/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal-volume/journal-volume.component.spec.ts @@ -8,22 +8,26 @@ import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'journalvolume.identifier.volume', - language: 'en_US', - value: '1234' - }, - { - key: 'journalvolume.issuedate', - language: 'en_US', - value: '2018' - }, - { - key: 'journalvolume.identifier.description', - language: 'en_US', - value: 'desc' - }], + metadata: { + 'journalvolume.identifier.volume': [ + { + language: 'en_US', + value: '1234' + } + ], + 'journalvolume.issuedate': [ + { + language: 'en_US', + value: '2018' + } + ], + 'journalvolume.identifier.description': [ + { + language: 'en_US', + value: 'desc' + } + ] + }, relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts index 8a18f0f727..08e8859b35 100644 --- a/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts +++ b/src/app/+item-page/simple/item-types/journal/journal.component.spec.ts @@ -21,22 +21,26 @@ let fixture: ComponentFixture; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'journal.identifier.issn', - language: 'en_US', - value: '1234' - }, - { - key: 'journal.publisher', - language: 'en_US', - value: 'a publisher' - }, - { - key: 'journal.identifier.description', - language: 'en_US', - value: 'desc' - }] + metadata: { + 'journal.identifier.issn': [ + { + language: 'en_US', + value: '1234' + } + ], + 'journal.publisher': [ + { + language: 'en_US', + value: 'a publisher' + } + ], + 'journal.identifier.description': [ + { + language: 'en_US', + value: 'desc' + } + ] + } }); describe('JournalComponent', () => { @@ -67,10 +71,10 @@ describe('JournalComponent', () => { fixture.detectChanges(); })); - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { + for (const key of Object.keys(mockItem.metadata)) { + it(`should be calling a component with metadata field ${key}`, () => { const fields = fixture.debugElement.queryAll(By.css('.item-page-fields')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + expect(containsFieldInput(fields, key)).toBeTruthy(); }); } }); diff --git a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts index bb356ba7fb..fa5396fb3d 100644 --- a/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts +++ b/src/app/+item-page/simple/item-types/orgunit/orgunit.component.spec.ts @@ -8,32 +8,38 @@ import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'orgunit.identifier.dateestablished', - language: 'en_US', - value: '2018' - }, - { - key: 'orgunit.identifier.city', - language: 'en_US', - value: 'New York' - }, - { - key: 'orgunit.identifier.country', - language: 'en_US', - value: 'USA' - }, - { - key: 'orgunit.identifier.id', - language: 'en_US', - value: '1' - }, - { - key: 'orgunit.identifier.description', - language: 'en_US', - value: 'desc' - }], + metadata: { + 'orgunit.identifier.dateestablished': [ + { + language: 'en_US', + value: '2018' + } + ], + 'orgunit.identifier.city': [ + { + language: 'en_US', + value: 'New York' + } + ], + 'orgunit.identifier.country': [ + { + language: 'en_US', + value: 'USA' + } + ], + 'orgunit.identifier.id': [ + { + language: 'en_US', + value: '1' + } + ], + 'orgunit.identifier.description': [ + { + language: 'en_US', + value: 'desc' + } + ] + }, relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/person/person.component.spec.ts b/src/app/+item-page/simple/item-types/person/person.component.spec.ts index 4c582f67e8..cf0d5c197d 100644 --- a/src/app/+item-page/simple/item-types/person/person.component.spec.ts +++ b/src/app/+item-page/simple/item-types/person/person.component.spec.ts @@ -8,42 +8,50 @@ import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'person.identifier.email', - language: 'en_US', - value: 'fake@email.com' - }, - { - key: 'person.identifier.orcid', - language: 'en_US', - value: 'ORCID-1' - }, - { - key: 'person.identifier.birthdate', - language: 'en_US', - value: '1993' - }, - { - key: 'person.identifier.staffid', - language: 'en_US', - value: '1' - }, - { - key: 'person.identifier.jobtitle', - language: 'en_US', - value: 'Developer' - }, - { - key: 'person.identifier.lastname', - language: 'en_US', - value: 'Doe' - }, - { - key: 'person.identifier.firstname', - language: 'en_US', - value: 'John' - }], + metadata: { + 'person.identifier.email': [ + { + language: 'en_US', + value: 'fake@email.com' + } + ], + 'person.identifier.orcid': [ + { + language: 'en_US', + value: 'ORCID-1' + } + ], + 'person.identifier.birthdate': [ + { + language: 'en_US', + value: '1993' + } + ], + 'person.identifier.staffid': [ + { + language: 'en_US', + value: '1' + } + ], + 'person.identifier.jobtitle': [ + { + language: 'en_US', + value: 'Developer' + } + ], + 'person.identifier.lastname': [ + { + language: 'en_US', + value: 'Doe' + } + ], + 'person.identifier.firstname': [ + { + language: 'en_US', + value: 'John' + } + ] + }, relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/project/project.component.spec.ts b/src/app/+item-page/simple/item-types/project/project.component.spec.ts index e28c97f87d..9b54ff9a41 100644 --- a/src/app/+item-page/simple/item-types/project/project.component.spec.ts +++ b/src/app/+item-page/simple/item-types/project/project.component.spec.ts @@ -8,32 +8,38 @@ import { of as observableOf } from 'rxjs'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [ - { - key: 'project.identifier.status', - language: 'en_US', - value: 'published' - }, - { - key: 'project.identifier.id', - language: 'en_US', - value: '1' - }, - { - key: 'project.identifier.expectedcompletion', - language: 'en_US', - value: 'exp comp' - }, - { - key: 'project.identifier.description', - language: 'en_US', - value: 'keyword' - }, - { - key: 'project.identifier.keyword', - language: 'en_US', - value: 'keyword' - }], + metadata: { + 'project.identifier.status': [ + { + language: 'en_US', + value: 'published' + } + ], + 'project.identifier.id': [ + { + language: 'en_US', + value: '1' + } + ], + 'project.identifier.expectedcompletion': [ + { + language: 'en_US', + value: 'exp comp' + } + ], + 'project.identifier.description': [ + { + language: 'en_US', + value: 'keyword' + } + ], + 'project.identifier.keyword': [ + { + language: 'en_US', + value: 'keyword' + } + ] + }, relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts index 603d358761..48a7a05f45 100644 --- a/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts +++ b/src/app/+item-page/simple/item-types/publication/publication.component.spec.ts @@ -16,10 +16,11 @@ import { By } from '@angular/platform-browser'; import { createRelationshipsObservable } from '../shared/item.component.spec'; import { PublicationComponent } from './publication.component'; import { of as observableOf } from 'rxjs'; +import { MetadataMap } from '../../../../core/shared/metadata.models'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [], + metadata: new MetadataMap(), relationships: createRelationshipsObservable() }); diff --git a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts index 28bd8e33e3..4b34cb442f 100644 --- a/src/app/+item-page/simple/item-types/shared/item.component.spec.ts +++ b/src/app/+item-page/simple/item-types/shared/item.component.spec.ts @@ -28,6 +28,7 @@ import { Observable } from 'rxjs/internal/Observable'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model'; import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; +import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models'; /** * Create a generic test for an item-page-fields component using a mockItem and the type of component @@ -75,10 +76,10 @@ export function getItemPageFieldsTest(mockItem: Item, component) { fixture.detectChanges(); })); - for (const metadata of mockItem.metadata) { - it(`should be calling a component with metadata field ${metadata.key}`, () => { + for (const key of Object.keys(mockItem.metadata)) { + it(`should be calling a component with metadata field ${key}`, () => { const fields = fixture.debugElement.queryAll(By.css('ds-generic-item-page-field')); - expect(containsFieldInput(fields, metadata.key)).toBeTruthy(); + expect(containsFieldInput(fields, key)).toBeTruthy(); }); } } @@ -324,30 +325,7 @@ describe('ItemComponent', () => { const mockItem = Object.assign(new Item(), { id: '1', uuid: '1', - metadata: [ - { - key: metadataField, - value: 'Second value', - place: 1 - }, - { - key: metadataField, - value: 'Third value', - place: 2, - authority: 'virtual::123' - }, - { - key: metadataField, - value: 'First value', - place: 0 - }, - { - key: metadataField, - value: 'Fourth value', - place: 3, - authority: '123' - } - ], + metadata: new MetadataMap(), relationships: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [ Object.assign(new Relationship(), { uuid: '123', @@ -358,22 +336,44 @@ describe('ItemComponent', () => { }) ]))) }); + mockItem.metadata[metadataField] = [ + { + value: 'Second value', + place: 1 + }, + { + value: 'Third value', + place: 2, + authority: 'virtual::123' + }, + { + value: 'First value', + place: 0 + }, + { + value: 'Fourth value', + place: 3, + authority: '123' + } + ] as MetadataValue[]; const relatedItem = Object.assign(new Item(), { id: '2', - metadata: [ - { - key: 'dc.title', - value: 'related item' - } - ] + metadata: Object.assign(new MetadataMap(), { + 'dc.title': [ + { + language: 'en_US', + value: 'related item' + } + ] + }) }); - const mockItemDataService = { + const mockItemDataService = Object.assign({ findById: (id) => { if (id === relatedItem.id) { return observableOf(new RemoteData(false, false, true, null, relatedItem)) } } - } as ItemDataService; + }) as ItemDataService; let representations: Observable; diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts index 20364b18ac..75708f5fd1 100644 --- a/src/app/+search-page/search-service/search.service.spec.ts +++ b/src/app/+search-page/search-service/search.service.spec.ts @@ -67,7 +67,7 @@ describe('SearchService', () => { it('should return list view mode', () => { searchService.getViewMode().subscribe((viewMode) => { - expect(viewMode).toBe(SetViewMode.List); + expect(viewMode).toBe(ViewMode.List); }); }); }); @@ -125,33 +125,33 @@ describe('SearchService', () => { }); it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => { - searchService.setViewMode(SetViewMode.List); + searchService.setViewMode(ViewMode.List); expect(router.navigate).toHaveBeenCalledWith(['/search'], { - queryParams: { view: SetViewMode.List }, + queryParams: { view: ViewMode.List }, queryParamsHandling: 'merge' }); }); it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => { - searchService.setViewMode(SetViewMode.Grid); + searchService.setViewMode(ViewMode.Grid); expect(router.navigate).toHaveBeenCalledWith(['/search'], { - queryParams: { view: SetViewMode.Grid }, + queryParams: { view: ViewMode.Grid }, queryParamsHandling: 'merge' }); }); it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => { - let viewMode = SetViewMode.Grid; - route.testParams = { view: SetViewMode.List }; + let viewMode = ViewMode.Grid; + route.testParams = { view: ViewMode.List }; searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toEqual(SetViewMode.List); + expect(viewMode).toEqual(ViewMode.List); }); it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => { - let viewMode = SetViewMode.List; - route.testParams = { view: SetViewMode.Grid }; + let viewMode = ViewMode.List; + route.testParams = { view: ViewMode.Grid }; searchService.getViewMode().subscribe((mode) => viewMode = mode); - expect(viewMode).toEqual(SetViewMode.Grid); + expect(viewMode).toEqual(ViewMode.Grid); }); describe('when search is called', () => { diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts index 13eaeb03d4..ce19d8d3ff 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts @@ -4,7 +4,7 @@ import { GenericConstructor } from '../shared/generic-constructor'; import { ResponseParsingService } from './parsing.service'; import { GetRequest } from './request.models'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; -import { FilteredDiscoveryQueryResponse } from '../cache/response-cache.models'; +import { FilteredDiscoveryQueryResponse } from '../cache/response.models'; describe('FilteredDiscoveryPageResponseParsingService', () => { let service: FilteredDiscoveryPageResponseParsingService; diff --git a/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts index e97f632033..49d6ead11a 100644 --- a/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component.spec.ts @@ -11,6 +11,7 @@ import { ITEM } from '../../../items/switcher/item-type-switcher.component'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; import { createRelationshipsObservable } from '../../../../+item-page/simple/item-types/shared/item.component.spec'; import { of as observableOf } from 'rxjs'; +import { MetadataMap } from '../../../../core/shared/metadata.models'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), @@ -19,7 +20,7 @@ const mockItem: Item = Object.assign(new Item(), { }); const mockSearchResult = { dspaceObject: mockItem as Item, - hitHighlights: [] + hitHighlights: new MetadataMap() } as ItemSearchResult; describe('ItemSearchResultComponent', () => { diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index 28fbbc419a..a6c0ac6fff 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -11,10 +11,11 @@ import { TruncatableService } from '../../../truncatable/truncatable.service'; import { TruncatePipe } from '../../../utils/truncate.pipe'; import { createRelationshipsObservable } from '../../../../+item-page/simple/item-types/shared/item.component.spec'; import { ItemSearchResult } from '../../../object-collection/shared/item-search-result.model'; +import { MetadataMap } from '../../../../core/shared/metadata.models'; const mockItem: Item = Object.assign(new Item(), { bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))), - metadata: [], + metadata: new MetadataMap(), relationships: createRelationshipsObservable() }); @@ -27,7 +28,7 @@ describe('ItemSearchResultListElementComponent', () => { }; const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult(); - mockItemWithAuthorAndDate.hitHighlights = []; + mockItemWithAuthorAndDate.hitHighlights = new MetadataMap(); mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: [ @@ -44,7 +45,7 @@ describe('ItemSearchResultListElementComponent', () => { }); const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult(); - mockItemWithoutAuthorAndDate.hitHighlights = []; + mockItemWithoutAuthorAndDate.hitHighlights = new MetadataMap(); mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: [ From b871148cc9afba8f37b13e928a35b603377ad8de Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 26 Mar 2019 10:04:05 +0100 Subject: [PATCH 102/205] fixes after merge --- .../normalized-search-result.model.ts | 2 +- .../search-facet-option.component.spec.ts | 9 +++++---- .../search-facet-option.component.ts | 2 +- ...earch-facet-range-option.component.spec.ts | 18 ++++++++++-------- .../search-facet-filter.component.spec.ts | 3 +++ .../search-filter/search-filter.reducer.ts | 7 +------ .../search-range-filter.component.spec.ts | 3 +++ .../search-filters.component.ts | 19 ++++++++++++++----- src/app/+search-page/search-page.module.ts | 10 ---------- .../core/cache/object-cache.service.spec.ts | 7 ++++--- src/app/core/cache/object-cache.service.ts | 2 +- src/app/core/data/data.service.spec.ts | 3 +-- .../data/mydspace-response-parsing.service.ts | 12 ++++++------ .../object-updates/object-updates.service.ts | 9 ++++++--- src/app/core/data/request.models.ts | 1 - src/app/core/data/request.service.ts | 13 ++++--------- .../data/search-response-parsing.service.ts | 4 ++-- src/app/core/json-patch/selectors.ts | 3 ++- .../dso-selector/dso-selector.component.html | 2 +- .../dso-selector.component.spec.ts | 2 +- ...te-collection-parent-selector.component.ts | 9 ++------- .../create-item-parent-selector.component.ts | 13 ++----------- ...o-selector-modal-wrapper.component.spec.ts | 7 ++----- .../dso-selector-modal-wrapper.component.ts | 2 +- .../edit-item-selector.component.ts | 15 +++------------ .../onclick-menu-item.component.spec.ts | 1 - src/app/shared/shared.module.ts | 1 - yarn.lock | 8 ++++++++ 28 files changed, 84 insertions(+), 103 deletions(-) diff --git a/src/app/+search-page/normalized-search-result.model.ts b/src/app/+search-page/normalized-search-result.model.ts index c7335bcde4..cb1238936a 100644 --- a/src/app/+search-page/normalized-search-result.model.ts +++ b/src/app/+search-page/normalized-search-result.model.ts @@ -9,7 +9,7 @@ export class NormalizedSearchResult implements ListableObject { /** * The UUID of the DSpaceObject that was found */ - @autoserializeAs(String, 'rObject') + @autoserializeAs(String, 'resultObject') dspaceObject: string; /** diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index f1dbedfb40..279ce0f97a 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -33,10 +33,11 @@ describe('SearchFacetOptionComponent', () => { maxValue: 3000, }); const value: FacetValue = { - value: value2, - count: 20, - search: '' - }; + label: value2, + value: value2, + count: 20, + search: '' + }; const searchLink = '/search'; const selectedValues = [value1]; diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts index 7a6a51e99d..c4b339f464 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts @@ -1,5 +1,5 @@ import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs'; -import { map, take } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { FacetValue } from '../../../../search-service/facet-value.model'; diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts index 218730263b..d3264214ed 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.spec.ts @@ -35,10 +35,11 @@ describe('SearchFacetRangeOptionComponent', () => { maxValue: 3000, }); const value: FacetValue = { - value: value2, - count: 20, - search: '' - }; + label: value2, + value: value2, + count: 20, + search: '' + }; const searchLink = '/search'; let filterService; @@ -92,10 +93,11 @@ describe('SearchFacetRangeOptionComponent', () => { it('should update the changeQueryParams with the new parameter values', () => { comp.changeQueryParams = {}; comp.filterValue = { - value: '50-60', - count: 20, - search: '' - }; + label: '50-60', + value: '50-60', + count: 20, + search: '' + }; (comp as any).updateChangeParams(); expect(comp.changeQueryParams).toEqual({ [mockFilterConfig.paramName + RANGE_FILTER_MIN_SUFFIX]: ['50'], diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index cb3d4730b4..9e775ab08b 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -35,14 +35,17 @@ describe('SearchFacetFilterComponent', () => { }); const values: FacetValue[] = [ { + label: value1, value: value1, count: 52, search: '' }, { + label: value2, value: value2, count: 20, search: '' }, { + label: value3, value: value3, count: 5, search: '' diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts index 187bcd50d0..7102c8c9bc 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts @@ -1,9 +1,4 @@ -import { - SearchFilterAction, - SearchFilterActionTypes, - SearchFilterInitializeAction -} from './search-filter.actions'; -import { isEmpty, isNotUndefined } from '../../../shared/empty.util'; +import { SearchFilterAction, SearchFilterActionTypes, SearchFilterInitializeAction } from './search-filter.actions'; /** * Interface that represents the state for a single filters diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts index 930ea8c9fb..845babd79c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts @@ -41,14 +41,17 @@ describe('SearchRangeFilterComponent', () => { }); const values: FacetValue[] = [ { + label: value1, value: value1, count: 52, search: '' }, { + label: value2, value: value2, count: 20, search: '' }, { + label: value3, value: value3, count: 5, search: '' diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index 876d2a5f74..a0a140c784 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,7 +1,7 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap, tap } from 'rxjs/operators'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; @@ -20,7 +20,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.comp /** * This component represents the part of the search sidebar that contains filters. */ -export class SearchFiltersComponent { +export class SearchFiltersComponent implements OnInit { /** * An observable containing configuration about which filters are shown and how they are shown */ @@ -43,8 +43,16 @@ export class SearchFiltersComponent { @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService, private filterService: SearchFilterService) { - this.filters = searchService.getConfig().pipe(getSucceededRemoteData()); - this.clearParams = searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { + } + + ngOnInit(): void { + + this.filters = this.searchConfigService.searchOptions.pipe( + tap((o) => console.log(o)), + switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData())) + ); + + this.clearParams = this.searchConfigService.getCurrentFrontendFilters().pipe(map((filters) => { Object.keys(filters).forEach((f) => filters[f] = null); return filters; })); @@ -63,4 +71,5 @@ export class SearchFiltersComponent { trackUpdate(index, config: SearchFilterConfig) { return config ? config.name : undefined; } + } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index ec44a5d74e..482d2db8c1 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -5,9 +5,6 @@ import { SharedModule } from '../shared/shared.module'; import { SearchPageRoutingModule } from './search-page-routing.module'; import { SearchPageComponent } from './search-page.component'; import { SearchResultsComponent } from './search-results/search-results.component'; -import { ItemSearchResultListElementComponent } from '../shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component'; -import { CollectionSearchResultListElementComponent } from '../shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component'; -import { CommunitySearchResultListElementComponent } from '../shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component'; import { ItemSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component'; import { CommunitySearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component' import { CollectionSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component'; @@ -41,13 +38,9 @@ const components = [ SearchResultsComponent, SearchSidebarComponent, SearchSettingsComponent, - ItemSearchResultListElementComponent, - CollectionSearchResultListElementComponent, - CommunitySearchResultListElementComponent, ItemSearchResultGridElementComponent, CollectionSearchResultGridElementComponent, CommunitySearchResultGridElementComponent, - CommunitySearchResultListElementComponent, SearchFiltersComponent, SearchFilterComponent, SearchFacetFilterComponent, @@ -79,9 +72,6 @@ const components = [ SearchConfigurationService ], entryComponents: [ - ItemSearchResultListElementComponent, - CollectionSearchResultListElementComponent, - CommunitySearchResultListElementComponent, ItemSearchResultGridElementComponent, CollectionSearchResultGridElementComponent, CommunitySearchResultGridElementComponent, diff --git a/src/app/core/cache/object-cache.service.spec.ts b/src/app/core/cache/object-cache.service.spec.ts index eae7c06be7..20e12108ad 100644 --- a/src/app/core/cache/object-cache.service.spec.ts +++ b/src/app/core/cache/object-cache.service.spec.ts @@ -1,18 +1,19 @@ +import * as ngrx from '@ngrx/store'; import { Store } from '@ngrx/store'; import { of as observableOf } from 'rxjs'; import { ObjectCacheService } from './object-cache.service'; import { AddPatchObjectCacheAction, - AddToObjectCacheAction, ApplyPatchObjectCacheAction, + AddToObjectCacheAction, + ApplyPatchObjectCacheAction, RemoveFromObjectCacheAction } from './object-cache.actions'; import { CoreState } from '../core.reducers'; import { ResourceType } from '../shared/resource-type'; import { NormalizedItem } from './models/normalized-item.model'; import { first } from 'rxjs/operators'; -import * as ngrx from '@ngrx/store'; -import { Operation } from '../../../../node_modules/fast-json-patch'; +import { Operation } from 'fast-json-patch'; import { RestRequestMethod } from '../data/rest-request-method'; import { AddToSSBAction } from './server-sync-buffer.actions'; import { Patch } from './object-cache.reducer'; diff --git a/src/app/core/cache/object-cache.service.ts b/src/app/core/cache/object-cache.service.ts index 483de65b98..e6384571c3 100644 --- a/src/app/core/cache/object-cache.service.ts +++ b/src/app/core/cache/object-cache.service.ts @@ -4,7 +4,7 @@ import { applyPatch, Operation } from 'fast-json-patch'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { distinctUntilChanged, filter, map, mergeMap, take, } from 'rxjs/operators'; -import { hasNoValue, hasValue, isNotEmpty } from '../../shared/empty.util'; +import { hasNoValue, isNotEmpty } from '../../shared/empty.util'; import { CoreState } from '../core.reducers'; import { coreSelector } from '../core.selectors'; import { RestRequestMethod } from '../data/rest-request-method'; diff --git a/src/app/core/data/data.service.spec.ts b/src/app/core/data/data.service.spec.ts index 4a244db24f..dede6f8ae2 100644 --- a/src/app/core/data/data.service.spec.ts +++ b/src/app/core/data/data.service.spec.ts @@ -9,13 +9,12 @@ import { Observable, of as observableOf } from 'rxjs'; import { FindAllOptions } from './request.models'; import { SortDirection, SortOptions } from '../cache/models/sort-options.model'; import { ObjectCacheService } from '../cache/object-cache.service'; -import { Operation } from '../../../../node_modules/fast-json-patch'; +import { compare, Operation } from 'fast-json-patch'; import { DSpaceObject } from '../shared/dspace-object.model'; import { ChangeAnalyzer } from './change-analyzer'; import { HttpClient } from '@angular/common/http'; import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; -import { compare } from 'fast-json-patch'; import { Item } from '../shared/item.model'; const endpoint = 'https://rest.api/core'; diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index 9cd2103657..076103158f 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -40,7 +40,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { const dsoSelfLinks = payload._embedded.objects .filter((object) => hasValue(object._embedded)) - .map((object) => object._embedded.rObject) + .map((object) => object._embedded.resultObject) .map((dso) => this.dsoParser.parse(request, { payload: dso, statusCode: data.statusCode, @@ -52,7 +52,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { const objects = payload._embedded.objects .filter((object) => hasValue(object._embedded)) .map((object, index) => Object.assign({}, object, { - rObject: dsoSelfLinks[index], + resultObject: dsoSelfLinks[index], hitHighlights: hitHighlights[index], _embedded: this.filterEmbeddedObjects(object) })); @@ -63,13 +63,13 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { protected filterEmbeddedObjects(object) { const allowedEmbeddedKeys = ['submitter', 'item', 'workspaceitem', 'workflowitem']; - if (object._embedded.rObject && object._embedded.rObject._embedded) { + if (object._embedded.resultObject && object._embedded.resultObject._embedded) { return Object.assign({}, object._embedded, { - rObject: Object.assign({}, object._embedded.rObject, { - _embedded: Object.keys(object._embedded.rObject._embedded) + resultObject: Object.assign({}, object._embedded.resultObject, { + _embedded: Object.keys(object._embedded.resultObject._embedded) .filter((key) => allowedEmbeddedKeys.includes(key)) .reduce((obj, key) => { - obj[key] = object._embedded.rObject._embedded[key]; + obj[key] = object._embedded.resultObject._embedded[key]; return obj; }, {}) }) diff --git a/src/app/core/data/object-updates/object-updates.service.ts b/src/app/core/data/object-updates/object-updates.service.ts index a13fb9487b..22d5fd3e77 100644 --- a/src/app/core/data/object-updates/object-updates.service.ts +++ b/src/app/core/data/object-updates/object-updates.service.ts @@ -5,7 +5,8 @@ import { coreSelector } from '../../core.selectors'; import { FieldState, FieldUpdates, - Identifiable, OBJECT_UPDATES_TRASH_PATH, + Identifiable, + OBJECT_UPDATES_TRASH_PATH, ObjectUpdatesEntry, ObjectUpdatesState } from './object-updates.reducer'; @@ -17,9 +18,10 @@ import { InitializeFieldsAction, ReinstateObjectUpdatesAction, RemoveFieldUpdateAction, - SetEditableFieldUpdateAction, SetValidFieldUpdateAction + SetEditableFieldUpdateAction, + SetValidFieldUpdateAction } from './object-updates.actions'; -import { distinctUntilChanged, filter, map, tap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, map } from 'rxjs/operators'; import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../../shared/empty.util'; import { INotification } from '../../../shared/notifications/models/notification.model'; @@ -212,6 +214,7 @@ export class ObjectUpdatesService { /** * Method to dispatch an RemoveFieldUpdateAction to the store * @param url The page's URL for which the changes should be removed + * @param uuid The UUID of the field that should be set */ removeSingleFieldUpdate(url: string, uuid) { this.store.dispatch(new RemoveFieldUpdateAction(url, uuid)); diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 1dedcc89e2..f8cc0a4fd8 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -14,7 +14,6 @@ import { RestRequestMethod } from './rest-request-method'; import { SearchParam } from '../cache/models/search-param.model'; import { EpersonResponseParsingService } from '../eperson/eperson-response-parsing.service'; import { BrowseItemsResponseParsingService } from './browse-items-response-parsing-service'; -import { RegistryMetadataschemasResponseParsingService } from './registry-metadataschemas-response-parsing.service'; import { MetadataschemaParsingService } from './metadataschema-parsing.service'; import { MetadatafieldParsingService } from './metadatafield-parsing.service'; import { URLCombiner } from '../url-combiner/url-combiner'; diff --git a/src/app/core/data/request.service.ts b/src/app/core/data/request.service.ts index efc3ecb449..1bf86dc1e2 100644 --- a/src/app/core/data/request.service.ts +++ b/src/app/core/data/request.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@angular/core'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; -import { merge as observableMerge, Observable, of as observableOf, race as observableRace } from 'rxjs'; -import { filter, find, first, map, mergeMap, switchMap, take } from 'rxjs/operators'; +import { Observable, race as observableRace } from 'rxjs'; +import { filter, find, mergeMap, take } from 'rxjs/operators'; import { remove } from 'lodash'; import { AppState } from '../../app.reducer'; -import { hasNoValue, hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util'; import { CacheableObject } from '../cache/object-cache.reducer'; import { ObjectCacheService } from '../cache/object-cache.service'; import { CoreState } from '../core.reducers'; @@ -17,11 +17,7 @@ import { uuidFromHrefSelector } from '../index/index.selectors'; import { UUIDService } from '../shared/uuid.service'; -import { - RequestConfigureAction, - RequestExecuteAction, - RequestRemoveAction -} from './request.actions'; +import { RequestConfigureAction, RequestExecuteAction, RequestRemoveAction } from './request.actions'; import { GetRequest, RestRequest } from './request.models'; import { RequestEntry, RequestState } from './request.reducer'; import { CommitSSBAction } from '../cache/server-sync-buffer.actions'; @@ -52,7 +48,6 @@ const entryFromUUIDSelector = (uuid: string): MemoizedSelector hasValue(object._embedded)) - .map((object) => object._embedded.rObject) + .map((object) => object._embedded.resultObject) // we don't need embedded collections, bitstreamformats, etc for search results. // And parsing them all takes up a lot of time. Throw them away to improve performance // until objs until partial results are supported by the rest api @@ -53,7 +53,7 @@ export class SearchResponseParsingService implements ResponseParsingService { const objects = payload._embedded.objects .filter((object) => hasValue(object._embedded)) .map((object, index) => Object.assign({}, object, { - rObject: dsoSelfLinks[index], + resultObject: dsoSelfLinks[index], hitHighlights: hitHighlights[index], // we don't need embedded collections, bitstreamformats, etc for search results. // And parsing them all takes up a lot of time. Throw them away to improve performance diff --git a/src/app/core/json-patch/selectors.ts b/src/app/core/json-patch/selectors.ts index a77afb7b7d..f6df4e1d07 100644 --- a/src/app/core/json-patch/selectors.ts +++ b/src/app/core/json-patch/selectors.ts @@ -1,7 +1,8 @@ // @TODO: Merge with keySelector function present in 'src/app/core/shared/selectors.ts' import { createSelector, MemoizedSelector, Selector } from '@ngrx/store'; import { hasValue } from '../../shared/empty.util'; -import { coreSelector, CoreState } from '../core.reducers'; +import { CoreState } from '../core.reducers'; +import { coreSelector } from '../core.selectors'; import { JsonPatchOperationsEntry, JsonPatchOperationsResourceEntry } from './json-patch-operations.reducer'; export function keySelector(parentSelector: Selector, subState: string, key: string): MemoizedSelector { diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index 1e0deed4b9..da6bfa40ba 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -17,4 +17,4 @@ (click)="onSelect.emit(listEntry.dspaceObject)" #listEntryElement> -
\ No newline at end of file +
diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts index 04111a4ea6..f9d1567245 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts @@ -1,4 +1,4 @@ -import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { DSOSelectorComponent } from './dso-selector.component'; diff --git a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts index 1e129c0dbe..0533addb01 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-collection-parent-selector/create-collection-parent-selector.component.ts @@ -1,7 +1,5 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; -import { Community } from '../../../../core/shared/community.model'; -import { RemoteData } from '../../../../core/data/remote-data'; import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @@ -9,10 +7,7 @@ import { COLLECTION_PARENT_PARAMETER, getCollectionCreatePath } from '../../../../+collection-page/collection-page-routing.module'; -import { - DSOSelectorModalWrapperComponent, - SelectorActionType -} from '../dso-selector-modal-wrapper.component'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; /** * Component to wrap a list of existing communities inside a modal diff --git a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts index dac5888bf7..29af9f624e 100644 --- a/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/create-item-parent-selector/create-item-parent-selector.component.ts @@ -1,18 +1,9 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Community } from '../../../../core/shared/community.model'; -import { RemoteData } from '../../../../core/data/remote-data'; -import { Collection } from '../../../../core/shared/collection.model'; import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; -import { hasValue } from '../../../empty.util'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { map } from 'rxjs/operators'; -import { Observable } from 'rxjs'; -import { - DSOSelectorModalWrapperComponent, - SelectorActionType -} from '../dso-selector-modal-wrapper.component'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; /** * Component to wrap a list of existing collections inside a modal diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts index ea857f7d62..4ceaeccb3a 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.spec.ts @@ -5,10 +5,7 @@ import { DSpaceObjectType } from '../../../core/shared/dspace-object-type.model' import { RemoteData } from '../../../core/data/remote-data'; import { Item } from '../../../core/shared/item.model'; import { of as observableOf } from 'rxjs'; -import { - DSOSelectorModalWrapperComponent, - SelectorActionType -} from './dso-selector-modal-wrapper.component'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from './dso-selector-modal-wrapper.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ActivatedRoute } from '@angular/router'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; @@ -16,7 +13,7 @@ import { first } from 'rxjs/operators'; import { By } from '@angular/platform-browser'; import { DSOSelectorComponent } from '../dso-selector/dso-selector.component'; import { MockComponent } from 'ng-mocks'; -import { MetadataMap, MetadataValue } from '../../../core/shared/metadata.models'; +import { MetadataValue } from '../../../core/shared/metadata.models'; describe('DSOSelectorModalWrapperComponent', () => { let component: DSOSelectorModalWrapperComponent; diff --git a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts index 351a92302c..881476cac6 100644 --- a/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component.ts @@ -1,4 +1,4 @@ -import { Component, Injectable, Input, OnInit } from '@angular/core'; +import { Injectable, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { DSpaceObject } from '../../../core/shared/dspace-object.model'; diff --git a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts index 9182df8045..dae36d3017 100644 --- a/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component.ts @@ -1,19 +1,10 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; -import { Community } from '../../../../core/shared/community.model'; -import { RemoteData } from '../../../../core/data/remote-data'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; import { DSpaceObjectType } from '../../../../core/shared/dspace-object-type.model'; import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Collection } from '../../../../core/shared/collection.model'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { Item } from '../../../../core/shared/item.model'; import { getItemEditPath } from '../../../../+item-page/item-page-routing.module'; -import { - DSOSelectorModalWrapperComponent, - SelectorActionType -} from '../dso-selector-modal-wrapper.component'; +import { DSOSelectorModalWrapperComponent, SelectorActionType } from '../dso-selector-modal-wrapper.component'; /** * Component to wrap a list of existing items inside a modal diff --git a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts index dd031a96e0..dbe6fdab6a 100644 --- a/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts +++ b/src/app/shared/menu/menu-item/onclick-menu-item.component.spec.ts @@ -1,5 +1,4 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TextMenuItemComponent } from './text-menu-item.component'; import { TranslateModule } from '@ngx-translate/core'; import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index d9fc667e23..cef04918c8 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -126,7 +126,6 @@ import { ItemSearchResultListElementComponent } from './object-list/search-resul import { EditItemSelectorComponent } from './dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; import { EditCommunitySelectorComponent } from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditCollectionSelectorComponent } from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; -import { DSOSelectorModalWrapperComponent } from './dso-selector/modal-wrappers/dso-selector-modal-wrapper.component'; import { ItemListPreviewComponent } from './object-list/item-list-preview/item-list-preview.component'; import { ItemPageAuthorFieldComponent } from '../+item-page/simple/field-components/specific-field/author/item-page-author-field.component'; import { ItemPageDateFieldComponent } from '../+item-page/simple/field-components/specific-field/date/item-page-date-field.component'; diff --git a/yarn.lock b/yarn.lock index a3a3b043c7..9ca37783d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -285,6 +285,7 @@ "@types/circular-json@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@types/circular-json/-/circular-json-0.4.0.tgz#7401f7e218cfe87ad4c43690da5658b9acaf51be" + integrity sha512-7+kYB7x5a7nFWW1YPBh3KxhwKfiaI4PbZ1RvzBU91LZy7lWJO822CI+pqzSre/DZ7KsCuMKdHnLHHFu8AyXbQg== "@types/connect@*": version "3.4.32" @@ -448,6 +449,7 @@ "@types/stacktrace-js@^0.0.32": version "0.0.32" resolved "https://registry.yarnpkg.com/@types/stacktrace-js/-/stacktrace-js-0.0.32.tgz#d23e4a36a5073d39487fbea8234cc6186862d389" + integrity sha512-SdxmlrHfO0BxgbBP9HZWMUo2rima8lwMjPiWm6S0dyKkDa5CseamktFhXg8umu3TPVBkSlX6ZoB5uUDJK89yvg== "@types/strip-bom@^3.0.0": version "3.0.0" @@ -1881,6 +1883,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: circular-json@^0.5.0: version "0.5.9" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" + integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== circular-json@^0.5.5: version "0.5.5" @@ -3052,6 +3055,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: error-stack-parser@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.2.tgz#4ae8dbaa2bf90a8b450707b9149dcabca135520d" + integrity sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw== dependencies: stackframe "^1.0.4" @@ -8418,6 +8422,7 @@ rx@^4.1.0: rxjs-spy@^7.5.1: version "7.5.1" resolved "https://registry.yarnpkg.com/rxjs-spy/-/rxjs-spy-7.5.1.tgz#1a9ef50bc8d7dd00d9ecf3c54c00929231eaf319" + integrity sha512-dJ9mO4HvW2r16PsU15Qsc0RVkG7pFrfyCNTGx3vrxWje3kIgZ6QjMVnWblQxbniZ32lwLk/2x9+D2O6GhgXV/w== dependencies: "@types/circular-json" "^0.4.0" "@types/stacktrace-js" "^0.0.32" @@ -8936,6 +8941,7 @@ source-map@0.5.0: source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= source-map@0.7.3: version "0.7.3" @@ -9079,10 +9085,12 @@ ssri@^5.2.4: stackframe@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b" + integrity sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw== stacktrace-gps@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.2.tgz#33f8baa4467323ab2bd1816efa279942ba431ccc" + integrity sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg== dependencies: source-map "0.5.6" stackframe "^1.0.4" From 67c9fc9fddb90503c6174a6e32339d6595225d26 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 26 Mar 2019 16:32:38 +0100 Subject: [PATCH 103/205] Fixes --- .../+search-page/search-filters/search-filters.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index a0a140c784..a6612154cf 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,7 +1,7 @@ import { Component, Inject, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { map, switchMap, tap } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; @@ -48,7 +48,6 @@ export class SearchFiltersComponent implements OnInit { ngOnInit(): void { this.filters = this.searchConfigService.searchOptions.pipe( - tap((o) => console.log(o)), switchMap((options) => this.searchService.getConfig(options.scope, options.configuration).pipe(getSucceededRemoteData())) ); From 9b65189b4a7c9e339ff185aa261fab40e466fcdc Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 26 Mar 2019 17:03:03 +0100 Subject: [PATCH 104/205] Fixed build errors --- src/app/shared/log-out/log-out.component.html | 2 +- src/app/shared/log-out/log-out.component.ts | 1 + src/app/shared/message-board/message/message.component.html | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/shared/log-out/log-out.component.html b/src/app/shared/log-out/log-out.component.html index ab398d1735..d522fc6fb9 100644 --- a/src/app/shared/log-out/log-out.component.html +++ b/src/app/shared/log-out/log-out.component.html @@ -1,6 +1,6 @@ diff --git a/src/app/shared/log-out/log-out.component.ts b/src/app/shared/log-out/log-out.component.ts index 0a6b936cd5..6fa71caa32 100644 --- a/src/app/shared/log-out/log-out.component.ts +++ b/src/app/shared/log-out/log-out.component.ts @@ -25,6 +25,7 @@ export class LogOutComponent implements OnInit { /** * @constructor * @param {Store} store + * @param {Router} router */ constructor(private router: Router, private store: Store) { diff --git a/src/app/shared/message-board/message/message.component.html b/src/app/shared/message-board/message/message.component.html index d8637e87fe..e6dfe7dcb5 100644 --- a/src/app/shared/message-board/message/message.component.html +++ b/src/app/shared/message-board/message/message.component.html @@ -1,4 +1,4 @@ -
  • @@ -7,7 +7,7 @@
    - {{m.findMetadata('dc.date.issued') | date: 'dd/MM/yyyy HH:mm'}} + {{m.firstMetadataValue('dc.date.issued') | date: 'dd/MM/yyyy HH:mm'}} - +
    From 39b98f7f1ca44c5daa8fbd70863872cb184a4a2a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 27 Mar 2019 14:49:37 +0100 Subject: [PATCH 105/205] Fixed unit tests --- .../metadata-field-form.component.spec.ts | 6 +++ .../search-facet-filter.component.spec.ts | 5 +- .../search-filter.component.spec.ts | 6 +-- .../search-filter/search-filter.component.ts | 8 ++- .../search-filter.service.spec.ts | 19 +------ .../search-range-filter.component.spec.ts | 6 +-- .../search-filters.component.spec.ts | 10 ++-- .../search-filters.component.ts | 4 +- .../search-labels.component.spec.ts | 5 +- .../search-page.component.spec.ts | 54 ++++++++++--------- src/app/+search-page/search-page.component.ts | 6 +-- .../search-configuration.service.ts | 6 +-- .../search-service/search.service.spec.ts | 28 +++++----- .../search-service/search.service.ts | 1 - .../search-settings.component.spec.ts | 4 +- .../auth-nav-menu.component.spec.ts | 4 +- src/app/shared/mocks/mock-router.ts | 9 +++- .../pagination/pagination.component.spec.ts | 2 +- src/app/shared/services/route.service.spec.ts | 5 +- src/app/shared/testing/route-service-stub.ts | 26 +++++++++ .../search-configuration-service-stub.ts | 16 ++++++ 21 files changed, 143 insertions(+), 87 deletions(-) create mode 100644 src/app/shared/testing/route-service-stub.ts create mode 100644 src/app/shared/testing/search-configuration-service-stub.ts diff --git a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts index 4364b0234a..c6402c1f3b 100644 --- a/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts +++ b/src/app/+admin/admin-registries/metadata-schema/metadata-field-form/metadata-field-form.component.spec.ts @@ -28,6 +28,7 @@ describe('MetadataFieldFormComponent', () => { const registryServiceStub = { getActiveMetadataField: () => observableOf(undefined), createOrUpdateMetadataField: (field: MetadataField) => observableOf(field), + cancelEditMetadataField: () => {}, cancelEditMetadataSchema: () => {}, }; const formBuilderServiceStub = { @@ -62,6 +63,11 @@ describe('MetadataFieldFormComponent', () => { registryService = s; })); + afterEach(() => { + component = null; + registryService = null + }) + describe('when submitting the form', () => { const element = 'fakeElement'; const qualifier = 'fakeQualifier'; diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index 9e775ab08b..adb2919653 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -17,7 +17,8 @@ import { Router } from '@angular/router'; import { PageInfo } from '../../../../core/shared/page-info.model'; import { SearchFacetFilterComponent } from './search-facet-filter.component'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; -import { SearchConfigurationService } from '../../../search-service/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service-stub'; +import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component'; describe('SearchFacetFilterComponent', () => { let comp: SearchFacetFilterComponent; @@ -69,7 +70,7 @@ describe('SearchFacetFilterComponent', () => { { provide: Router, useValue: new RouterStub() }, { provide: FILTER_CONFIG, useValue: new SearchFilterConfig() }, { provide: RemoteDataBuildService, useValue: {aggregate: () => observableOf({})} }, - { provide: SearchConfigurationService, useValue: {searchOptions: observableOf({})} }, + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: SearchFilterService, useValue: { getSelectedValuesForFilter: () => observableOf(selectedValues), diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts index 30ef349675..23c4ab3b53 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.spec.ts @@ -11,6 +11,8 @@ import { SearchFilterComponent } from './search-filter.component'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { FilterType } from '../../search-service/filter-type.model'; import { SearchConfigurationService } from '../../search-service/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../../shared/testing/search-configuration-service-stub'; +import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component'; describe('SearchFilterComponent', () => { let comp: SearchFilterComponent; @@ -54,8 +56,6 @@ describe('SearchFilterComponent', () => { getFacetValuesFor: (filter) => mockResults }; - const searchConfigServiceStub = {}; - beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule], @@ -66,7 +66,7 @@ describe('SearchFilterComponent', () => { provide: SearchFilterService, useValue: mockFilterService }, - { provide: SearchConfigurationService, useValue: searchConfigServiceStub }, + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchFilterComponent, { diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index 83bf436e6e..5c6973bd98 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Observable, of as observableOf } from 'rxjs'; import { filter, first, map, startWith, switchMap, take } from 'rxjs/operators'; @@ -9,6 +9,7 @@ import { slide } from '../../../shared/animations/slide'; import { isNotEmpty } from '../../../shared/empty.util'; import { SearchService } from '../../search-service/search.service'; import { SearchConfigurationService } from '../../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-search-filter', @@ -46,7 +47,10 @@ export class SearchFilterComponent implements OnInit { */ active$: Observable; - constructor(private filterService: SearchFilterService, private searchService: SearchService, private searchConfigService: SearchConfigurationService) { + constructor( + private filterService: SearchFilterService, + private searchService: SearchService, + @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) { } /** diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts index 19239d899c..24648c8995 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts @@ -14,6 +14,7 @@ import { SearchFilterConfig } from '../../search-service/search-filter-config.mo import { FilterType } from '../../search-service/filter-type.model'; import { ActivatedRouteStub } from '../../../shared/testing/active-router-stub'; import { of as observableOf } from 'rxjs'; +import { routeServiceStub } from '../../../shared/testing/route-service-stub'; describe('SearchFilterService', () => { let service: SearchFilterService; @@ -34,24 +35,6 @@ describe('SearchFilterService', () => { select: observableOf(true) }); - const routeServiceStub: any = { - /* tslint:disable:no-empty */ - hasQueryParamWithValue: (param: string, value: string) => { - }, - hasQueryParam: (param: string) => { - }, - removeQueryParameterValue: (param: string, value: string) => { - }, - addQueryParameterValue: (param: string, value: string) => { - }, - getQueryParameterValues: (param: string) => { - return observableOf({}); - }, - getQueryParamsWithPrefix: (param: string) => { - return observableOf({}); - } - /* tslint:enable:no-empty */ - }; const activatedRoute: any = new ActivatedRouteStub(); const searchServiceStub: any = { uiSearchRoute: '/search' diff --git a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts index 845babd79c..4fc3222600 100644 --- a/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts @@ -19,6 +19,8 @@ import { SearchRangeFilterComponent } from './search-range-filter.component'; import { RouteService } from '../../../../shared/services/route.service'; import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; import { SearchConfigurationService } from '../../../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../+my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service-stub'; describe('SearchRangeFilterComponent', () => { let comp: SearchRangeFilterComponent; @@ -76,9 +78,7 @@ describe('SearchRangeFilterComponent', () => { { provide: FILTER_CONFIG, useValue: mockFilterConfig }, { provide: RemoteDataBuildService, useValue: {aggregate: () => observableOf({})} }, { provide: RouteService, useValue: {getQueryParameterValue: () => observableOf({})} }, - { provide: SearchConfigurationService, useValue: { - searchOptions: observableOf({}) } - }, + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: SearchFilterService, useValue: { getSelectedValuesForFilter: () => selectedValues, diff --git a/src/app/+search-page/search-filters/search-filters.component.spec.ts b/src/app/+search-page/search-filters/search-filters.component.spec.ts index db21fc8a69..dc883cd290 100644 --- a/src/app/+search-page/search-filters/search-filters.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filters.component.spec.ts @@ -7,13 +7,15 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { SearchFilterService } from './search-filter/search-filter.service'; import { SearchFiltersComponent } from './search-filters.component'; import { SearchService } from '../search-service/search.service'; -import { SearchConfigurationService } from '../search-service/search-configuration.service'; import { of as observableOf } from 'rxjs'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub'; describe('SearchFiltersComponent', () => { let comp: SearchFiltersComponent; let fixture: ComponentFixture; let searchService: SearchService; + const searchServiceStub = { /* tslint:disable:no-empty */ getConfig: () => @@ -30,17 +32,13 @@ describe('SearchFiltersComponent', () => { [] }; - const searchConfigServiceStub = jasmine.createSpyObj('SearchConfigurationService', { - getCurrentFrontendFilters: observableOf({}) - }); - beforeEach(async(() => { TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule], declarations: [SearchFiltersComponent], providers: [ { provide: SearchService, useValue: searchServiceStub }, - { provide: SearchConfigurationService, useValue: searchConfigServiceStub }, + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: SearchFilterService, useValue: searchFiltersStub }, ], diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index a6612154cf..9c235b2f58 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -40,8 +40,8 @@ export class SearchFiltersComponent implements OnInit { */ constructor( private searchService: SearchService, - @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService, - private filterService: SearchFilterService) { + private filterService: SearchFilterService, + @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) { } diff --git a/src/app/+search-page/search-labels/search-labels.component.spec.ts b/src/app/+search-page/search-labels/search-labels.component.spec.ts index 81fa5b5df8..aada73673e 100644 --- a/src/app/+search-page/search-labels/search-labels.component.spec.ts +++ b/src/app/+search-page/search-labels/search-labels.component.spec.ts @@ -10,6 +10,8 @@ import { Observable, of as observableOf } from 'rxjs'; import { Params } from '@angular/router'; import { ObjectKeysPipe } from '../../shared/utils/object-keys-pipe'; import { SearchConfigurationService } from '../search-service/search-configuration.service'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub'; describe('SearchLabelsComponent', () => { let comp: SearchLabelsComponent; @@ -35,7 +37,8 @@ describe('SearchLabelsComponent', () => { declarations: [SearchLabelsComponent, ObjectKeysPipe], providers: [ { provide: SearchService, useValue: new SearchServiceStub(searchLink) }, - { provide: SearchConfigurationService, useValue: {getCurrentFrontendFilters : () => observableOf({})} } + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() } + // { provide: SearchConfigurationService, useValue: {getCurrentFrontendFilters : () => observableOf({})} } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchLabelsComponent, { diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index 1991cf8f1b..71d6997420 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -4,7 +4,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; -import { cold, hot } from 'jasmine-marbles'; +import { cold } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; import { CommunityDataService } from '../core/data/community-data.service'; @@ -20,11 +20,17 @@ import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchFilterService } from './search-filters/search-filter/search-filter.service'; import { SearchConfigurationService } from './search-service/search-configuration.service'; import { RemoteData } from '../core/data/remote-data'; +import { SEARCH_CONFIG_SERVICE } from '../+my-dspace-page/my-dspace-page.component'; +import { RouteService } from '../shared/services/route.service'; +import { routeServiceStub } from '../shared/testing/route-service-stub'; +import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub'; +import { PaginatedSearchOptions } from './paginated-search-options.model'; describe('SearchPageComponent', () => { let comp: SearchPageComponent; let fixture: ComponentFixture; let searchServiceObject: SearchService; + let searchConfigurationServiceObject: SearchConfigurationService; const store: Store = jasmine.createSpyObj('store', { /* tslint:disable:no-empty */ dispatch: {}, @@ -42,15 +48,23 @@ describe('SearchPageComponent', () => { getSearchLink: '/search', getScopes: observableOf(['test-scope']) }); + const configurationParam = 'default'; const queryParam = 'test query'; const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; - const paginatedSearchOptions = { + const paginatedSearchOptions = new PaginatedSearchOptions({ + configuration: configurationParam, query: queryParam, scope: scopeParam, pagination, sort - }; + }); const activatedRouteStub = { + snapshot: { + queryParamMap: new Map([ + ['query', queryParam], + ['scope', scopeParam] + ]) + }, queryParams: observableOf({ query: queryParam, scope: scopeParam @@ -73,6 +87,7 @@ describe('SearchPageComponent', () => { useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) }, { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: RouteService, useValue: routeServiceStub }, { provide: Store, useValue: store }, @@ -92,13 +107,8 @@ describe('SearchPageComponent', () => { provide: SearchFilterService, useValue: {} }, { - provide: SearchConfigurationService, - useValue: { - paginatedSearchOptions: hot('a', { - a: paginatedSearchOptions - }), - getCurrentScope: (a) => observableOf('test-id') - } + provide: SEARCH_CONFIG_SERVICE, + useValue: new SearchConfigurationServiceStub() }, ], schemas: [NO_ERRORS_SCHEMA] @@ -112,25 +122,21 @@ describe('SearchPageComponent', () => { comp = fixture.componentInstance; // SearchPageComponent test instance fixture.detectChanges(); searchServiceObject = (comp as any).service; + searchConfigurationServiceObject = (comp as any).searchConfigService; + }); + + afterEach(() => { + comp = null; + searchServiceObject = null; + searchConfigurationServiceObject = null; }); it('should get the scope and query from the route parameters', () => { + + searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions); expect(comp.searchOptions$).toBeObservable(cold('b', { b: paginatedSearchOptions })); - }); - - describe('when the closeSidebar event is emitted clicked in mobile view', () => { - - beforeEach(() => { - spyOn(comp, 'closeSidebar'); - const closeSidebarButton = fixture.debugElement.query(By.css('#search-sidebar-sm')); - closeSidebarButton.triggerEventHandler('toggleSidebar', null); - }); - - it('should trigger the closeSidebar function', () => { - expect(comp.closeSidebar).toHaveBeenCalled(); - }); }); @@ -177,4 +183,4 @@ describe('SearchPageComponent', () => { }); }); -}) +}); diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 333faacc47..610811981c 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -53,7 +53,7 @@ export class SearchPageComponent implements OnInit { /** * The current relevant scopes */ - scopeListRD$: Observable; + scopeListRD$: Observable = new BehaviorSubject(null); /** * Emits true if were on a small screen @@ -86,9 +86,9 @@ export class SearchPageComponent implements OnInit { .subscribe((results) => { this.resultsRD$.next(results); }); - this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( +/* this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) - ); + );*/ } /** diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index 380bace080..39f1919f72 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -87,8 +87,8 @@ export class SearchConfigurationService implements OnDestroy { .pipe(getSucceededRemoteData()) .subscribe((defRD) => { const defs = defRD.payload; - this.paginatedSearchOptions = new BehaviorSubject(defs); - this.searchOptions = new BehaviorSubject(defs); + this.paginatedSearchOptions = new BehaviorSubject(defs); + this.searchOptions = new BehaviorSubject(defs); this.subs.push(this.subscribeToSearchOptions(defs)); this.subs.push(this.subscribeToPaginatedSearchOptions(defs)); @@ -129,7 +129,7 @@ export class SearchConfigurationService implements OnDestroy { */ getCurrentDSOType(): Observable { return this.routeService.getQueryParameterValue('dsoType').pipe( - filter((type) => hasValue(type) && hasValue(DSpaceObjectType[type.toUpperCase()])), + filter((type) => isNotEmpty(type) && hasValue(DSpaceObjectType[type.toUpperCase()])), map((type) => DSpaceObjectType[type.toUpperCase()]),); } diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts index ca48b02aa7..b188dc35b8 100644 --- a/src/app/+search-page/search-service/search.service.spec.ts +++ b/src/app/+search-page/search-service/search.service.spec.ts @@ -6,27 +6,25 @@ import { Component } from '@angular/core'; import { SearchService } from './search.service'; import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service'; -import { ActivatedRoute, Router, UrlTree } from '@angular/router'; +import { Router, UrlTree } from '@angular/router'; import { RequestService } from '../../core/data/request.service'; import { ActivatedRouteStub } from '../../shared/testing/active-router-stub'; import { RouterStub } from '../../shared/testing/router-stub'; import { HALEndpointService } from '../../core/shared/hal-endpoint.service'; -import { Observable, combineLatest as observableCombineLatest } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { PaginatedSearchOptions } from '../paginated-search-options.model'; import { RemoteData } from '../../core/data/remote-data'; import { RequestEntry } from '../../core/data/request.reducer'; import { getMockRequestService } from '../../shared/mocks/mock-request.service'; -import { - FacetConfigSuccessResponse, - SearchSuccessResponse -} from '../../core/cache/response.models'; +import { FacetConfigSuccessResponse, SearchSuccessResponse } from '../../core/cache/response.models'; import { SearchQueryResponse } from './search-query-response.model'; import { SearchFilterConfig } from './search-filter-config.model'; import { CommunityDataService } from '../../core/data/community-data.service'; import { ViewMode } from '../../core/shared/view-mode.model'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; -import { of as observableOf } from 'rxjs'; import { map } from 'rxjs/operators'; +import { RouteService } from '../../shared/services/route.service'; +import { routeServiceStub } from '../../shared/testing/route-service-stub'; @Component({ template: '' }) class DummyComponent { @@ -50,7 +48,7 @@ describe('SearchService', () => { ], providers: [ { provide: Router, useValue: router }, - { provide: ActivatedRoute, useValue: route }, + { provide: RouteService, useValue: routeServiceStub }, { provide: RequestService, useValue: getMockRequestService() }, { provide: RemoteDataBuildService, useValue: {} }, { provide: HALEndpointService, useValue: {} }, @@ -71,7 +69,7 @@ describe('SearchService', () => { describe('', () => { let searchService: SearchService; const router = new RouterStub(); - const route = new ActivatedRouteStub(); + let routeService; const halService = { /* tslint:disable:no-empty */ @@ -107,7 +105,7 @@ describe('SearchService', () => { ], providers: [ { provide: Router, useValue: router }, - { provide: ActivatedRoute, useValue: route }, + { provide: RouteService, useValue: routeServiceStub }, { provide: RequestService, useValue: getMockRequestService() }, { provide: RemoteDataBuildService, useValue: remoteDataBuildService }, { provide: HALEndpointService, useValue: halService }, @@ -117,6 +115,7 @@ describe('SearchService', () => { ], }); searchService = TestBed.get(SearchService); + routeService = TestBed.get(RouteService); const urlTree = Object.assign(new UrlTree(), { root: { children: { primary: 'search' } } }); router.parseUrl.and.returnValue(urlTree); }); @@ -139,14 +138,19 @@ describe('SearchService', () => { it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => { let viewMode = ViewMode.Grid; - route.testParams = { view: ViewMode.List }; + spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([ + [ 'view', ViewMode.List ], + ]))); + searchService.getViewMode().subscribe((mode) => viewMode = mode); expect(viewMode).toEqual(ViewMode.List); }); it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => { let viewMode = ViewMode.List; - route.testParams = { view: ViewMode.Grid }; + spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([ + [ 'view', ViewMode.Grid ], + ]))); searchService.getViewMode().subscribe((mode) => viewMode = mode); expect(viewMode).toEqual(ViewMode.Grid); }); diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 361f08b724..ed19f1df68 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -74,7 +74,6 @@ export class SearchService implements OnDestroy { private sub; constructor(private router: Router, - private route: ActivatedRoute, private routeService: RouteService, protected requestService: RequestService, private rdb: RemoteDataBuildService, diff --git a/src/app/+search-page/search-settings/search-settings.component.spec.ts b/src/app/+search-page/search-settings/search-settings.component.spec.ts index b1585c4347..b9b5c5a5eb 100644 --- a/src/app/+search-page/search-settings/search-settings.component.spec.ts +++ b/src/app/+search-page/search-settings/search-settings.component.spec.ts @@ -14,8 +14,8 @@ import { By } from '@angular/platform-browser'; import { SearchFilterService } from '../search-filters/search-filter/search-filter.service'; import { hot } from 'jasmine-marbles'; import { VarDirective } from '../../shared/utils/var.directive'; -import { SearchConfigurationService } from '../search-service/search-configuration.service'; import { first } from 'rxjs/operators'; +import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; describe('SearchSettingsComponent', () => { @@ -73,7 +73,7 @@ describe('SearchSettingsComponent', () => { useValue: {} }, { - provide: SearchConfigurationService, + provide: SEARCH_CONFIG_SERVICE, useValue: { paginatedSearchOptions: hot('a', { a: paginatedSearchOptions diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts index ff4948caa0..5e01494674 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.spec.ts @@ -228,8 +228,8 @@ describe('AuthNavMenuComponent', () => { fixture.destroy(); component = null; }); - it('should render logout dropdown menu', () => { - const logoutDropdownMenu = deNavMenuItem.query(By.css('ul[id=logoutDropdownMenu]')); + it('should render UserMenuComponent component', () => { + const logoutDropdownMenu = deNavMenuItem.query(By.css('ds-user-menu')); expect(logoutDropdownMenu.nativeElement).toBeDefined(); }); }) diff --git a/src/app/shared/mocks/mock-router.ts b/src/app/shared/mocks/mock-router.ts index 9ebe321cf6..f1357104b4 100644 --- a/src/app/shared/mocks/mock-router.ts +++ b/src/app/shared/mocks/mock-router.ts @@ -4,7 +4,10 @@ export class MockRouter { public events = observableOf({}); public routerState = { snapshot: { - url: '' + url: '', + root: { + queryParamMap: null + } } }; @@ -15,4 +18,8 @@ export class MockRouter { setRoute(route) { this.routerState.snapshot.url = route; } + + setParams(paramsMap) { + this.routerState.snapshot.root.queryParamMap = paramsMap; + } } diff --git a/src/app/shared/pagination/pagination.component.spec.ts b/src/app/shared/pagination/pagination.component.spec.ts index 7b340b2eed..dfbef9123a 100644 --- a/src/app/shared/pagination/pagination.component.spec.ts +++ b/src/app/shared/pagination/pagination.component.spec.ts @@ -262,7 +262,7 @@ describe('Pagination component', () => { changePage(testFixture, 3); tick(); - expect(routerStub.navigate).toHaveBeenCalledWith([], { queryParams: { pageId: 'test', page: 3, pageSize: 10, sortDirection: 'ASC', sortField: 'dc.title' }, queryParamsHandling: 'merge' }); + expect(routerStub.navigate).toHaveBeenCalledWith([], { queryParams: { pageId: 'test', page: '3', pageSize: 10, sortDirection: 'ASC', sortField: 'dc.title' }, queryParamsHandling: 'merge' }); })); diff --git a/src/app/shared/services/route.service.spec.ts b/src/app/shared/services/route.service.spec.ts index 7d249cb12a..c9b3710ee6 100644 --- a/src/app/shared/services/route.service.spec.ts +++ b/src/app/shared/services/route.service.spec.ts @@ -29,6 +29,9 @@ describe('RouteService', () => { select: jasmine.createSpy('select') }); + const router = new MockRouter(); + router.setParams(convertToParamMap(paramObject)); + paramObject[paramName1] = paramValue1; paramObject[paramName2] = [paramValue2a, paramValue2b]; @@ -42,7 +45,7 @@ describe('RouteService', () => { queryParamMap: observableOf(convertToParamMap(paramObject)) }, }, - { provide: Router, useValue: new MockRouter() }, + { provide: Router, useValue: router }, { provide: Store, useValue: store }, ] }); diff --git a/src/app/shared/testing/route-service-stub.ts b/src/app/shared/testing/route-service-stub.ts new file mode 100644 index 0000000000..e872d0703b --- /dev/null +++ b/src/app/shared/testing/route-service-stub.ts @@ -0,0 +1,26 @@ +import { of as observableOf } from 'rxjs/internal/observable/of'; + +export const routeServiceStub: any = { + /* tslint:disable:no-empty */ + hasQueryParamWithValue: (param: string, value: string) => { + }, + hasQueryParam: (param: string) => { + }, + removeQueryParameterValue: (param: string, value: string) => { + }, + addQueryParameterValue: (param: string, value: string) => { + }, + getQueryParameterValues: (param: string) => { + return observableOf({}); + }, + getQueryParamsWithPrefix: (param: string) => { + return observableOf({}); + }, + getQueryParamMap: () => { + return observableOf(new Map()) + }, + getQueryParameterValue: () => { + return observableOf({}) + } + /* tslint:enable:no-empty */ +}; diff --git a/src/app/shared/testing/search-configuration-service-stub.ts b/src/app/shared/testing/search-configuration-service-stub.ts new file mode 100644 index 0000000000..4c9d94c877 --- /dev/null +++ b/src/app/shared/testing/search-configuration-service-stub.ts @@ -0,0 +1,16 @@ +import { BehaviorSubject, of as observableOf } from 'rxjs'; + +export class SearchConfigurationServiceStub { + + private searchOptions: BehaviorSubject = new BehaviorSubject({}); + private paginatedSearchOptions: BehaviorSubject = new BehaviorSubject({}); + + getCurrentFrontendFilters() { + return observableOf([]); + } + + getCurrentScope(a) { + return observableOf('test-id') + } + +} From 237100a9cf6dab6530f269f564a52dc3d4186a52 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 27 Mar 2019 16:01:49 +0100 Subject: [PATCH 106/205] fixed after merge --- src/app/+my-dspace-page/my-dspace-page.component.ts | 2 +- src/app/+search-page/search-page.component.ts | 6 +++--- src/app/core/data/request.service.spec.ts | 2 +- .../workspaceitem/workspaceitem-actions.component.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts index a173a39d99..712a25b0c0 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.ts @@ -67,7 +67,7 @@ export class MyDSpacePageComponent implements OnInit { /** * The current relevant scopes */ - scopeListRD$: Observable; + scopeListRD$: Observable = new BehaviorSubject([]); /** * Emits true if were on a small screen diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 610811981c..b2a5b9a6eb 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -53,7 +53,7 @@ export class SearchPageComponent implements OnInit { /** * The current relevant scopes */ - scopeListRD$: Observable = new BehaviorSubject(null); + scopeListRD$: Observable = new BehaviorSubject([]); /** * Emits true if were on a small screen @@ -86,9 +86,9 @@ export class SearchPageComponent implements OnInit { .subscribe((results) => { this.resultsRD$.next(results); }); -/* this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( + this.scopeListRD$ = this.searchConfigService.getCurrentScope('').pipe( switchMap((scopeId) => this.service.getScopes(scopeId)) - );*/ + ); } /** diff --git a/src/app/core/data/request.service.spec.ts b/src/app/core/data/request.service.spec.ts index b83481fb24..9cce2509ef 100644 --- a/src/app/core/data/request.service.spec.ts +++ b/src/app/core/data/request.service.spec.ts @@ -1,7 +1,7 @@ import * as ngrx from '@ngrx/store'; import { ActionsSubject, Store } from '@ngrx/store'; import { cold, getTestScheduler, hot } from 'jasmine-marbles'; -import { EMPTY, of as observableOf } from 'rxjs'; +import { BehaviorSubject, EMPTY, of as observableOf } from 'rxjs'; import { getMockObjectCacheService } from '../../shared/mocks/mock-object-cache.service'; import { defaultUUID, getMockUUIDService } from '../../shared/mocks/mock-uuid.service'; import { ObjectCacheService } from '../cache/object-cache.service'; diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts index 05eee97d2a..c05eb557d2 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts @@ -7,7 +7,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model'; import { MyDSpaceActionsComponent } from '../mydspace-actions'; -import { SubmissionRestService } from '../../../submission/submission-rest.service'; +import { SubmissionRestService } from '../../../core/submission/submission-rest.service'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { ResourceType } from '../../../core/shared/resource-type'; import { NotificationsService } from '../../notifications/notifications.service'; From 96bb4e9bb4fef079dda61970ec703852deef802e Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 27 Mar 2019 16:04:00 +0100 Subject: [PATCH 107/205] Removed wrong initialization --- src/app/+my-dspace-page/my-dspace-page.component.ts | 2 +- src/app/+search-page/search-page.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-page.component.ts b/src/app/+my-dspace-page/my-dspace-page.component.ts index 712a25b0c0..a173a39d99 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.ts +++ b/src/app/+my-dspace-page/my-dspace-page.component.ts @@ -67,7 +67,7 @@ export class MyDSpacePageComponent implements OnInit { /** * The current relevant scopes */ - scopeListRD$: Observable = new BehaviorSubject([]); + scopeListRD$: Observable; /** * Emits true if were on a small screen diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index b2a5b9a6eb..333faacc47 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -53,7 +53,7 @@ export class SearchPageComponent implements OnInit { /** * The current relevant scopes */ - scopeListRD$: Observable = new BehaviorSubject([]); + scopeListRD$: Observable; /** * Emits true if were on a small screen From c44216652254920f157f2bf9501823719ef8921b Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 10:14:01 +0100 Subject: [PATCH 108/205] Added tests --- .../my-dspace-configuration.service.spec.ts | 253 ++++++++++++++++++ .../my-dspace-configuration.service.ts | 6 +- .../my-dspace-page.component.html | 2 +- .../my-dspace-page.component.spec.ts | 194 ++++++++++++++ .../my-dspace-results.component.spec.ts | 58 ++++ src/app/+my-dspace-page/my-dspace.guard.ts | 11 +- .../search-configuration.service.spec.ts | 15 ++ src/app/shared/mocks/mock-role-service.ts | 51 ++++ 8 files changed, 583 insertions(+), 7 deletions(-) create mode 100644 src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts create mode 100644 src/app/+my-dspace-page/my-dspace-page.component.spec.ts create mode 100644 src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts create mode 100644 src/app/shared/mocks/mock-role-service.ts diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts new file mode 100644 index 0000000000..84909777c4 --- /dev/null +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.spec.ts @@ -0,0 +1,253 @@ +import { of as observableOf } from 'rxjs'; + +import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; +import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; +import { SearchFilter } from '../+search-page/search-filter.model'; +import { ActivatedRouteStub } from '../shared/testing/active-router-stub'; +import { MockRoleService } from '../shared/mocks/mock-role-service'; +import { cold, hot } from 'jasmine-marbles'; +import { MyDSpaceConfigurationValueType } from './my-dspace-configuration-value-type'; + +describe('MyDSpaceConfigurationService', () => { + let service: MyDSpaceConfigurationService; + const value1 = 'random value'; + const prefixFilter = { + 'f.namedresourcetype': ['another value'], + 'f.dateSubmitted.min': ['2013'], + 'f.dateSubmitted.max': ['2018'] + }; + const defaults = new PaginatedSearchOptions({ + pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }), + sort: new SortOptions('score', SortDirection.DESC), + query: '', + scope: '' + }); + + const backendFilters = [new SearchFilter('f.namedresourcetype', ['another value']), new SearchFilter('f.dateSubmitted', ['[2013 TO 2018]'])]; + + const spy = jasmine.createSpyObj('RouteService', { + getQueryParameterValue: observableOf(value1), + getQueryParamsWithPrefix: observableOf(prefixFilter) + }); + + const activatedRoute: any = new ActivatedRouteStub(); + + const roleService: any = new MockRoleService(); + + beforeEach(() => { + service = new MyDSpaceConfigurationService(roleService, spy, activatedRoute); + }); + + describe('when the scope is called', () => { + beforeEach(() => { + service.getCurrentScope(''); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'scope\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('scope'); + }); + }); + + describe('when getCurrentConfiguration is called', () => { + beforeEach(() => { + service.getCurrentConfiguration(''); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'configuration\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('configuration'); + }); + }); + + describe('when getCurrentQuery is called', () => { + beforeEach(() => { + service.getCurrentQuery(''); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'query\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('query'); + }); + }); + + describe('when getCurrentDSOType is called', () => { + beforeEach(() => { + service.getCurrentDSOType(); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'dsoType\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('dsoType'); + }); + }); + + describe('when getCurrentFrontendFilters is called', () => { + beforeEach(() => { + service.getCurrentFrontendFilters(); + }); + it('should call getQueryParamsWithPrefix on the routeService with parameter prefix \'f.\'', () => { + expect((service as any).routeService.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.'); + }); + }); + + describe('when getCurrentFilters is called', () => { + let parsedValues$; + beforeEach(() => { + parsedValues$ = service.getCurrentFilters(); + }); + it('should call getQueryParamsWithPrefix on the routeService with parameter prefix \'f.\'', () => { + expect((service as any).routeService.getQueryParamsWithPrefix).toHaveBeenCalledWith('f.'); + parsedValues$.subscribe((values) => { + expect(values).toEqual(backendFilters); + }); + }); + }); + + describe('when getCurrentSort is called', () => { + beforeEach(() => { + service.getCurrentSort({} as any); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'sortDirection\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortDirection'); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'sortField\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField'); + }); + }); + + describe('when getCurrentPagination is called', () => { + beforeEach(() => { + service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'page\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('page'); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'pageSize\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize'); + }); + }); + + describe('when subscribeToSearchOptions or subscribeToPaginatedSearchOptions is called', () => { + beforeEach(() => { + spyOn(service, 'getCurrentPagination').and.callThrough(); + spyOn(service, 'getCurrentSort').and.callThrough(); + spyOn(service, 'getCurrentScope').and.callThrough(); + spyOn(service, 'getCurrentConfiguration').and.callThrough(); + spyOn(service, 'getCurrentQuery').and.callThrough(); + spyOn(service, 'getCurrentDSOType').and.callThrough(); + spyOn(service, 'getCurrentFilters').and.callThrough(); + }); + + describe('when subscribeToSearchOptions is called', () => { + beforeEach(() => { + (service as any).subscribeToSearchOptions(defaults) + }); + it('should call all getters it needs, but not call any others', () => { + expect(service.getCurrentPagination).not.toHaveBeenCalled(); + expect(service.getCurrentSort).not.toHaveBeenCalled(); + expect(service.getCurrentScope).toHaveBeenCalled(); + expect(service.getCurrentConfiguration).toHaveBeenCalled(); + expect(service.getCurrentQuery).toHaveBeenCalled(); + expect(service.getCurrentDSOType).toHaveBeenCalled(); + expect(service.getCurrentFilters).toHaveBeenCalled(); + }); + }); + + describe('when subscribeToPaginatedSearchOptions is called', () => { + beforeEach(() => { + (service as any).subscribeToPaginatedSearchOptions(defaults); + }); + it('should call all getters it needs', () => { + expect(service.getCurrentPagination).toHaveBeenCalled(); + expect(service.getCurrentSort).toHaveBeenCalled(); + expect(service.getCurrentScope).toHaveBeenCalled(); + expect(service.getCurrentConfiguration).toHaveBeenCalled(); + expect(service.getCurrentQuery).toHaveBeenCalled(); + expect(service.getCurrentDSOType).toHaveBeenCalled(); + expect(service.getCurrentFilters).toHaveBeenCalled(); + }); + }); + }); + + describe('when getAvailableConfigurationTypes is called', () => { + + it('should return properly list when user is submitter', () => { + roleService.setSubmitter(true); + roleService.setController(false); + roleService.setAdmin(false); + + const list$ = service.getAvailableConfigurationTypes(); + + expect(list$).toBeObservable(cold('(b|)', { + b: [ + MyDSpaceConfigurationValueType.Workspace + ] + })); + }); + + it('should return properly list when user is controller', () => { + roleService.setSubmitter(false); + roleService.setController(true); + roleService.setAdmin(false); + + const list$ = service.getAvailableConfigurationTypes(); + + expect(list$).toBeObservable(cold('(b|)', { + b: [ + MyDSpaceConfigurationValueType.Workflow + ] + })); + }); + + it('should return properly list when user is admin', () => { + roleService.setSubmitter(false); + roleService.setController(false); + roleService.setAdmin(true); + + const list$ = service.getAvailableConfigurationTypes(); + + expect(list$).toBeObservable(cold('(b|)', { + b: [ + MyDSpaceConfigurationValueType.Workflow + ] + })); + }); + + it('should return properly list when user is submitter and controller', () => { + roleService.setSubmitter(true); + roleService.setController(true); + roleService.setAdmin(false); + + const list$ = service.getAvailableConfigurationTypes(); + + expect(list$).toBeObservable(cold('(b|)', { + b: [ + MyDSpaceConfigurationValueType.Workspace, + MyDSpaceConfigurationValueType.Workflow + ] + })); + }); + }); + + describe('when getAvailableConfigurationOptions is called', () => { + + it('should return properly options list', () => { + spyOn(service, 'getAvailableConfigurationTypes').and.returnValue(hot('a', { + a: [ + MyDSpaceConfigurationValueType.Workspace, + MyDSpaceConfigurationValueType.Workflow + ] + })); + + const list$ = service.getAvailableConfigurationOptions(); + + expect(list$).toBeObservable(cold('(b|)', { + b: [ + { + value: MyDSpaceConfigurationValueType.Workspace, + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workspace}` + }, + { + value: MyDSpaceConfigurationValueType.Workflow, + label: `mydspace.show.${MyDSpaceConfigurationValueType.Workflow}` + } + ] + })); + }); + }); +}); diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts index a580ba9920..c5c37ae367 100644 --- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts @@ -13,7 +13,7 @@ import { PaginationComponentOptions } from '../shared/pagination/pagination-comp import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; /** - * Service that performs all actions that have to do with the current search configuration + * Service that performs all actions that have to do with the current mydspace configuration */ @Injectable() export class MyDSpaceConfigurationService extends SearchConfigurationService { @@ -97,8 +97,4 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { ) } - public getCurrentView(): Observable { - return this.routeService.getQueryParameterValue('view'); - } - } diff --git a/src/app/+my-dspace-page/my-dspace-page.component.html b/src/app/+my-dspace-page/my-dspace-page.component.html index b38edebd97..80b52a273c 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.html +++ b/src/app/+my-dspace-page/my-dspace-page.component.html @@ -3,7 +3,7 @@ (uploadEnd)="reload($event)">
    diff --git a/src/app/+my-dspace-page/my-dspace-page.component.spec.ts b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts new file mode 100644 index 0000000000..34053af3b9 --- /dev/null +++ b/src/app/+my-dspace-page/my-dspace-page.component.spec.ts @@ -0,0 +1,194 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { ActivatedRoute } from '@angular/router'; +import { By } from '@angular/platform-browser'; +import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { Store } from '@ngrx/store'; +import { TranslateModule } from '@ngx-translate/core'; +import { cold } from 'jasmine-marbles'; +import { of as observableOf } from 'rxjs'; + +import { SortDirection, SortOptions } from '../core/cache/models/sort-options.model'; +import { CommunityDataService } from '../core/data/community-data.service'; +import { HostWindowService } from '../shared/host-window.service'; +import { PaginationComponentOptions } from '../shared/pagination/pagination-component-options.model'; +import { RemoteData } from '../core/data/remote-data'; +import { MyDSpacePageComponent, SEARCH_CONFIG_SERVICE } from './my-dspace-page.component'; +import { RouteService } from '../shared/services/route.service'; +import { routeServiceStub } from '../shared/testing/route-service-stub'; +import { SearchConfigurationServiceStub } from '../shared/testing/search-configuration-service-stub'; +import { SearchService } from '../+search-page/search-service/search.service'; +import { SearchConfigurationService } from '../+search-page/search-service/search-configuration.service'; +import { PaginatedSearchOptions } from '../+search-page/paginated-search-options.model'; +import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service'; +import { SearchFilterService } from '../+search-page/search-filters/search-filter/search-filter.service'; +import { RoleDirective } from '../shared/roles/role.directive'; +import { RoleService } from '../core/roles/role.service'; +import { MockRoleService } from '../shared/mocks/mock-role-service'; + +describe('MyDSpacePageComponent', () => { + let comp: MyDSpacePageComponent; + let fixture: ComponentFixture; + let searchServiceObject: SearchService; + let searchConfigurationServiceObject: SearchConfigurationService; + const store: Store = jasmine.createSpyObj('store', { + /* tslint:disable:no-empty */ + dispatch: {}, + /* tslint:enable:no-empty */ + select: observableOf(true) + }); + const pagination: PaginationComponentOptions = new PaginationComponentOptions(); + pagination.id = 'mydspace-results-pagination'; + pagination.currentPage = 1; + pagination.pageSize = 10; + const sort: SortOptions = new SortOptions('score', SortDirection.DESC); + const mockResults = observableOf(new RemoteData(false, false, true, null, ['test', 'data'])); + const searchServiceStub = jasmine.createSpyObj('SearchService', { + search: mockResults, + getSearchLink: '/mydspace', + getScopes: observableOf(['test-scope']), + setServiceOptions: {} + }); + const configurationParam = 'default'; + const queryParam = 'test query'; + const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; + const paginatedSearchOptions = new PaginatedSearchOptions({ + configuration: configurationParam, + query: queryParam, + scope: scopeParam, + pagination, + sort + }); + const activatedRouteStub = { + snapshot: { + queryParamMap: new Map([ + ['query', queryParam], + ['scope', scopeParam] + ]) + }, + queryParams: observableOf({ + query: queryParam, + scope: scopeParam + }) + }; + const sidebarService = { + isCollapsed: observableOf(true), + collapse: () => this.isCollapsed = observableOf(true), + expand: () => this.isCollapsed = observableOf(false) + }; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, NgbCollapseModule.forRoot()], + declarations: [MyDSpacePageComponent, RoleDirective], + providers: [ + { provide: SearchService, useValue: searchServiceStub }, + { + provide: CommunityDataService, + useValue: jasmine.createSpyObj('communityService', ['findById', 'findAll']) + }, + { provide: ActivatedRoute, useValue: activatedRouteStub }, + { provide: RouteService, useValue: routeServiceStub }, + { + provide: Store, useValue: store + }, + { + provide: HostWindowService, useValue: jasmine.createSpyObj('hostWindowService', + { + isXs: observableOf(true), + isSm: observableOf(false), + isXsOrSm: observableOf(true) + }) + }, + { + provide: SearchSidebarService, + useValue: sidebarService + }, + { + provide: SearchFilterService, + useValue: {} + }, { + provide: SEARCH_CONFIG_SERVICE, + useValue: new SearchConfigurationServiceStub() + }, + { + provide: RoleService, + useValue: new MockRoleService() + } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MyDSpacePageComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MyDSpacePageComponent); + comp = fixture.componentInstance; // SearchPageComponent test instance + fixture.detectChanges(); + searchServiceObject = (comp as any).service; + searchConfigurationServiceObject = (comp as any).searchConfigService; + }); + + afterEach(() => { + comp = null; + searchServiceObject = null; + searchConfigurationServiceObject = null; + }); + + it('should get the scope and query from the route parameters', () => { + + searchConfigurationServiceObject.paginatedSearchOptions.next(paginatedSearchOptions); + expect(comp.searchOptions$).toBeObservable(cold('b', { + b: paginatedSearchOptions + })); + + }); + + describe('when the open sidebar button is clicked in mobile view', () => { + + beforeEach(() => { + spyOn(comp, 'openSidebar'); + const openSidebarButton = fixture.debugElement.query(By.css('.open-sidebar')); + openSidebarButton.triggerEventHandler('click', null); + }); + + it('should trigger the openSidebar function', () => { + expect(comp.openSidebar).toHaveBeenCalled(); + }); + + }); + + describe('when sidebarCollapsed is true in mobile view', () => { + let menu: HTMLElement; + + beforeEach(() => { + menu = fixture.debugElement.query(By.css('#mydspace-sidebar-sm')).nativeElement; + comp.isSidebarCollapsed = () => observableOf(true); + fixture.detectChanges(); + }); + + it('should close the sidebar', () => { + expect(menu.classList).not.toContain('active'); + }); + + }); + + describe('when sidebarCollapsed is false in mobile view', () => { + let menu: HTMLElement; + + beforeEach(() => { + menu = fixture.debugElement.query(By.css('#mydspace-sidebar-sm')).nativeElement; + comp.isSidebarCollapsed = () => observableOf(false); + fixture.detectChanges(); + }); + + it('should open the menu', () => { + expect(menu.classList).toContain('active'); + }); + + }); +}); diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts new file mode 100644 index 0000000000..67625706a6 --- /dev/null +++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.spec.ts @@ -0,0 +1,58 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +import { TranslateModule } from '@ngx-translate/core'; +import { QueryParamsDirectiveStub } from '../../shared/testing/query-params-directive-stub'; +import { MyDSpaceResultsComponent } from './my-dspace-results.component'; + +describe('MyDSpaceResultsComponent', () => { + let comp: MyDSpaceResultsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), NoopAnimationsModule], + declarations: [ + MyDSpaceResultsComponent, + QueryParamsDirectiveStub], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MyDSpaceResultsComponent); + comp = fixture.componentInstance; // MyDSpaceResultsComponent test instance + }); + + it('should display results when results are not empty', () => { + (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } }; + (comp as any).searchConfig = {}; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('ds-viewable-collection'))).not.toBeNull(); + }); + + it('should not display link when results are not empty', () => { + (comp as any).searchResults = { hasSucceeded: true, isLoading: false, payload: { page: { length: 2 } } }; + (comp as any).searchConfig = {}; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('a'))).toBeNull(); + }); + + it('should display error message if error is != 400', () => { + (comp as any).searchResults = { hasFailed: true, error: { statusCode: 500 } }; + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('ds-error'))).not.toBeNull(); + }); + + it('should display a message if search result is empty', () => { + (comp as any).searchResults = { payload: { page: { length: 0 } } }; + (comp as any).searchConfig = { query: 'foobar' }; + fixture.detectChanges(); + + const linkDes = fixture.debugElement.queryAll(By.css('text-muted')); + + expect(linkDes).toBeDefined() + }); +}); diff --git a/src/app/+my-dspace-page/my-dspace.guard.ts b/src/app/+my-dspace-page/my-dspace.guard.ts index c8cea6b272..9cb9aff485 100644 --- a/src/app/+my-dspace-page/my-dspace.guard.ts +++ b/src/app/+my-dspace-page/my-dspace.guard.ts @@ -22,7 +22,7 @@ export class MyDSpaceGuard implements CanActivate { } /** - * True when user is authenticated + * True when configuration is valid * @method canActivate */ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { @@ -31,6 +31,15 @@ export class MyDSpaceGuard implements CanActivate { map((configurationList) => this.validateConfigurationParam(route.queryParamMap.get('configuration'), configurationList))); } + /** + * Check if the given configuration is present in the list of those available + * + * @param configuration + * the configuration to validate + * @param configurationList + * the list of available configuration + * + */ private validateConfigurationParam(configuration: string, configurationList: MyDSpaceConfigurationValueType[]): boolean { const configurationDefault: string = configurationList[0]; if (isEmpty(configuration) || !configurationList.includes(configuration as MyDSpaceConfigurationValueType)) { diff --git a/src/app/+search-page/search-service/search-configuration.service.spec.ts b/src/app/+search-page/search-service/search-configuration.service.spec.ts index f1f4ef8bdc..f695bf268b 100644 --- a/src/app/+search-page/search-service/search-configuration.service.spec.ts +++ b/src/app/+search-page/search-service/search-configuration.service.spec.ts @@ -17,6 +17,7 @@ describe('SearchConfigurationService', () => { const defaults = new PaginatedSearchOptions({ pagination: Object.assign(new PaginationComponentOptions(), { currentPage: 1, pageSize: 20 }), sort: new SortOptions('score', SortDirection.DESC), + configuration: 'default', query: '', scope: '' }); @@ -43,6 +44,15 @@ describe('SearchConfigurationService', () => { }); }); + describe('when getCurrentConfiguration is called', () => { + beforeEach(() => { + service.getCurrentConfiguration(''); + }); + it('should call getQueryParameterValue on the routeService with parameter name \'configuration\'', () => { + expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('configuration'); + }); + }); + describe('when getCurrentQuery is called', () => { beforeEach(() => { service.getCurrentQuery(''); @@ -94,6 +104,7 @@ describe('SearchConfigurationService', () => { expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('sortField'); }); }); + describe('when getCurrentPagination is called', () => { beforeEach(() => { service.getCurrentPagination({ currentPage: 1, pageSize: 10 } as any); @@ -105,11 +116,13 @@ describe('SearchConfigurationService', () => { expect((service as any).routeService.getQueryParameterValue).toHaveBeenCalledWith('pageSize'); }); }); + describe('when subscribeToSearchOptions or subscribeToPaginatedSearchOptions is called', () => { beforeEach(() => { spyOn(service, 'getCurrentPagination').and.callThrough(); spyOn(service, 'getCurrentSort').and.callThrough(); spyOn(service, 'getCurrentScope').and.callThrough(); + spyOn(service, 'getCurrentConfiguration').and.callThrough(); spyOn(service, 'getCurrentQuery').and.callThrough(); spyOn(service, 'getCurrentDSOType').and.callThrough(); spyOn(service, 'getCurrentFilters').and.callThrough(); @@ -123,6 +136,7 @@ describe('SearchConfigurationService', () => { expect(service.getCurrentPagination).not.toHaveBeenCalled(); expect(service.getCurrentSort).not.toHaveBeenCalled(); expect(service.getCurrentScope).toHaveBeenCalled(); + expect(service.getCurrentConfiguration).toHaveBeenCalled(); expect(service.getCurrentQuery).toHaveBeenCalled(); expect(service.getCurrentDSOType).toHaveBeenCalled(); expect(service.getCurrentFilters).toHaveBeenCalled(); @@ -137,6 +151,7 @@ describe('SearchConfigurationService', () => { expect(service.getCurrentPagination).toHaveBeenCalled(); expect(service.getCurrentSort).toHaveBeenCalled(); expect(service.getCurrentScope).toHaveBeenCalled(); + expect(service.getCurrentConfiguration).toHaveBeenCalled(); expect(service.getCurrentQuery).toHaveBeenCalled(); expect(service.getCurrentDSOType).toHaveBeenCalled(); expect(service.getCurrentFilters).toHaveBeenCalled(); diff --git a/src/app/shared/mocks/mock-role-service.ts b/src/app/shared/mocks/mock-role-service.ts new file mode 100644 index 0000000000..dad296f986 --- /dev/null +++ b/src/app/shared/mocks/mock-role-service.ts @@ -0,0 +1,51 @@ +import { Observable } from 'rxjs/internal/Observable'; +import { RoleType } from '../../core/roles/role-types'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; + +export class MockRoleService { + + _isSubmitter = new BehaviorSubject(true); + _isController = new BehaviorSubject(true); + _isAdmin = new BehaviorSubject(true); + + setSubmitter(isSubmitter: boolean) { + this._isSubmitter.next(isSubmitter); + } + + setController(isController: boolean) { + this._isController.next(isController); + } + + setAdmin(isAdmin: boolean) { + this._isAdmin.next(isAdmin); + } + + isSubmitter(): Observable { + return this._isSubmitter; + } + + isController(): Observable { + return this._isController; + } + + isAdmin(): Observable { + return this._isAdmin; + } + + checkRole(role: RoleType): Observable { + let check: Observable; + switch (role) { + case RoleType.Submitter: + check = this.isSubmitter(); + break; + case RoleType.Controller: + check = this.isController(); + break; + case RoleType.Admin: + check = this.isAdmin(); + break; + } + + return check; + } +} From eeea331748266fd252105392e147add2d1086a46 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 13:23:01 +0100 Subject: [PATCH 109/205] Fixed issue with initialization of child class --- .../my-dspace-configuration.service.ts | 9 +++++++-- .../search-service/search-configuration.service.ts | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts index c5c37ae367..3ee91c5f31 100644 --- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts @@ -21,7 +21,7 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { * Default pagination settings */ protected defaultPagination = Object.assign(new PaginationComponentOptions(), { - id: 'mydspace-page-configuration', + id: 'mydspace-page', pageSize: 10, currentPage: 1 }); @@ -34,7 +34,7 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { /** * Default configuration parameter setting */ - protected defaultConfiguration = 'default'; + protected defaultConfiguration = 'workspace'; /** * Default scope setting @@ -62,6 +62,11 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { protected route: ActivatedRoute) { super(routeService, route); + + // override parent class initialization + this._defaults = null; + this.initDefaults(); + this.isSubmitter$ = this.roleService.isSubmitter(); this.isController$ = this.roleService.isController(); this.isAdmin$ = this.roleService.isAdmin(); diff --git a/src/app/+search-page/search-service/search-configuration.service.ts b/src/app/+search-page/search-service/search-configuration.service.ts index 39f1919f72..5d33d3dac7 100644 --- a/src/app/+search-page/search-service/search-configuration.service.ts +++ b/src/app/+search-page/search-service/search-configuration.service.ts @@ -59,7 +59,7 @@ export class SearchConfigurationService implements OnDestroy { /** * Emits the current default values */ - private _defaults: Observable>; + protected _defaults: Observable>; /** * Emits the current search options @@ -74,7 +74,7 @@ export class SearchConfigurationService implements OnDestroy { /** * List of subscriptions to unsubscribe from on destroy */ - private subs: Subscription[] = new Array(); + protected subs: Subscription[] = new Array(); /** * Initialize the search options @@ -83,6 +83,14 @@ export class SearchConfigurationService implements OnDestroy { */ constructor(protected routeService: RouteService, protected route: ActivatedRoute) { + + this.initDefaults(); + } + + /** + * Initialize the search options + */ + protected initDefaults() { this.defaults .pipe(getSucceededRemoteData()) .subscribe((defRD) => { From 6b40efdf992fd91e740386bf6ddf765d991a5f3c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 14:14:02 +0100 Subject: [PATCH 110/205] Fixed loadin spin icon --- .../approve/claimed-task-actions-approve.component.html | 2 +- .../reject/claimed-task-actions-reject.component.html | 4 ++-- .../pool-task/pool-task-actions.component.html | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html index 39b0f34a7b..1b6b1ea755 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html @@ -3,6 +3,6 @@ ngbTooltip="{{'submission.workflow.tasks.claimed.approve_help' | translate}}" [disabled]="processingApprove" (click)="click()"> - {{'submission.workflow.tasks.generic.processing' | translate}} + {{'submission.workflow.tasks.generic.processing' | translate}} {{'submission.workflow.tasks.claimed.approve' | translate}} diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html index 29835f2b40..7c7f4365b8 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html @@ -3,7 +3,7 @@ [ngbTooltip]="rejectTipContent" [disabled]="processingReject" (click)="openRejectModal(rejectModal)" > - {{'submission.workflow.tasks.generic.processing' | translate}} + {{'submission.workflow.tasks.generic.processing' | translate}} {{'submission.workflow.tasks.claimed.reject.submit' | translate}} @@ -32,7 +32,7 @@ class="btn btn-danger btn-lg btn-block mt-3" [disabled]="!rejectForm.valid || processingReject" type="submit"> - {{'submission.workflow.tasks.generic.processing' | translate}} + {{'submission.workflow.tasks.generic.processing' | translate}} {{'submission.workflow.tasks.claimed.reject.reason.submit' | translate}} diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html index faf1fcc37e..7ab3c12ea4 100644 --- a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html @@ -1,5 +1,3 @@ - - - - + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts index c3c27a53ae..123032b86f 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts @@ -1,20 +1,19 @@ -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; -import { Observable, Subscription } from 'rxjs'; -import { find, first } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { find } from 'rxjs/operators'; import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator'; import { MyDSpaceResultListElementComponent, } from '../my-dspace-result-list-element.component'; import { ViewMode } from '../../../../core/shared/view-mode.model'; import { RemoteData } from '../../../../core/data/remote-data'; -import { hasValue, isNotUndefined } from '../../../empty.util'; +import { isNotUndefined } from '../../../empty.util'; import { ListableObject } from '../../../object-collection/shared/listable-object.model'; import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model'; -import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; -import { MYDSPACE_ROUTE } from '../../../../+my-dspace-page/my-dspace-page.component'; @Component({ selector: 'ds-pooltask-my-dspace-result-list-element', @@ -24,11 +23,9 @@ import { MYDSPACE_ROUTE } from '../../../../+my-dspace-page/my-dspace-page.compo @renderElementsFor(PoolTaskMyDSpaceResult, ViewMode.List) @renderElementsFor(PoolTask, ViewMode.List) -export class PoolTaskMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent implements OnDestroy, OnInit { +export class PoolTaskMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent implements OnInit { public status = MyDspaceItemStatusType.WAITING_CONTROLLER; public workFlow: Workflowitem; - public viewMode: ViewMode = ViewMode.List; - private sub: Subscription; constructor(@Inject('objectElementProvider') public listable: ListableObject, @Inject('indexElementProvider') public index: number, @@ -48,34 +45,4 @@ export class PoolTaskMyDSpaceResultListElementComponent extends MyDSpaceResultLi this.workFlow = rd.payload; }); } - - switchView() { - this.viewMode = (this.viewMode === ViewMode.List) ? ViewMode.Detail : ViewMode.List; - } - - view() { - this.sub = this.route.queryParams.pipe( - first() - ).subscribe((params) => { - const pageSize = params.pageSize || 1; - const page = (pageSize * this.dsoIndex ) + 1; - - const navigationExtras: NavigationExtras = { - queryParams: { - view: ViewMode.Detail, - page, - pageSize - }, - queryParamsHandling: 'merge' - }; - this.router.navigate([MYDSPACE_ROUTE], navigationExtras); - }); - } - - ngOnDestroy() { - if (hasValue(this.sub)) { - this.sub.unsubscribe(); - } - } - } From 1a57fe703ab8d6e19386d37f80112a69537998d6 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 14:18:55 +0100 Subject: [PATCH 112/205] Added comments --- .../my-dspace-page.component.html | 2 +- .../+my-dspace-page/my-dspace-page.component.ts | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-page.component.html b/src/app/+my-dspace-page/my-dspace-page.component.html index 80b52a273c..4fecfa3209 100644 --- a/src/app/+my-dspace-page/my-dspace-page.component.html +++ b/src/app/+my-dspace-page/my-dspace-page.component.html @@ -1,5 +1,5 @@
    -
    = new InjectionToken('searchConfigurationService'); /** - * This component renders a simple item page. - * The route parameter 'id' is used to request the item it represents. - * All fields of the item that should be displayed, are defined in its template. + * This component represents the whole mydspace page */ - @Component({ selector: 'ds-my-dspace-page', styleUrls: ['./my-dspace-page.component.scss'], @@ -43,10 +40,6 @@ export const SEARCH_CONFIG_SERVICE: InjectionToken = } ] }) - -/** - * This component represents the whole mydspace page - */ export class MyDSpacePageComponent implements OnInit { /** @@ -79,8 +72,14 @@ export class MyDSpacePageComponent implements OnInit { */ sub: Subscription; + /** + * Variable for enumeration RoleType + */ roleTypeEnum = RoleType; + /** + * List of available view mode + */ viewModeList = [ViewMode.List, ViewMode.Detail]; constructor(private service: SearchService, @@ -92,6 +91,8 @@ export class MyDSpacePageComponent implements OnInit { } /** + * Initialize available configuration list + * * Listening to changes in the paginated search options * If something changes, update the search results * From 6616d759cb1649cd13577effbdca5448453b7881 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 14:47:03 +0100 Subject: [PATCH 113/205] Renamed mydspace results components and files --- .../+my-dspace-page/my-dspace-page.module.ts | 34 +++++++++---------- ...pace-result-detail-element.component.html} | 0 ...dspace-result-detail-element.component.ts} | 6 ++-- ...pace-result-detail-element.component.html} | 0 ...-dspace-result-detail-lement.component.ts} | 6 ++-- ...pace-result-detail-element.component.html} | 0 ...dspace-result-detail-element.component.ts} | 2 +- ...pace-result-detail-element.component.html} | 0 ...pace-result-detail-element.component.scss} | 0 ...dspace-result-detail-element.component.ts} | 4 +-- ...dspace-result-list-element.component.html} | 0 ...y-dspace-result-list-element.component.ts} | 6 ++-- ...dspace-result-list-element.component.html} | 0 ...y-dspace-result-list-element.component.ts} | 11 +++--- ...dspace-result-list-element.component.html} | 0 ...y-dspace-result-list-element.component.ts} | 2 +- ...dspace-result-list-element.component.html} | 0 ...dspace-result-list-element.component.scss} | 0 ...y-dspace-result-list-element.component.ts} | 4 +-- 19 files changed, 36 insertions(+), 39 deletions(-) rename src/app/shared/object-detail/my-dspace-result-detail-element/{ct-my-dspace-result/ct-my-dspace-result-detail-element.component.html => claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html} (100%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{ct-my-dspace-result/ct-my-dspace-result-detail-element.component.ts => claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts} (89%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{pt-my-dspace-result/pt-my-dspace-result-detail-element.component.html => pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html} (100%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{pt-my-dspace-result/pt-my-dspace-result-detail-lement.component.ts => pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts} (86%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.html => workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html} (100%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.ts => workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts} (96%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.html => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html} (100%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.scss => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.scss} (100%) rename src/app/shared/object-detail/my-dspace-result-detail-element/{wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.ts => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts} (92%) rename src/app/shared/object-list/my-dspace-result-list-element/{ct-my-dspace-result/ct-my-dspace-result-list-element.component.html => claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html} (100%) rename src/app/shared/object-list/my-dspace-result-list-element/{ct-my-dspace-result/ct-my-dspace-result-list-element.component.ts => claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts} (86%) rename src/app/shared/object-list/my-dspace-result-list-element/{pt-my-dspace-result/pt-my-dspace-result-list-element.component.html => pool-my-dspace-result/pool-my-dspace-result-list-element.component.html} (100%) rename src/app/shared/object-list/my-dspace-result-list-element/{pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts => pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts} (81%) rename src/app/shared/object-list/my-dspace-result-list-element/{wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.html => workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html} (100%) rename src/app/shared/object-list/my-dspace-result-list-element/{wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.ts => workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts} (95%) rename src/app/shared/object-list/my-dspace-result-list-element/{wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.html => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html} (100%) rename src/app/shared/object-list/my-dspace-result-list-element/{wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.scss => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss} (100%) rename src/app/shared/object-list/my-dspace-result-list-element/{wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.ts => workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts} (91%) diff --git a/src/app/+my-dspace-page/my-dspace-page.module.ts b/src/app/+my-dspace-page/my-dspace-page.module.ts index 4be59b4181..697c22dd0e 100644 --- a/src/app/+my-dspace-page/my-dspace-page.module.ts +++ b/src/app/+my-dspace-page/my-dspace-page.module.ts @@ -1,5 +1,5 @@ import { CommonModule } from '@angular/common'; -import { CUSTOM_ELEMENTS_SCHEMA, NgModule, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; @@ -7,17 +7,17 @@ import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module'; import { MyDSpacePageComponent } from './my-dspace-page.component'; import { SearchPageModule } from '../+search-page/search-page.module'; import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component'; -import { WorkspaceitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component'; +import { WorkspaceitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component'; import { ItemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component'; -import { WorkflowitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component'; -import { ClaimedTaskMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component'; -import { PoolTaskMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component'; +import { WorkflowitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component'; +import { ClaimedMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component'; +import { PoolMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component'; import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component'; import { ItemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component'; -import { WorkspaceitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component'; -import { WorkflowitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component'; -import { ClaimedTaskMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component'; -import { PoolTaskMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-lement.component'; +import { WorkspaceitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component'; +import { WorkflowitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component'; +import { ClaimedMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component'; +import { PoolMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component'; import { MyDSpaceGuard } from './my-dspace.guard'; import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'; @@ -34,13 +34,13 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service' ItemMyDSpaceResultListElementComponent, WorkspaceitemMyDSpaceResultListElementComponent, WorkflowitemMyDSpaceResultListElementComponent, - ClaimedTaskMyDSpaceResultListElementComponent, - PoolTaskMyDSpaceResultListElementComponent, + ClaimedMyDSpaceResultListElementComponent, + PoolMyDSpaceResultListElementComponent, ItemMyDSpaceResultDetailElementComponent, WorkspaceitemMyDSpaceResultDetailElementComponent, WorkflowitemMyDSpaceResultDetailElementComponent, - ClaimedTaskMyDSpaceResultDetailElementComponent, - PoolTaskMyDSpaceResultDetailElementComponent, + ClaimedMyDSpaceResultDetailElementComponent, + PoolMyDSpaceResultDetailElementComponent, MyDSpaceNewSubmissionComponent ], providers: [ @@ -51,13 +51,13 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service' ItemMyDSpaceResultListElementComponent, WorkspaceitemMyDSpaceResultListElementComponent, WorkflowitemMyDSpaceResultListElementComponent, - ClaimedTaskMyDSpaceResultListElementComponent, - PoolTaskMyDSpaceResultListElementComponent, + ClaimedMyDSpaceResultListElementComponent, + PoolMyDSpaceResultListElementComponent, ItemMyDSpaceResultDetailElementComponent, WorkspaceitemMyDSpaceResultDetailElementComponent, WorkflowitemMyDSpaceResultDetailElementComponent, - ClaimedTaskMyDSpaceResultDetailElementComponent, - PoolTaskMyDSpaceResultDetailElementComponent + ClaimedMyDSpaceResultDetailElementComponent, + PoolMyDSpaceResultDetailElementComponent ] }) export class MyDSpacePageModule { diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html similarity index 100% rename from src/app/shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component.html rename to src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts similarity index 89% rename from src/app/shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component.ts rename to src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts index 90531af2b0..4a5367d2a0 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/ct-my-dspace-result/ct-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts @@ -19,15 +19,15 @@ import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; @Component({ - selector: 'ds-claimtask-my-dspace-result-detail-element', + selector: 'ds-claimed-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], - templateUrl: './ct-my-dspace-result-detail-element.component.html', + templateUrl: './claimed-my-dspace-result-detail-element.component.html', providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}] }) @renderElementsFor(ClaimedTaskMyDSpaceResult, ViewMode.Detail) @renderElementsFor(ClaimedTask, ViewMode.Detail) -export class ClaimedTaskMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { +export class ClaimedMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { public status = MyDspaceItemStatusType.VALIDATION; public workFlow: Workflowitem; public rejectForm: FormGroup; diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html similarity index 100% rename from src/app/shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-element.component.html rename to src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-lement.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts similarity index 86% rename from src/app/shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-lement.component.ts rename to src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts index 28ebefe633..1917a5f1af 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pt-my-dspace-result/pt-my-dspace-result-detail-lement.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts @@ -15,14 +15,14 @@ import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; @Component({ - selector: 'ds-pooltask-my-dspace-result-detail-element', + selector: 'ds-pool-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], - templateUrl: './pt-my-dspace-result-detail-element.component.html', + templateUrl: './pool-my-dspace-result-detail-element.component.html', }) @renderElementsFor(PoolTaskMyDSpaceResult, ViewMode.Detail) @renderElementsFor(PoolTask, ViewMode.Detail) -export class PoolTaskMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { +export class PoolMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { public status = MyDspaceItemStatusType.WAITING_CONTROLLER; public workFlow: Workflowitem; diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html similarity index 100% rename from src/app/shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.html rename to src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.html diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts similarity index 96% rename from src/app/shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.ts rename to src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts index 865e9c4fec..c76ad84b22 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/wfi-my-dspace-result/wfi-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts @@ -16,7 +16,7 @@ import { isNotUndefined } from '../../../empty.util'; @Component({ selector: 'ds-workflowitem-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], - templateUrl: './wfi-my-dspace-result-detail-element.component.html', + templateUrl: './workflowitem-my-dspace-result-detail-element.component.html', }) @renderElementsFor(WorkflowitemMyDSpaceResult, ViewMode.Detail) diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html similarity index 100% rename from src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.html rename to src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.html diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.scss b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.scss similarity index 100% rename from src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.scss rename to src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.scss diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts similarity index 92% rename from src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.ts rename to src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts index c600ba9773..4a1635ec5d 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/wsi-my-dspace-result/wsi-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts @@ -16,8 +16,8 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @Component({ selector: 'ds-workspaceitem-my-dspace-result-detail-element', - styleUrls: ['../my-dspace-result-detail-element.component.scss', './wsi-my-dspace-result-detail-element.component.scss'], - templateUrl: './wsi-my-dspace-result-detail-element.component.html', + styleUrls: ['../my-dspace-result-detail-element.component.scss', './workspaceitem-my-dspace-result-detail-element.component.scss'], + templateUrl: './workspaceitem-my-dspace-result-detail-element.component.html', }) @renderElementsFor(WorkspaceitemMyDSpaceResult, ViewMode.Detail) diff --git a/src/app/shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html similarity index 100% rename from src/app/shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component.html rename to src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts similarity index 86% rename from src/app/shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component.ts rename to src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts index 36626e1968..c37973ae11 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/ct-my-dspace-result/ct-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts @@ -15,15 +15,15 @@ import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/cla import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; @Component({ - selector: 'ds-claimtask-my-dspace-result-list-element', + selector: 'ds-claimed-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], - templateUrl: './ct-my-dspace-result-list-element.component.html', + templateUrl: './claimed-my-dspace-result-list-element.component.html', providers: [Location, { provide: LocationStrategy, useClass: PathLocationStrategy }] }) @renderElementsFor(ClaimedTaskMyDSpaceResult, ViewMode.List) @renderElementsFor(ClaimedTask, ViewMode.List) -export class ClaimedTaskMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { +export class ClaimedMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { public showSubmitter = true; public status = MyDspaceItemStatusType.VALIDATION; public workFlow: Workflowitem; diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html similarity index 100% rename from src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.html rename to src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts similarity index 81% rename from src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts rename to src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts index 123032b86f..443d5d7e8c 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pt-my-dspace-result/pt-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts @@ -12,25 +12,22 @@ import { ListableObject } from '../../../object-collection/shared/listable-objec import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model'; -import { ActivatedRoute, Router } from '@angular/router'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; @Component({ - selector: 'ds-pooltask-my-dspace-result-list-element', + selector: 'ds-pool-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], - templateUrl: './pt-my-dspace-result-list-element.component.html', + templateUrl: './pool-my-dspace-result-list-element.component.html', }) @renderElementsFor(PoolTaskMyDSpaceResult, ViewMode.List) @renderElementsFor(PoolTask, ViewMode.List) -export class PoolTaskMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent implements OnInit { +export class PoolMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent implements OnInit { public status = MyDspaceItemStatusType.WAITING_CONTROLLER; public workFlow: Workflowitem; constructor(@Inject('objectElementProvider') public listable: ListableObject, - @Inject('indexElementProvider') public index: number, - private route: ActivatedRoute, - private router: Router) { + @Inject('indexElementProvider') public index: number) { super(listable, index); } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html similarity index 100% rename from src/app/shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.html rename to src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts similarity index 95% rename from src/app/shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.ts rename to src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts index a0f1e8decd..7fad9d7677 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/wfi-my-dspace-result/wfi-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts @@ -16,7 +16,7 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @Component({ selector: 'ds-workflowitem-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], - templateUrl: './wfi-my-dspace-result-list-element.component.html', + templateUrl: './workflowitem-my-dspace-result-list-element.component.html', }) @renderElementsFor(WorkflowitemMyDSpaceResult, ViewMode.List) diff --git a/src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html similarity index 100% rename from src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.html rename to src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss similarity index 100% rename from src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.scss rename to src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.scss diff --git a/src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts similarity index 91% rename from src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.ts rename to src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts index 7a19f54a10..b0df02c64c 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/wsi-my-dspace-result/wsi-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts @@ -15,8 +15,8 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @Component({ selector: 'ds-workspaceitem-my-dspace-result-list-element', - styleUrls: ['../my-dspace-result-list-element.component.scss', './wsi-my-dspace-result-list-element.component.scss'], - templateUrl: './wsi-my-dspace-result-list-element.component.html', + styleUrls: ['../my-dspace-result-list-element.component.scss', './workspaceitem-my-dspace-result-list-element.component.scss'], + templateUrl: './workspaceitem-my-dspace-result-list-element.component.html', }) @renderElementsFor(WorkspaceitemMyDSpaceResult, ViewMode.List) From 7b63d145d85061cffa0b5b222c7f27102eeef314 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 28 Mar 2019 19:33:40 +0100 Subject: [PATCH 114/205] Added more tests and comments --- ...space-result-detail-element.component.html | 10 +- ...ce-result-detail-element.component.spec.ts | 89 ++++++++++++ ...-dspace-result-detail-element.component.ts | 43 +++--- .../item-detail-preview.component.html | 33 +---- .../item-detail-preview.component.spec.ts | 90 ++++++++++++ .../item-detail-preview.component.ts | 31 ++++- ...ce-result-detail-element.component.spec.ts | 78 +++++++++++ ...-dspace-result-detail-element.component.ts | 6 + ...space-result-detail-element.component.html | 7 +- ...ce-result-detail-element.component.spec.ts | 89 ++++++++++++ ...y-dspace-result-detail-lement.component.ts | 27 +++- ...ce-result-detail-element.component.spec.ts | 86 ++++++++++++ ...-dspace-result-detail-element.component.ts | 16 +++ ...ce-result-detail-element.component.spec.ts | 86 ++++++++++++ ...-dspace-result-detail-element.component.ts | 17 +++ .../item-list-preview.component.scss | 5 - ...-dspace-result-list-element.component.html | 6 +- ...pace-result-list-element.component.spec.ts | 89 ++++++++++++ ...my-dspace-result-list-element.component.ts | 29 +++- .../item-list-preview.component.html | 0 .../item-list-preview.component.scss | 5 + .../item-list-preview.component.spec.ts | 131 ++++++++++++++++++ .../item-list-preview.component.ts | 28 +++- ...pace-result-list-element.component.spec.ts | 78 +++++++++++ ...my-dspace-result-list-element.component.ts | 6 + ...-dspace-result-list-element.component.html | 6 +- ...pace-result-list-element.component.spec.ts | 89 ++++++++++++ ...my-dspace-result-list-element.component.ts | 26 +++- ...pace-result-list-element.component.spec.ts | 86 ++++++++++++ ...my-dspace-result-list-element.component.ts | 17 +++ ...pace-result-list-element.component.spec.ts | 86 ++++++++++++ ...my-dspace-result-list-element.component.ts | 16 +++ src/app/shared/shared.module.ts | 2 +- 33 files changed, 1326 insertions(+), 87 deletions(-) create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts create mode 100644 src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts delete mode 100644 src/app/shared/object-list/item-list-preview/item-list-preview.component.scss create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts rename src/app/shared/object-list/{ => my-dspace-result-list-element}/item-list-preview/item-list-preview.component.html (100%) create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts rename src/app/shared/object-list/{ => my-dspace-result-list-element}/item-list-preview/item-list-preview.component.ts (69%) create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts create mode 100644 src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html index 134b85523e..d4ecaaa332 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.html @@ -1,6 +1,8 @@ - + [showSubmitter]="showSubmitter" + [status]="status"> + - + diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts new file mode 100644 index 0000000000..38c4f461cd --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts @@ -0,0 +1,89 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ClaimedMyDSpaceResultDetailElementComponent } from './claimed-my-dspace-result-detail-element.component'; +import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; + +let component: ClaimedMyDSpaceResultDetailElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: ClaimedTaskMyDSpaceResult = new ClaimedTaskMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rdItem = new RemoteData(false, false, true, null, item); +const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject.dspaceObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('ClaimedMyDSpaceResultDetailElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ClaimedMyDSpaceResultDetailElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedMyDSpaceResultDetailElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ClaimedMyDSpaceResultDetailElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.workflowitem).toEqual(workflowitem); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts index 4a5367d2a0..29e8e907ad 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.ts @@ -11,47 +11,56 @@ import { ListableObject } from '../../../object-collection/shared/listable-objec import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model'; -import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common'; import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders claimed task object for the mydspace result in the detail view. + */ @Component({ selector: 'ds-claimed-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], - templateUrl: './claimed-my-dspace-result-detail-element.component.html', - providers: [Location, {provide: LocationStrategy, useClass: PathLocationStrategy}] + templateUrl: './claimed-my-dspace-result-detail-element.component.html' }) @renderElementsFor(ClaimedTaskMyDSpaceResult, ViewMode.Detail) @renderElementsFor(ClaimedTask, ViewMode.Detail) export class ClaimedMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { + + /** + * A boolean representing if to show submitter information + */ + public showSubmitter = true; + + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.VALIDATION; - public workFlow: Workflowitem; - public rejectForm: FormGroup; - constructor(private ctDataService: ClaimedTaskDataService, - private modalService: NgbModal, - private formBuilder: FormBuilder, - @Inject('objectElementProvider') public listable: ListableObject) { + /** + * The workflowitem object that belonging to the result object + */ + public workflowitem: Workflowitem; + + constructor(@Inject('objectElementProvider') public listable: ListableObject) { super(listable); - - this.rejectForm = this.formBuilder.group({ - reason: ['', Validators.required] - }); } + /** + * Initialize all instance variables + */ ngOnInit() { this.initWorkflowItem(this.dso.workflowitem as Observable>); } + /** + * Retrieve workflowitem from result object + */ initWorkflowItem(wfi$: Observable>) { wfi$.pipe( find((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))) ).subscribe((rd: RemoteData) => { - this.workFlow = rd.payload; + this.workflowitem = rd.payload; }); } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html index 4f9d8f3e94..3cb4665884 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html @@ -24,35 +24,4 @@
    - - - - - - + diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts new file mode 100644 index 0000000000..5823e9969b --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.spec.ts @@ -0,0 +1,90 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { Item } from '../../../../core/shared/item.model'; +import { ItemDetailPreviewComponent } from './item-detail-preview.component'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +let component: ItemDetailPreviewComponent; +let fixture: ComponentFixture; + +const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ] + } +}); + +describe('ItemDetailPreviewComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), + ], + declarations: [ItemDetailPreviewComponent, TruncatePipe], + providers: [ + { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate } } + + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemDetailPreviewComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemDetailPreviewComponent); + component = fixture.componentInstance; + + })); + + beforeEach(() => { + component.object = { hitHighlights: {} }; + component.item = mockItemWithAuthorAndDate; + fixture.detectChanges(); + }); + + it('should init thumbnail on init', () => { + expect(component.thumbnail$).toBeDefined(); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts index 9b42bb2268..9f92f79859 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts @@ -7,25 +7,46 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa import { fadeInOut } from '../../../animations/fade'; import { Bitstream } from '../../../../core/shared/bitstream.model'; +/** + * This component show metadata for the given item object in the detail view. + */ @Component({ selector: 'ds-item-detail-preview', styleUrls: ['./item-detail-preview.component.scss'], templateUrl: './item-detail-preview.component.html', animations: [fadeInOut] }) -export class ItemDetailPreviewComponent { +export class ItemDetailPreviewComponent { + /** + * The item to display + */ @Input() item: Item; + + /** + * The mydspace result object + */ @Input() object: any; + + /** + * Represent item's status + */ @Input() status: MyDspaceItemStatusType; - public ALL_STATUS = []; + /** + * A boolean representing if to show submitter information + */ + @Input() showSubmitter = false; + + /** + * The item's thumbnail + */ public thumbnail$: Observable; + /** + * Initialize all instance variables + */ ngOnInit() { - Object.keys(MyDspaceItemStatusType).forEach((s) => { - this.ALL_STATUS.push(MyDspaceItemStatusType[s]); - }); this.thumbnail$ = this.item.getThumbnail(); } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts new file mode 100644 index 0000000000..c48d755d61 --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts @@ -0,0 +1,78 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ItemMyDSpaceResultDetailElementComponent } from './item-my-dspace-result-detail-element.component'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model'; + +let component: ItemMyDSpaceResultDetailElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +mockResultObject.dspaceObject = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); + +describe('ItemMyDSpaceResultDetailElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ItemMyDSpaceResultDetailElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemMyDSpaceResultDetailElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemMyDSpaceResultDetailElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.ACCEPTED); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts index 75bfb30939..a55d911581 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.ts @@ -7,6 +7,9 @@ import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-ds import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders item object for the mydspace result in the detail view. + */ @Component({ selector: 'ds-workspaceitem-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss', './item-my-dspace-result-detail-element.component.scss'], @@ -16,6 +19,9 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(ItemMyDSpaceResult, ViewMode.Detail) export class ItemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.ACCEPTED; } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html index 8d808815c2..96ef68e3f8 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.html @@ -1,6 +1,7 @@ - - + diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts new file mode 100644 index 0000000000..b108d48c33 --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts @@ -0,0 +1,89 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { PoolMyDSpaceResultDetailElementComponent } from './pool-my-dspace-result-detail-lement.component'; +import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model'; +import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; + +let component: PoolMyDSpaceResultDetailElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: PoolTaskMyDSpaceResult = new PoolTaskMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rdItem = new RemoteData(false, false, true, null, item); +const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject.dspaceObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('PoolMyDSpaceResultDetailElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [PoolMyDSpaceResultDetailElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PoolMyDSpaceResultDetailElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PoolMyDSpaceResultDetailElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.workflowitem).toEqual(workflowitem); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts index 1917a5f1af..fbc37c9525 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component.ts @@ -14,6 +14,9 @@ import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-t import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders pool task object for the mydspace result in the detail view. + */ @Component({ selector: 'ds-pool-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], @@ -23,23 +26,41 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(PoolTaskMyDSpaceResult, ViewMode.Detail) @renderElementsFor(PoolTask, ViewMode.Detail) export class PoolMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { + + /** + * A boolean representing if to show submitter information + */ + public showSubmitter = true; + + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.WAITING_CONTROLLER; - public workFlow: Workflowitem; + + /** + * The workflowitem object that belonging to the result object + */ + public workflowitem: Workflowitem; constructor(@Inject('objectElementProvider') public listable: ListableObject) { - super(listable); } + /** + * Initialize all instance variables + */ ngOnInit() { this.initWorkflowItem(this.dso.workflowitem as Observable>); } + /** + * Retrieve workflowitem from result object + */ initWorkflowItem(wfi$: Observable>) { wfi$.pipe( find((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))) ).subscribe((rd: RemoteData) => { - this.workFlow = rd.payload; + this.workflowitem = rd.payload; }); } diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts new file mode 100644 index 0000000000..3c6505218e --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts @@ -0,0 +1,86 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { WorkflowitemMyDSpaceResultDetailElementComponent } from './workflowitem-my-dspace-result-detail-element.component'; +import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; + +let component: WorkflowitemMyDSpaceResultDetailElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: WorkflowitemMyDSpaceResult = new WorkflowitemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockResultObject.dspaceObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); + +describe('WorkflowitemMyDSpaceResultDetailElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [WorkflowitemMyDSpaceResultDetailElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkflowitemMyDSpaceResultDetailElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(WorkflowitemMyDSpaceResultDetailElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.item).toEqual(item); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts index c76ad84b22..32e005309d 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.ts @@ -13,6 +13,9 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { find } from 'rxjs/operators'; import { isNotUndefined } from '../../../empty.util'; +/** + * This component renders workflowitem object for the mydspace result in the detail view. + */ @Component({ selector: 'ds-workflowitem-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss'], @@ -23,17 +26,30 @@ import { isNotUndefined } from '../../../empty.util'; @renderElementsFor(Workflowitem, ViewMode.Detail) export class WorkflowitemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { + /** + * The item object that belonging to the result object + */ public item: Item; + + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.WORKFLOW; constructor(@Inject('objectElementProvider') public listable: ListableObject) { super(listable); } + /** + * Initialize all instance variables + */ ngOnInit() { this.initItem(this.dso.item as Observable>); } + /** + * Retrieve item from result object + */ initItem(item$: Observable>) { item$.pipe( find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts new file mode 100644 index 0000000000..830ab80cf1 --- /dev/null +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts @@ -0,0 +1,86 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { WorkspaceitemMyDSpaceResultDetailElementComponent } from './workspaceitem-my-dspace-result-detail-element.component'; +import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model'; +import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; + +let component: WorkspaceitemMyDSpaceResultDetailElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: WorkspaceitemMyDSpaceResult = new WorkspaceitemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockResultObject.dspaceObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); + +describe('WorkspaceitemMyDSpaceResultDetailElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [WorkspaceitemMyDSpaceResultDetailElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkspaceitemMyDSpaceResultDetailElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(WorkspaceitemMyDSpaceResultDetailElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.item).toEqual(item); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.IN_PROGRESS); + }); +}); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts index 4a1635ec5d..ca6ed59c12 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.ts @@ -14,6 +14,9 @@ import { ListableObject } from '../../../object-collection/shared/listable-objec import { MyDSpaceResultDetailElementComponent } from '../my-dspace-result-detail-element.component'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders workspaceitem object for the mydspace result in the detail view. + */ @Component({ selector: 'ds-workspaceitem-my-dspace-result-detail-element', styleUrls: ['../my-dspace-result-detail-element.component.scss', './workspaceitem-my-dspace-result-detail-element.component.scss'], @@ -23,17 +26,31 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(WorkspaceitemMyDSpaceResult, ViewMode.Detail) @renderElementsFor(Workspaceitem, ViewMode.Detail) export class WorkspaceitemMyDSpaceResultDetailElementComponent extends MyDSpaceResultDetailElementComponent { + + /** + * The item object that belonging to the result object + */ public item: Item; + + /** + * Represent item's status + */ status = MyDspaceItemStatusType.IN_PROGRESS; constructor(@Inject('objectElementProvider') public listable: ListableObject) { super(listable); } + /** + * Initialize all instance variables + */ ngOnInit() { this.initItem(this.dso.item as Observable>); } + /** + * Retrieve item from result object + */ initItem(item$: Observable>) { item$.pipe( find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) diff --git a/src/app/shared/object-list/item-list-preview/item-list-preview.component.scss b/src/app/shared/object-list/item-list-preview/item-list-preview.component.scss deleted file mode 100644 index 8d0339f264..0000000000 --- a/src/app/shared/object-list/item-list-preview/item-list-preview.component.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import '../../../../styles/_variables.scss'; - -.h3-title { - color: $link-color; -} diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html index 5ec6e59f95..5ec4c9a5b5 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.html @@ -1,7 +1,7 @@ - - + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts new file mode 100644 index 0000000000..c79812d10b --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts @@ -0,0 +1,89 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ClaimedMyDSpaceResultListElementComponent } from './claimed-my-dspace-result-list-element.component'; +import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; + +let component: ClaimedMyDSpaceResultListElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: ClaimedTaskMyDSpaceResult = new ClaimedTaskMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rdItem = new RemoteData(false, false, true, null, item); +const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject.dspaceObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('ClaimedMyDSpaceResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ClaimedMyDSpaceResultListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedMyDSpaceResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ClaimedMyDSpaceResultListElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.workflowitem).toEqual(workflowitem); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.VALIDATION); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts index c37973ae11..5f2cd2495e 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.ts @@ -14,6 +14,9 @@ import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.m import { ClaimedTaskMyDSpaceResult } from '../../../object-collection/shared/claimed-task-my-dspace-result.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders claimed task object for the mydspace result in the list view. + */ @Component({ selector: 'ds-claimed-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], @@ -24,19 +27,37 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(ClaimedTaskMyDSpaceResult, ViewMode.List) @renderElementsFor(ClaimedTask, ViewMode.List) export class ClaimedMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { - public showSubmitter = true; - public status = MyDspaceItemStatusType.VALIDATION; - public workFlow: Workflowitem; + /** + * A boolean representing if to show submitter information + */ + public showSubmitter = true; + + /** + * Represent item's status + */ + public status = MyDspaceItemStatusType.VALIDATION; + + /** + * The workflowitem object that belonging to the result object + */ + public workflowitem: Workflowitem; + + /** + * Initialize all instance variables + */ ngOnInit() { this.initWorkflowItem(this.dso.workflowitem as Observable>); } + /** + * Retrieve workflowitem from result object + */ initWorkflowItem(wfi$: Observable>) { wfi$.pipe( find((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))) ).subscribe((rd: RemoteData) => { - this.workFlow = rd.payload; + this.workflowitem = rd.payload; }); } } diff --git a/src/app/shared/object-list/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html similarity index 100% rename from src/app/shared/object-list/item-list-preview/item-list-preview.component.html rename to src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss new file mode 100644 index 0000000000..949e96d484 --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.scss @@ -0,0 +1,5 @@ +@import '../../../../../styles/variables'; + +.h3-title { + color: $link-color; +} diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts new file mode 100644 index 0000000000..9b917b56e4 --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.spec.ts @@ -0,0 +1,131 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +import { of as observableOf } from 'rxjs'; + +import { TruncatePipe } from '../../../utils/truncate.pipe'; +import { Item } from '../../../../core/shared/item.model'; +import { ItemListPreviewComponent } from './item-list-preview.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; + +let component: ItemListPreviewComponent; +let fixture: ComponentFixture; + +const mockItemWithAuthorAndDate: Item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const mockItemWithoutAuthorAndDate: Item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ] + } +}); + +describe('ItemListPreviewComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }), + ], + declarations: [ItemListPreviewComponent, TruncatePipe], + providers: [ + { provide: 'objectElementProvider', useValue: { mockItemWithAuthorAndDate } } + + ], + + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemListPreviewComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemListPreviewComponent); + component = fixture.componentInstance; + + })); + + beforeEach(() => { + component.object = { hitHighlights: {} }; + }); + + describe('When the item has an author', () => { + beforeEach(() => { + component.item = mockItemWithAuthorAndDate; + fixture.detectChanges(); + }); + + it('should show the author paragraph', () => { + const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); + expect(itemAuthorField).not.toBeNull(); + }); + }); + + describe('When the item has no author', () => { + beforeEach(() => { + component.item = mockItemWithoutAuthorAndDate; + fixture.detectChanges(); + }); + + it('should not show the author paragraph', () => { + const itemAuthorField = fixture.debugElement.query(By.css('span.item-list-authors')); + expect(itemAuthorField).toBeNull(); + }); + }); + + describe('When the item has an issuedate', () => { + beforeEach(() => { + component.item = mockItemWithAuthorAndDate; + fixture.detectChanges(); + }); + + it('should show the issuedate span', () => { + const dateField = fixture.debugElement.query(By.css('span.item-list-date')); + expect(dateField).not.toBeNull(); + }); + }); + + describe('When the item has no issuedate', () => { + beforeEach(() => { + component.item = mockItemWithoutAuthorAndDate; + fixture.detectChanges(); + }); + + it('should show the issuedate empty placeholder', () => { + const dateField = fixture.debugElement.query(By.css('span.item-list-date')); + expect(dateField).not.toBeNull(); + }); + }); +}); diff --git a/src/app/shared/object-list/item-list-preview/item-list-preview.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts similarity index 69% rename from src/app/shared/object-list/item-list-preview/item-list-preview.component.ts rename to src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts index 0a853b9c5e..5112069071 100644 --- a/src/app/shared/object-list/item-list-preview/item-list-preview.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.ts @@ -1,21 +1,39 @@ import { Component, Input } from '@angular/core'; -import { Item } from '../../../core/shared/item.model'; -import { fadeInOut } from '../../animations/fade'; -import { MyDspaceItemStatusType } from '../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; -import { Metadata } from '../../../core/shared/metadata.utils'; +import { Item } from '../../../../core/shared/item.model'; +import { fadeInOut } from '../../../animations/fade'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { Metadata } from '../../../../core/shared/metadata.utils'; +/** + * This component show metadata for the given item object in the list view. + */ @Component({ selector: 'ds-item-list-preview', styleUrls: ['item-list-preview.component.scss'], templateUrl: 'item-list-preview.component.html', animations: [fadeInOut] }) - export class ItemListPreviewComponent { + + /** + * The item to display + */ @Input() item: Item; + + /** + * The mydspace result object + */ @Input() object: any; + + /** + * Represent item's status + */ @Input() status: MyDspaceItemStatusType; + + /** + * A boolean representing if to show submitter information + */ @Input() showSubmitter = false; /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts new file mode 100644 index 0000000000..bcd323ae2a --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts @@ -0,0 +1,78 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { ItemMyDSpaceResultListElementComponent } from './item-my-dspace-result-list-element.component'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model'; + +let component: ItemMyDSpaceResultListElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +mockResultObject.dspaceObject = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); + +describe('ItemMyDSpaceResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ItemMyDSpaceResultListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemMyDSpaceResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemMyDSpaceResultListElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.ACCEPTED); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts index 484ca9af11..faeeed8ea2 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.ts @@ -7,6 +7,9 @@ import { Item } from '../../../../core/shared/item.model'; import { ItemMyDSpaceResult } from '../../../object-collection/shared/item-my-dspace-result.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders item object for the mydspace result in the list view. + */ @Component({ selector: 'ds-workspaceitem-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss', './item-my-dspace-result-list-element.component.scss'], @@ -16,6 +19,9 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(ItemMyDSpaceResult, ViewMode.List) export class ItemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.ACCEPTED; } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html index b6771db087..5f0f1bb6d4 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.html @@ -1,7 +1,7 @@ - diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts new file mode 100644 index 0000000000..3b2545e069 --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts @@ -0,0 +1,89 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { PoolMyDSpaceResultListElementComponent } from './pool-my-dspace-result-list-element.component'; +import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model'; +import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; + +let component: PoolMyDSpaceResultListElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: PoolTaskMyDSpaceResult = new PoolTaskMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rdItem = new RemoteData(false, false, true, null, item); +const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject.dspaceObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('PoolMyDSpaceResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [PoolMyDSpaceResultListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PoolMyDSpaceResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(PoolMyDSpaceResultListElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.workflowitem).toEqual(workflowitem); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.WAITING_CONTROLLER); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts index 443d5d7e8c..36b95271c7 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.ts @@ -14,6 +14,9 @@ import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; import { PoolTaskMyDSpaceResult } from '../../../object-collection/shared/pool-task-my-dspace-result.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders pool task object for the mydspace result in the list view. + */ @Component({ selector: 'ds-pool-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], @@ -23,23 +26,42 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(PoolTaskMyDSpaceResult, ViewMode.List) @renderElementsFor(PoolTask, ViewMode.List) export class PoolMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent implements OnInit { + + /** + * A boolean representing if to show submitter information + */ + public showSubmitter = true; + + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.WAITING_CONTROLLER; - public workFlow: Workflowitem; + + /** + * The workflowitem object that belonging to the result object + */ + public workflowitem: Workflowitem; constructor(@Inject('objectElementProvider') public listable: ListableObject, @Inject('indexElementProvider') public index: number) { super(listable, index); } + /** + * Initialize all instance variables + */ ngOnInit() { this.initWorkflowItem(this.dso.workflowitem as Observable>); } + /** + * Retrieve workflowitem from result object + */ initWorkflowItem(wfi$: Observable>) { wfi$.pipe( find((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))) ).subscribe((rd: RemoteData) => { - this.workFlow = rd.payload; + this.workflowitem = rd.payload; }); } } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts new file mode 100644 index 0000000000..de2dfd73f4 --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts @@ -0,0 +1,86 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { WorkflowitemMyDSpaceResultListElementComponent } from './workflowitem-my-dspace-result-list-element.component'; +import { WorkflowitemMyDSpaceResult } from '../../../object-collection/shared/workflowitem-my-dspace-result.model'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; + +let component: WorkflowitemMyDSpaceResultListElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: WorkflowitemMyDSpaceResult = new WorkflowitemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockResultObject.dspaceObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); + +describe('WorkflowitemMyDSpaceResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [WorkflowitemMyDSpaceResultListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkflowitemMyDSpaceResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(WorkflowitemMyDSpaceResultListElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.item).toEqual(item); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.WORKFLOW); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts index 7fad9d7677..ebce79c2b8 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.ts @@ -13,6 +13,9 @@ import { Workflowitem } from '../../../../core/submission/models/workflowitem.mo import { Item } from '../../../../core/shared/item.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders workflowitem object for the mydspace result in the list view. + */ @Component({ selector: 'ds-workflowitem-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss'], @@ -22,13 +25,27 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(WorkflowitemMyDSpaceResult, ViewMode.List) @renderElementsFor(Workflowitem, ViewMode.List) export class WorkflowitemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { + + /** + * The item object that belonging to the result object + */ public item: Item; + + /** + * Represent item's status + */ public status = MyDspaceItemStatusType.WORKFLOW; + /** + * Initialize all instance variables + */ ngOnInit() { this.initItem(this.dso.item as Observable>); } + /** + * Retrieve item from result object + */ initItem(item$: Observable>) { item$.pipe( find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts new file mode 100644 index 0000000000..f47c05c9e9 --- /dev/null +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts @@ -0,0 +1,86 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +import { of as observableOf } from 'rxjs'; + +import { Item } from '../../../../core/shared/item.model'; +import { WorkspaceitemMyDSpaceResultListElementComponent } from './workspaceitem-my-dspace-result-list-element.component'; +import { WorkspaceitemMyDSpaceResult } from '../../../object-collection/shared/workspaceitem-my-dspace-result.model'; +import { Workspaceitem } from '../../../../core/submission/models/workspaceitem.model'; +import { RemoteData } from '../../../../core/data/remote-data'; +import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; + +let component: WorkspaceitemMyDSpaceResultListElementComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +const mockResultObject: WorkspaceitemMyDSpaceResult = new WorkspaceitemMyDSpaceResult(); +mockResultObject.hitHighlights = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockResultObject.dspaceObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); + +describe('WorkspaceitemMyDSpaceResultListElementComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [WorkspaceitemMyDSpaceResultListElementComponent], + providers: [ + { provide: 'objectElementProvider', useValue: (mockResultObject) }, + { provide: 'indexElementProvider', useValue: (compIndex) } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkspaceitemMyDSpaceResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(WorkspaceitemMyDSpaceResultListElementComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.dso = mockResultObject.dspaceObject; + fixture.detectChanges(); + }); + + it('should init item properly', () => { + expect(component.item).toEqual(item); + }); + + it('should have properly status', () => { + expect(component.status).toEqual(MyDspaceItemStatusType.IN_PROGRESS); + }); +}); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts index b0df02c64c..c60b742ae0 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.ts @@ -13,6 +13,9 @@ import { isNotUndefined } from '../../../empty.util'; import { Item } from '../../../../core/shared/item.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; +/** + * This component renders workspaceitem object for the mydspace result in the list view. + */ @Component({ selector: 'ds-workspaceitem-my-dspace-result-list-element', styleUrls: ['../my-dspace-result-list-element.component.scss', './workspaceitem-my-dspace-result-list-element.component.scss'], @@ -22,13 +25,26 @@ import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspa @renderElementsFor(WorkspaceitemMyDSpaceResult, ViewMode.List) export class WorkspaceitemMyDSpaceResultListElementComponent extends MyDSpaceResultListElementComponent { + /** + * The item object that belonging to the result object + */ item: Item; + + /** + * Represent item's status + */ status = MyDspaceItemStatusType.IN_PROGRESS; + /** + * Initialize all instance variables + */ ngOnInit() { this.initItem(this.dso.item as Observable>); } + /** + * Retrieve item from result object + */ initItem(item$: Observable>) { item$.pipe( find((rd: RemoteData) => rd.hasSucceeded && isNotUndefined(rd.payload)) diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 94d9e73e5c..a5a426f1a7 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -126,7 +126,7 @@ import { ItemSearchResultListElementComponent } from './object-list/search-resul import { EditItemSelectorComponent } from './dso-selector/modal-wrappers/edit-item-selector/edit-item-selector.component'; import { EditCommunitySelectorComponent } from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditCollectionSelectorComponent } from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; -import { ItemListPreviewComponent } from './object-list/item-list-preview/item-list-preview.component'; +import { ItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; import { ItemPageAuthorFieldComponent } from '../+item-page/simple/field-components/specific-field/author/item-page-author-field.component'; import { ItemPageDateFieldComponent } from '../+item-page/simple/field-components/specific-field/date/item-page-date-field.component'; import { ItemPageAbstractFieldComponent } from '../+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component'; From c749167f7bc891e3e2d4e934d980cbc1d51a844b Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 1 Apr 2019 16:13:19 +0200 Subject: [PATCH 115/205] 61493: AoT build fix --- .../filtered-discovery-page-response-parsing.service.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts index ce19d8d3ff..d81ce4b6bd 100644 --- a/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts +++ b/src/app/core/data/filtered-discovery-page-response-parsing.service.spec.ts @@ -24,7 +24,8 @@ describe('FilteredDiscoveryPageResponseParsingService', () => { payload: { 'discovery-query': 'query' }, - statusCode: '200' + statusCode: 200, + statusText: 'OK' } as DSpaceRESTV2Response; it('should return a FilteredDiscoveryQueryResponse containing the correct query', () => { From 607beb1bef379bf2731b93339a9948c779fd9fca Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 Apr 2019 15:14:50 +0200 Subject: [PATCH 116/205] Added ClaimedTaskActionsReturnToPoolComponent --- ...task-actions-return-to-pool.component.html | 8 +++ ...task-actions-return-to-pool.component.scss | 0 ...k-actions-return-to-pool.component.spec.ts | 65 +++++++++++++++++++ ...d-task-actions-return-to-pool.component.ts | 32 +++++++++ 4 files changed, 105 insertions(+) create mode 100644 src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html new file mode 100644 index 0000000000..2002c55528 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html @@ -0,0 +1,8 @@ + diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.scss b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts new file mode 100644 index 0000000000..fcb370595a --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts @@ -0,0 +1,65 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { ClaimedTaskActionsReturnToPoolComponent } from './claimed-task-actions-return-to-pool.component'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; + +let component: ClaimedTaskActionsReturnToPoolComponent; +let fixture: ComponentFixture; + +describe('ClaimedTaskActionsReturnToPoolComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ClaimedTaskActionsReturnToPoolComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedTaskActionsReturnToPoolComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClaimedTaskActionsReturnToPoolComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should display return to pool button', () => { + const btn = fixture.debugElement.query(By.css('.btn-secondary')); + + expect(btn).toBeDefined(); + }); + + it('should display spin icon when return to pool action is pending', () => { + component.processingReturnToPool = true; + fixture.detectChanges(); + + const span = fixture.debugElement.query(By.css('.btn-secondary .fa-spin')); + + expect(span).toBeDefined(); + }); + + it('should emit return to pool event', () => { + spyOn(component.returnToPool, 'emit'); + + component.confirmApprove(); + fixture.detectChanges(); + + expect(component.returnToPool.emit).toHaveBeenCalled(); + }); + +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts new file mode 100644 index 0000000000..381b8f1afe --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts @@ -0,0 +1,32 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'ds-claimed-task-actions-return-to-pool', + styleUrls: ['./claimed-task-actions-return-to-pool.component.scss'], + templateUrl: './claimed-task-actions-return-to-pool.component.html', +}) + +export class ClaimedTaskActionsReturnToPoolComponent { + + /** + * A boolean representing if a return to pool operation is pending + */ + @Input() processingReturnToPool: boolean; + + /** + * CSS classes to append to return to pool button + */ + @Input() wrapperClass: string; + + /** + * An event fired when a return to pool action is confirmed. + */ + @Output() returnToPool: EventEmitter = new EventEmitter(); + + /** + * Emit approve event + */ + confirmApprove() { + this.returnToPool.emit(); + } +} From 16b8e91585c37de1564d83a377b710d1e015ef9d Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 Apr 2019 15:18:06 +0200 Subject: [PATCH 117/205] Added tests and comments --- .../my-dspace-configuration.service.ts | 12 + .../my-dspace-page-routing.module.ts | 3 + .../+my-dspace-page/my-dspace-page.module.ts | 4 + .../my-dspace-results.component.ts | 24 +- .../search-configuration-option.model.ts | 11 + ...search-switch-configuration.component.html | 2 +- ...rch-switch-configuration.component.spec.ts | 212 +++++++------- .../search-switch-configuration.component.ts | 32 ++- src/app/core/eperson/models/eperson.model.ts | 4 +- .../normalized-submission-object.model.ts | 8 +- .../models/submission-object.model.ts | 5 + .../user-menu/user-menu.component.spec.ts | 151 ++++++++++ .../user-menu/user-menu.component.ts | 6 + ...laimed-task-actions-approve.component.html | 2 +- ...med-task-actions-approve.component.spec.ts | 65 +++++ .../claimed-task-actions-approve.component.ts | 17 +- .../claimed-task-actions.component.html | 27 +- .../claimed-task-actions.component.spec.ts | 269 ++++++++++++++++++ .../claimed-task-actions.component.ts | 84 ++++-- ...claimed-task-actions-reject.component.html | 20 +- ...imed-task-actions-reject.component.spec.ts | 108 +++++++ .../claimed-task-actions-reject.component.ts | 43 ++- .../item/item-actions.component.spec.ts | 96 +++++++ .../item/item-actions.component.ts | 31 +- .../mydspace-actions-service.factory.ts | 7 + .../mydspace-actions/mydspace-actions.ts | 66 ++++- .../pool-task-actions.component.spec.ts | 170 +++++++++++ .../pool-task/pool-task-actions.component.ts | 63 ++-- .../workflowitem-actions.component.spec.ts | 98 +++++++ .../workflowitem-actions.component.ts | 32 ++- .../workspaceitem-actions.component.spec.ts | 163 +++++++++++ .../workspaceitem-actions.component.ts | 49 +++- src/app/shared/shared.module.ts | 2 + src/app/shared/testing/eperson-mock.ts | 48 ++-- src/app/shared/testing/router-stub.ts | 1 + .../search-configuration-service-stub.ts | 5 +- 36 files changed, 1693 insertions(+), 247 deletions(-) create mode 100644 src/app/shared/auth-nav-menu/user-menu/user-menu.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/item/item-actions.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts diff --git a/src/app/+my-dspace-page/my-dspace-configuration.service.ts b/src/app/+my-dspace-page/my-dspace-configuration.service.ts index 3ee91c5f31..420e045887 100644 --- a/src/app/+my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/+my-dspace-page/my-dspace-configuration.service.ts @@ -72,6 +72,12 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { this.isAdmin$ = this.roleService.isAdmin(); } + /** + * Returns the list of available configuration depend on the user role + * + * @return {Observable} + * Emits the available configuration list + */ public getAvailableConfigurationTypes(): Observable { return combineLatest(this.isSubmitter$, this.isController$, this.isAdmin$).pipe( first(), @@ -87,6 +93,12 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { })); } + /** + * Returns the select options for the available configuration list + * + * @return {Observable} + * Emits the select options list + */ public getAvailableConfigurationOptions(): Observable { return this.getAvailableConfigurationTypes().pipe( first(), diff --git a/src/app/+my-dspace-page/my-dspace-page-routing.module.ts b/src/app/+my-dspace-page/my-dspace-page-routing.module.ts index 70b758af0d..d70a007e3a 100644 --- a/src/app/+my-dspace-page/my-dspace-page-routing.module.ts +++ b/src/app/+my-dspace-page/my-dspace-page-routing.module.ts @@ -18,5 +18,8 @@ import { MyDSpaceGuard } from './my-dspace.guard'; ]) ] }) +/** + * This module defines the default component to load when navigating to the mydspace page path. + */ export class MyDspacePageRoutingModule { } diff --git a/src/app/+my-dspace-page/my-dspace-page.module.ts b/src/app/+my-dspace-page/my-dspace-page.module.ts index 697c22dd0e..4b8cf37b7a 100644 --- a/src/app/+my-dspace-page/my-dspace-page.module.ts +++ b/src/app/+my-dspace-page/my-dspace-page.module.ts @@ -60,6 +60,10 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service' PoolMyDSpaceResultDetailElementComponent ] }) + +/** + * This module handles all components that are necessary for the mydspace page + */ export class MyDSpacePageModule { } diff --git a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts index e6a086fd46..3a16def9c1 100644 --- a/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts +++ b/src/app/+my-dspace-page/my-dspace-results/my-dspace-results.component.ts @@ -3,7 +3,6 @@ import { Component, Input } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { fadeIn, fadeInOut } from '../../shared/animations/fade'; -import { SortOptions } from '../../core/cache/models/sort-options.model'; import { MyDSpaceResult } from '../my-dspace-result.model'; import { SearchOptions } from '../../+search-page/search-options.model'; import { PaginatedList } from '../../core/data/paginated-list'; @@ -11,9 +10,7 @@ import { ViewMode } from '../../core/shared/view-mode.model'; import { isEmpty } from '../../shared/empty.util'; /** - * This component renders a simple item page. - * The route parameter 'id' is used to request the item it represents. - * All fields of the item that should be displayed, are defined in its template. + * Component that represents all results for mydspace page */ @Component({ selector: 'ds-my-dspace-results', @@ -24,13 +21,30 @@ import { isEmpty } from '../../shared/empty.util'; ] }) export class MyDSpaceResultsComponent { + + /** + * The actual search result objects + */ @Input() searchResults: RemoteData>>; + + /** + * The current configuration of the search + */ @Input() searchConfig: SearchOptions; - @Input() sortConfig: SortOptions; + + /** + * The current view mode for the search results + */ @Input() viewMode: ViewMode; + /** + * A boolean representing if search results entry are separated by a line + */ hasBorder = true; + /** + * Check if mydspace search results are loading + */ isLoading() { return !this.searchResults || isEmpty(this.searchResults) || this.searchResults.isLoading; } diff --git a/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts index 7f9b4acd96..6f9a72da48 100644 --- a/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts +++ b/src/app/+search-page/search-switch-configuration/search-configuration-option.model.ts @@ -1,4 +1,15 @@ +/** + * Represents a search configuration select option + */ export interface SearchConfigurationOption { + + /** + * The select option value + */ value: string; + + /** + * The select option label + */ label: string; } diff --git a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html index 5b1bdc1ddd..8df37214d1 100644 --- a/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html +++ b/src/app/+search-page/search-switch-configuration/search-switch-configuration.component.html @@ -4,7 +4,7 @@ - diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts new file mode 100644 index 0000000000..d7e0b53748 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.spec.ts @@ -0,0 +1,108 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; + +import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { ClaimedTaskActionsRejectComponent } from './claimed-task-actions-reject.component'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; + +let component: ClaimedTaskActionsRejectComponent; +let fixture: ComponentFixture; +let formBuilder: FormBuilder; +let modalService: NgbModal; + +describe('ClaimedTaskActionsRejectComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbModule.forRoot(), + ReactiveFormsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ClaimedTaskActionsRejectComponent], + providers: [ + FormBuilder, + NgbModal + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedTaskActionsRejectComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClaimedTaskActionsRejectComponent); + component = fixture.componentInstance; + formBuilder = TestBed.get(FormBuilder); + modalService = TestBed.get(NgbModal); + component.modalRef = modalService.open('ok'); + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + modalService = null; + formBuilder = null; + }); + + it('should init reject form properly', () => { + expect(component.rejectForm).toBeDefined(); + expect(component.rejectForm instanceof FormGroup).toBeTruthy(); + expect(component.rejectForm.controls.reason).toBeDefined(); + }); + + it('should display reject button', () => { + const btn = fixture.debugElement.query(By.css('.btn-danger')); + + expect(btn).toBeDefined(); + }); + + it('should display spin icon when reject is pending', () => { + component.processingReject = true; + fixture.detectChanges(); + + const span = fixture.debugElement.query(By.css('.btn-danger .fa-spin')); + + expect(span).toBeDefined(); + }); + + it('should call openRejectModal on reject button click', fakeAsync(() => { + spyOn(component.rejectForm, 'reset'); + const btn = fixture.debugElement.query(By.css('.btn-danger')); + btn.nativeElement.click(); + fixture.detectChanges(); + + expect(component.rejectForm.reset).toHaveBeenCalled(); + expect(component.modalRef).toBeDefined(); + + component.modalRef.close() + })); + + it('should call confirmReject on form submit', fakeAsync(() => { + spyOn(component.reject, 'emit'); + + const btn = fixture.debugElement.query(By.css('.btn-danger')); + btn.nativeElement.click(); + fixture.detectChanges(); + + expect(component.modalRef).toBeDefined(); + + const form = ((document as any).querySelector('form')); + form.dispatchEvent(new Event('ngSubmit')); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(component.reject.emit).toHaveBeenCalled(); + }); + + })); +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts index 7f09896b4c..b66c104695 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts @@ -10,18 +10,45 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; }) export class ClaimedTaskActionsRejectComponent implements OnInit { + + /** + * A boolean representing if a reject operation is pending + */ @Input() processingReject: boolean; - @Input() taskId: string; + + /** + * CSS classes to append to reject button + */ @Input() wrapperClass: string; + /** + * An event fired when a reject action is confirmed. + * Event's payload equals to reject reason. + */ @Output() reject: EventEmitter = new EventEmitter(); + /** + * The reject form group + */ public rejectForm: FormGroup; + + /** + * Reference to NgbModal + */ public modalRef: NgbModalRef; + /** + * Initialize instance variables + * + * @param {FormBuilder} formBuilder + * @param {NgbModal} modalService + */ constructor(private formBuilder: FormBuilder, private modalService: NgbModal) { } + /** + * Initialize form + */ ngOnInit() { this.rejectForm = this.formBuilder.group({ reason: ['', Validators.required] @@ -29,15 +56,23 @@ export class ClaimedTaskActionsRejectComponent implements OnInit { } - click() { + /** + * Close modal and emit reject event + */ + confirmReject() { this.processingReject = true; this.modalRef.close('Send Button'); const reason = this.rejectForm.get('reason').value; this.reject.emit(reason); } - openRejectModal(rejectModal) { + /** + * Open modal + * + * @param content + */ + openRejectModal(content: any) { this.rejectForm.reset(); - this.modalRef = this.modalService.open(rejectModal); + this.modalRef = this.modalService.open(content); } } diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts b/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts new file mode 100644 index 0000000000..72be122c8f --- /dev/null +++ b/src/app/shared/mydspace-actions/item/item-actions.component.spec.ts @@ -0,0 +1,96 @@ +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { MockTranslateLoader } from '../../mocks/mock-translate-loader'; +import { RouterStub } from '../../testing/router-stub'; +import { Item } from '../../../core/shared/item.model'; +import { ItemActionsComponent } from './item-actions.component'; +import { ItemDataService } from '../../../core/data/item-data.service'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service-stub'; + +let component: ItemActionsComponent; +let fixture: ComponentFixture; + +let mockObject: Item; + +const mockDataService = {}; + +mockObject = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); + +describe('ItemActionsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ItemActionsComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: Router, useValue: new RouterStub() }, + { provide: ItemDataService, useValue: mockDataService }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemActionsComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemActionsComponent); + component = fixture.componentInstance; + component.object = mockObject; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should init object properly', () => { + component.object = null; + component.initObjects(mockObject); + + expect(component.object).toEqual(mockObject); + }); + +}); diff --git a/src/app/shared/mydspace-actions/item/item-actions.component.ts b/src/app/shared/mydspace-actions/item/item-actions.component.ts index b2022bbdd4..0760fe54e0 100644 --- a/src/app/shared/mydspace-actions/item/item-actions.component.ts +++ b/src/app/shared/mydspace-actions/item/item-actions.component.ts @@ -1,11 +1,17 @@ import { Component, Injector, Input } from '@angular/core'; import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; + import { MyDSpaceActionsComponent } from '../mydspace-actions'; -import { ResourceType } from '../../../core/shared/resource-type'; import { ItemDataService } from '../../../core/data/item-data.service'; import { Item } from '../../../core/shared/item.model'; +import { ResourceType } from '../../../core/shared/resource-type'; +import { NotificationsService } from '../../notifications/notifications.service'; +/** + * This component represents mydspace actions related to Item object. + */ @Component({ selector: 'ds-item-actions', styleUrls: ['./item-actions.component.scss'], @@ -13,13 +19,32 @@ import { Item } from '../../../core/shared/item.model'; }) export class ItemActionsComponent extends MyDSpaceActionsComponent { + + /** + * The Item object + */ @Input() object: Item; + /** + * Initialize instance variables + * + * @param {Injector} injector + * @param {Router} router + * @param {NotificationsService} notificationsService + * @param {TranslateService} translate + */ constructor(protected injector: Injector, - protected router: Router) { - super(ResourceType.Workspaceitem, injector, router); + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService) { + super(ResourceType.Item, injector, router, notificationsService, translate); } + /** + * Init the target object + * + * @param {Item} object + */ initObjects(object: Item) { this.object = object; } diff --git a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts index b3efa4c9db..7aa948f689 100644 --- a/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts +++ b/src/app/shared/mydspace-actions/mydspace-actions-service.factory.ts @@ -5,10 +5,17 @@ import { ClaimedTaskDataService } from '../../core/tasks/claimed-task-data.servi import { PoolTaskDataService } from '../../core/tasks/pool-task-data.service'; import { WorkflowitemDataService } from '../../core/submission/workflowitem-data.service'; import { CacheableObject } from '../../core/cache/object-cache.reducer'; +import { ItemDataService } from '../../core/data/item-data.service'; +/** + * Class to return DataService for given ResourceType + */ export class MydspaceActionsServiceFactory> { public getConstructor(type: ResourceType): TService { switch (type) { + case ResourceType.Item: { + return ItemDataService as any; + } case ResourceType.Workspaceitem: { return WorkspaceitemDataService as any; } diff --git a/src/app/shared/mydspace-actions/mydspace-actions.ts b/src/app/shared/mydspace-actions/mydspace-actions.ts index ab76eaf280..8e465644c3 100644 --- a/src/app/shared/mydspace-actions/mydspace-actions.ts +++ b/src/app/shared/mydspace-actions/mydspace-actions.ts @@ -8,19 +8,55 @@ import { RemoteData } from '../../core/data/remote-data'; import { DataService } from '../../core/data/data.service'; import { DSpaceObject } from '../../core/shared/dspace-object.model'; import { ResourceType } from '../../core/shared/resource-type'; +import { NotificationOptions } from '../notifications/models/notification-options.model'; +import { NotificationsService } from '../notifications/notifications.service'; +import { TranslateService } from '@ngx-translate/core'; +/** + * Abstract class for all different representations of mydspace actions + */ export abstract class MyDSpaceActionsComponent> { + + /** + * The target mydspace object + */ @Input() abstract object: T; + + /** + * Instance of DataService realted to mydspace object + */ protected objectDataService: TService; - constructor(protected objectType: ResourceType, protected injector: Injector, protected router: Router) { + /** + * Initialize instance variables + * + * @param {ResourceType} objectType + * @param {Injector} injector + * @param {Router} router + * @param {NotificationsService} notificationsService + * @param {TranslateService} translate + */ + constructor( + protected objectType: ResourceType, + protected injector: Injector, + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService) { const factory = new MydspaceActionsServiceFactory(); this.objectDataService = injector.get(factory.getConstructor(objectType)); } + /** + * Abstract method called to init the target object + * + * @param {T} object + */ abstract initObjects(object: T): void; - reload() { + /** + * Refresh current page + */ + reload(): void { // override the route reuse strategy this.router.routeReuseStrategy.shouldReuseRoute = () => { return false; @@ -30,12 +66,34 @@ export abstract class MyDSpaceActionsComponent) => rd.hasSucceeded) ).subscribe((rd: RemoteData) => { this.initObjects(rd.payload as T); }); } + + /** + * Handle action response and show properly notification + * + * @param result + * true on success, false otherwise + */ + handleActionResponse(result: boolean): void { + if (result) { + this.reload(); + this.notificationsService.success(null, + this.translate.get('submission.workflow.tasks.generic.success'), + new NotificationOptions(5000, false)); + } else { + this.notificationsService.error(null, + this.translate.get('submission.workflow.tasks.generic.error'), + new NotificationOptions(20000, true)); + } + } } diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts new file mode 100644 index 0000000000..1c0e8e71fa --- /dev/null +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.spec.ts @@ -0,0 +1,170 @@ +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { By } from '@angular/platform-browser'; + +import { of as observableOf } from 'rxjs'; +import { cold } from 'jasmine-marbles'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { MockTranslateLoader } from '../../mocks/mock-translate-loader'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service-stub'; +import { RouterStub } from '../../testing/router-stub'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Item } from '../../../core/shared/item.model'; +import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service'; +import { PoolTaskActionsComponent } from './pool-task-actions.component'; +import { PoolTask } from '../../../core/tasks/models/pool-task-object.model'; +import { Workflowitem } from '../../../core/submission/models/workflowitem.model'; + +let component: PoolTaskActionsComponent; +let fixture: ComponentFixture; + +let mockObject: PoolTask; +let notificationsServiceStub: NotificationsServiceStub; +let router: RouterStub; + +const mockDataService = jasmine.createSpyObj('PoolTaskDataService', { + claimTask: jasmine.createSpy('claimTask') +}); + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rdItem = new RemoteData(false, false, true, null, item); +const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' }); + +describe('PoolTaskActionsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [PoolTaskActionsComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: Router, useValue: new RouterStub() }, + { provide: PoolTaskDataService, useValue: mockDataService }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(PoolTaskActionsComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PoolTaskActionsComponent); + component = fixture.componentInstance; + component.object = mockObject; + notificationsServiceStub = TestBed.get(NotificationsService); + router = TestBed.get(Router); + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should init objects properly', () => { + component.object = null; + component.initObjects(mockObject); + + expect(component.object).toEqual(mockObject); + + expect(component.workflowitem$).toBeObservable(cold('(b|)', { + b: rdWorkflowitem.payload + })) + }); + + it('should display claim task button', () => { + const btn = fixture.debugElement.query(By.css('.btn-info')); + + expect(btn).toBeDefined(); + }); + + it('should call claimTask method on claim', fakeAsync(() => { + spyOn(component, 'reload'); + mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true})); + + component.claim(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(mockDataService.claimTask).toHaveBeenCalledWith(mockObject.id); + }); + + })); + + it('should display a success notification on claim success', async(() => { + spyOn(component, 'reload'); + mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true})); + + component.claim(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(notificationsServiceStub.success).toHaveBeenCalled(); + }); + })); + + it('should reload page on claim success', async(() => { + spyOn(router, 'navigateByUrl'); + router.url = 'test.url/test'; + mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: true})); + + component.claim(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(router.navigateByUrl).toHaveBeenCalledWith('test.url/test'); + }); + })); + + it('should display an error notification on claim failure', async(() => { + mockDataService.claimTask.and.returnValue(observableOf({hasSucceeded: false})); + + component.claim(); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(notificationsServiceStub.error).toHaveBeenCalled(); + }); + })); + +}); diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts index eccd427668..bd8f3f1a37 100644 --- a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.ts @@ -10,35 +10,64 @@ import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-res import { RemoteData } from '../../../core/data/remote-data'; import { PoolTask } from '../../../core/tasks/models/pool-task-object.model'; import { PoolTaskDataService } from '../../../core/tasks/pool-task-data.service'; -import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationOptions } from '../../notifications/models/notification-options.model'; import { isNotUndefined } from '../../empty.util'; import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { ResourceType } from '../../../core/shared/resource-type'; +import { NotificationsService } from '../../notifications/notifications.service'; +/** + * This component represents mydspace actions related to PoolTask object. + */ @Component({ selector: 'ds-pool-task-actions', styleUrls: ['./pool-task-actions.component.scss'], templateUrl: './pool-task-actions.component.html', }) - export class PoolTaskActionsComponent extends MyDSpaceActionsComponent { + + /** + * The PoolTask object + */ @Input() object: PoolTask; + /** + * A boolean representing if a claim operation is pending + * @type {BehaviorSubject} + */ public processingClaim$ = new BehaviorSubject(false); + + /** + * The workflowitem object that belonging to the PoolTask object + */ public workflowitem$: Observable; + /** + * Initialize instance variables + * + * @param {Injector} injector + * @param {Router} router + * @param {NotificationsService} notificationsService + * @param {TranslateService} translate + */ constructor(protected injector: Injector, protected router: Router, - private notificationsService: NotificationsService, - private translate: TranslateService) { - super(ResourceType.PoolTask, injector, router); + protected notificationsService: NotificationsService, + protected translate: TranslateService) { + super(ResourceType.PoolTask, injector, router, notificationsService, translate); } + /** + * Initialize objects + */ ngOnInit() { this.initObjects(this.object); } + /** + * Init the PoolTask and Workflowitem objects + * + * @param {PoolTask} object + */ initObjects(object: PoolTask) { this.object = object; this.workflowitem$ = (this.object.workflowitem as Observable>).pipe( @@ -46,27 +75,15 @@ export class PoolTaskActionsComponent extends MyDSpaceActionsComponent) => rd.payload)); } + /** + * Claim the task. + */ claim() { this.processingClaim$.next(true); this.objectDataService.claimTask(this.object.id) .subscribe((res: ProcessTaskResponse) => { - this.responseHandle(res); + this.handleActionResponse(res.hasSucceeded); + this.processingClaim$.next(false); }); } - - private responseHandle(res: ProcessTaskResponse) { - if (res.hasSucceeded) { - this.processingClaim$.next(false); - this.reload(); - this.notificationsService.success(null, - this.translate.get('submission.workflow.tasks.generic.success'), - new NotificationOptions(5000, false)); - } else { - this.processingClaim$.next(false); - this.notificationsService.error(null, - this.translate.get('submission.workflow.tasks.generic.error'), - new NotificationOptions(20000, true)); - } - } - } diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts new file mode 100644 index 0000000000..7533565afe --- /dev/null +++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.spec.ts @@ -0,0 +1,98 @@ +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { MockTranslateLoader } from '../../mocks/mock-translate-loader'; +import { RouterStub } from '../../testing/router-stub'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Item } from '../../../core/shared/item.model'; +import { Workflowitem } from '../../../core/submission/models/workflowitem.model'; +import { WorkflowitemActionsComponent } from './workflowitem-actions.component'; +import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service-stub'; + +let component: WorkflowitemActionsComponent; +let fixture: ComponentFixture; + +let mockObject: Workflowitem; + +const mockDataService = {}; + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockObject = Object.assign(new Workflowitem(), { item: observableOf(rd), id: '1234', uuid: '1234' }); + +describe('WorkflowitemActionsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [WorkflowitemActionsComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: Router, useValue: new RouterStub() }, + { provide: WorkflowitemDataService, useValue: mockDataService }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkflowitemActionsComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WorkflowitemActionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should init object properly', () => { + component.initObjects(mockObject); + + expect(component.object).toEqual(mockObject); + }); + +}); diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts index 04de5da885..a6304bf5d4 100644 --- a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.ts @@ -1,25 +1,49 @@ import { Component, Injector, Input } from '@angular/core'; import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; + import { MyDSpaceActionsComponent } from '../mydspace-actions'; -import { ResourceType } from '../../../core/shared/resource-type'; import { Workflowitem } from '../../../core/submission/models/workflowitem.model'; import { WorkflowitemDataService } from '../../../core/submission/workflowitem-data.service'; +import { ResourceType } from '../../../core/shared/resource-type'; +import { NotificationsService } from '../../notifications/notifications.service'; +/** + * This component represents mydspace actions related to Workflowitem object. + */ @Component({ selector: 'ds-workflowitem-actions', styleUrls: ['./workflowitem-actions.component.scss'], templateUrl: './workflowitem-actions.component.html', }) - export class WorkflowitemActionsComponent extends MyDSpaceActionsComponent { + + /** + * The Workflowitem object + */ @Input() object: Workflowitem; + /** + * Initialize instance variables + * + * @param {Injector} injector + * @param {Router} router + * @param {NotificationsService} notificationsService + * @param {TranslateService} translate + */ constructor(protected injector: Injector, - protected router: Router) { - super(ResourceType.Workflowitem, injector, router); + protected router: Router, + protected notificationsService: NotificationsService, + protected translate: TranslateService) { + super(ResourceType.Workflowitem, injector, router, notificationsService, translate); } + /** + * Init the target object + * + * @param {Workflowitem} object + */ initObjects(object: Workflowitem) { this.object = object; } diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts new file mode 100644 index 0000000000..ec8bc4a11c --- /dev/null +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.spec.ts @@ -0,0 +1,163 @@ +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { By } from '@angular/platform-browser'; + +import { of as observableOf } from 'rxjs'; +import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { MockTranslateLoader } from '../../mocks/mock-translate-loader'; +import { NotificationsService } from '../../notifications/notifications.service'; +import { NotificationsServiceStub } from '../../testing/notifications-service-stub'; +import { RouterStub } from '../../testing/router-stub'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Item } from '../../../core/shared/item.model'; +import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model'; +import { WorkspaceitemActionsComponent } from './workspaceitem-actions.component'; +import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; + +let component: WorkspaceitemActionsComponent; +let fixture: ComponentFixture; + +let mockObject: Workspaceitem; +let notificationsServiceStub: NotificationsServiceStub; + +const mockDataService = jasmine.createSpyObj('WorkspaceitemDataService', { + delete: jasmine.createSpy('delete') +}); + +const item = Object.assign(new Item(), { + bitstreams: observableOf({}), + metadata: { + 'dc.title': [ + { + language: 'en_US', + value: 'This is just another title' + } + ], + 'dc.type': [ + { + language: null, + value: 'Article' + } + ], + 'dc.contributor.author': [ + { + language: 'en_US', + value: 'Smith, Donald' + } + ], + 'dc.date.issued': [ + { + language: null, + value: '2015-06-26' + } + ] + } +}); +const rd = new RemoteData(false, false, true, null, item); +mockObject = Object.assign(new Workspaceitem(), { item: observableOf(rd), id: '1234', uuid: '1234' }); + +describe('WorkspaceitemActionsComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NgbModule.forRoot(), + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [WorkspaceitemActionsComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: NotificationsService, useValue: new NotificationsServiceStub() }, + { provide: Router, useValue: new RouterStub() }, + { provide: WorkspaceitemDataService, useValue: mockDataService }, + NgbModal + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(WorkspaceitemActionsComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(WorkspaceitemActionsComponent); + component = fixture.componentInstance; + component.object = mockObject; + notificationsServiceStub = TestBed.get(NotificationsService); + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should init object properly', () => { + component.object = null; + component.initObjects(mockObject); + + expect(component.object).toEqual(mockObject); + }); + + it('should display edit button', () => { + const btn = fixture.debugElement.query(By.css('.btn-primary')); + + expect(btn).toBeDefined(); + }); + + it('should display delete button', () => { + const btn = fixture.debugElement.query(By.css('.btn-danger')); + + expect(btn).toBeDefined(); + }); + + it('should call confirmDiscard on discard confirmation', fakeAsync(() => { + mockDataService.delete.and.returnValue(observableOf(true)); + spyOn(component, 'reload'); + const btn = fixture.debugElement.query(By.css('.btn-danger')); + btn.nativeElement.click(); + fixture.detectChanges(); + + const confirmBtn: any = ((document as any).querySelector('.modal-footer .btn-danger')); + confirmBtn.click(); + + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(mockDataService.delete).toHaveBeenCalledWith(mockObject); + }); + + })); + + it('should display a success notification on delete success', async(() => { + spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')}); + mockDataService.delete.and.returnValue(observableOf(true)); + spyOn(component, 'reload'); + + component.confirmDiscard('ok'); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(notificationsServiceStub.success).toHaveBeenCalled(); + }); + })); + + it('should display an error notification on delete failure', async(() => { + spyOn((component as any).modalService, 'open').and.returnValue({result: Promise.resolve('ok')}); + mockDataService.delete.and.returnValue(observableOf(false)); + spyOn(component, 'reload'); + + component.confirmDiscard('ok'); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(notificationsServiceStub.error).toHaveBeenCalled(); + }); + })); +}); diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts index c05eb557d2..cea4c3746e 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.ts @@ -7,50 +7,71 @@ import { TranslateService } from '@ngx-translate/core'; import { Workspaceitem } from '../../../core/submission/models/workspaceitem.model'; import { MyDSpaceActionsComponent } from '../mydspace-actions'; -import { SubmissionRestService } from '../../../core/submission/submission-rest.service'; import { WorkspaceitemDataService } from '../../../core/submission/workspaceitem-data.service'; import { ResourceType } from '../../../core/shared/resource-type'; import { NotificationsService } from '../../notifications/notifications.service'; -import { NotificationOptions } from '../../notifications/models/notification-options.model'; +/** + * This component represents mydspace actions related to Workspaceitem object. + */ @Component({ selector: 'ds-workspaceitem-actions', styleUrls: ['./workspaceitem-actions.component.scss'], templateUrl: './workspaceitem-actions.component.html', }) - export class WorkspaceitemActionsComponent extends MyDSpaceActionsComponent { + + /** + * The workspaceitem object + */ @Input() object: Workspaceitem; + /** + * A boolean representing if a delete operation is pending + * @type {BehaviorSubject} + */ public processingDelete$ = new BehaviorSubject(false); + /** + * Initialize instance variables + * + * @param {Injector} injector + * @param {Router} router + * @param {NgbModal} modalService + * @param {NotificationsService} notificationsService + * @param {TranslateService} translate + */ constructor(protected injector: Injector, protected router: Router, - private modalService: NgbModal, - private notificationsService: NotificationsService, - private restService: SubmissionRestService, - private translate: TranslateService) { - super(ResourceType.Workspaceitem, injector, router); + protected modalService: NgbModal, + protected notificationsService: NotificationsService, + protected translate: TranslateService) { + super(ResourceType.Workspaceitem, injector, router, notificationsService, translate); } + /** + * Delete the target workspaceitem object + */ public confirmDiscard(content) { this.modalService.open(content).result.then( (result) => { if (result === 'ok') { this.processingDelete$.next(true); - this.restService.deleteById(this.object.id) - .subscribe(() => { - this.notificationsService.success(null, - this.translate.get('submission.workflow.tasks.generic.success'), - new NotificationOptions(5000, false)); + this.objectDataService.delete(this.object) + .subscribe((response: boolean) => { this.processingDelete$.next(false); - this.reload(); + this.handleActionResponse(response); }) } } ); } + /** + * Init the target object + * + * @param {Workspaceitem} object + */ initObjects(object: Workspaceitem) { this.object = object; } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index a5a426f1a7..00f357cda6 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -140,6 +140,7 @@ import { MetadataValuesComponent } from '../+item-page/field-components/metadata import { MetadataUriValuesComponent } from '../+item-page/field-components/metadata-uri-values/metadata-uri-values.component'; import { RoleDirective } from './roles/role.directive'; import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; +import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -234,6 +235,7 @@ const COMPONENTS = [ ClaimedTaskActionsComponent, ClaimedTaskActionsApproveComponent, ClaimedTaskActionsRejectComponent, + ClaimedTaskActionsReturnToPoolComponent, ItemActionsComponent, PoolTaskActionsComponent, WorkflowitemActionsComponent, diff --git a/src/app/shared/testing/eperson-mock.ts b/src/app/shared/testing/eperson-mock.ts index ef27f4983d..c822fc15d6 100644 --- a/src/app/shared/testing/eperson-mock.ts +++ b/src/app/shared/testing/eperson-mock.ts @@ -13,26 +13,30 @@ export const EPersonMock: EPerson = Object.assign(new EPerson(),{ id: 'testid', uuid: 'testid', type: 'eperson', - metadata: [ - { - key: 'dc.title', - language: null, - value: 'User Test' - }, - { - key: 'eperson.firstname', - language: null, - value: 'User' - }, - { - key: 'eperson.lastname', - language: null, - value: 'Test' - }, - { - key: 'eperson.language', - language: null, - value: 'en' - } - ] + metadata: { + 'dc.title': [ + { + language: null, + value: 'User Test' + } + ], + 'eperson.firstname': [ + { + language: null, + value: 'User' + } + ], + 'eperson.lastname': [ + { + language: null, + value: 'Test' + }, + ], + 'eperson.language': [ + { + language: null, + value: 'en' + }, + ] + } }); diff --git a/src/app/shared/testing/router-stub.ts b/src/app/shared/testing/router-stub.ts index 31c09c41e3..210ee91fdf 100644 --- a/src/app/shared/testing/router-stub.ts +++ b/src/app/shared/testing/router-stub.ts @@ -1,6 +1,7 @@ export class RouterStub { url: string; + routeReuseStrategy = {shouldReuseRoute: {}}; //noinspection TypeScriptUnresolvedFunction navigate = jasmine.createSpy('navigate'); parseUrl = jasmine.createSpy('parseUrl'); diff --git a/src/app/shared/testing/search-configuration-service-stub.ts b/src/app/shared/testing/search-configuration-service-stub.ts index 4c9d94c877..4c9402afb1 100644 --- a/src/app/shared/testing/search-configuration-service-stub.ts +++ b/src/app/shared/testing/search-configuration-service-stub.ts @@ -10,7 +10,10 @@ export class SearchConfigurationServiceStub { } getCurrentScope(a) { - return observableOf('test-id') + return observableOf('test-id'); } + getCurrentConfiguration(a) { + return observableOf(a); + } } From 49aee1898bcae93b2ee5f5f5c17e135ddbc45b0c Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 Apr 2019 16:25:50 +0200 Subject: [PATCH 118/205] Added tests and comments --- ...-dspace-result-detail-element.component.ts | 14 +- .../object-detail.component.spec.ts | 181 ++++++++++++++++++ .../object-detail/object-detail.component.ts | 52 ++++- .../wrapper-detail-element.component.spec.ts | 24 ++- .../wrapper-detail-element.component.ts | 22 +++ ...my-dspace-result-list-element.component.ts | 15 +- .../object-list/object-list.component.spec.ts | 2 +- .../shared/pagination/pagination.component.ts | 2 +- 8 files changed, 290 insertions(+), 22 deletions(-) diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts index 3efb668c5a..4017026396 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts @@ -10,12 +10,20 @@ import { Metadata } from '../../../core/shared/metadata.utils'; selector: 'ds-my-dspace-result-detail-element', template: `` }) - export class MyDSpaceResultDetailElementComponent, K extends DSpaceObject> extends AbstractListableElementComponent { + + /** + * The result element object + */ dso: K; - public constructor(@Inject('objectElementProvider') public gridable: ListableObject) { - super(gridable); + /** + * Initialize instance variables + * + * @param {ListableObject} detailable + */ + public constructor(@Inject('objectElementProvider') public detailable: ListableObject) { + super(detailable); this.dso = this.object.dspaceObject; } diff --git a/src/app/shared/object-detail/object-detail.component.spec.ts b/src/app/shared/object-detail/object-detail.component.spec.ts index e69de29bb2..9b81f1019f 100644 --- a/src/app/shared/object-detail/object-detail.component.spec.ts +++ b/src/app/shared/object-detail/object-detail.component.spec.ts @@ -0,0 +1,181 @@ +import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { ObjectDetailComponent } from './object-detail.component'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { MockTranslateLoader } from '../mocks/mock-translate-loader'; +import { RemoteData } from '../../core/data/remote-data'; +import { PaginatedList } from '../../core/data/paginated-list'; +import { PageInfo } from '../../core/shared/page-info.model'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('ObjectDetailComponent', () => { + let comp: ObjectDetailComponent; + let fixture: ComponentFixture; + const testEvent = {test: 'test'}; + + const testObjects = [ + { one: 1 }, + { two: 2 }, + { three: 3 }, + { four: 4 }, + { five: 5 }, + { six: 6 }, + { seven: 7 }, + { eight: 8 }, + { nine: 9 }, + { ten: 10 } + ]; + const pageInfo = Object.assign(new PageInfo(), {elementsPerPage: 1, totalElements: 10, totalPages: 10, currentPage: 1}) + const mockRD = new RemoteData(false, false, true, null, new PaginatedList(pageInfo, testObjects)); + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + NoopAnimationsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ObjectDetailComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ObjectDetailComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ObjectDetailComponent); + comp = fixture.componentInstance; // SearchPageComponent test instance + comp.objects = mockRD; + fixture.detectChanges(); + }); + + describe('when the pageChange output on the pagination is triggered', () => { + beforeEach(() => { + spyOn(comp, 'onPageChange'); + const paginationEl = fixture.debugElement.query(By.css('ds-pagination')); + paginationEl.triggerEventHandler('pageChange', testEvent); + }); + + it('should call onPageChange on the component', () => { + expect(comp.onPageChange).toHaveBeenCalledWith(testEvent); + }); + }); + + describe('when the pageSizeChange output on the pagination is triggered', () => { + beforeEach(() => { + spyOn(comp, 'onPageSizeChange'); + const paginationEl = fixture.debugElement.query(By.css('ds-pagination')); + paginationEl.triggerEventHandler('pageSizeChange', testEvent); + }); + + it('should call onPageSizeChange on the component', () => { + expect(comp.onPageSizeChange).toHaveBeenCalledWith(testEvent); + }); + }); + + describe('when the sortDirectionChange output on the pagination is triggered', () => { + beforeEach(() => { + spyOn(comp, 'onSortDirectionChange'); + const paginationEl = fixture.debugElement.query(By.css('ds-pagination')); + paginationEl.triggerEventHandler('sortDirectionChange', testEvent); + }); + + it('should call onSortDirectionChange on the component', () => { + expect(comp.onSortDirectionChange).toHaveBeenCalledWith(testEvent); + }); + }); + + describe('when the sortFieldChange output on the pagination is triggered', () => { + beforeEach(() => { + spyOn(comp, 'onSortFieldChange'); + const paginationEl = fixture.debugElement.query(By.css('ds-pagination')); + paginationEl.triggerEventHandler('sortFieldChange', testEvent); + }); + + it('should call onSortFieldChange on the component', () => { + expect(comp.onSortFieldChange).toHaveBeenCalledWith(testEvent); + }); + }); + + describe('when the paginationChange output on the pagination is triggered', () => { + beforeEach(() => { + spyOn(comp, 'onPaginationChange'); + const paginationEl = fixture.debugElement.query(By.css('ds-pagination')); + paginationEl.triggerEventHandler('paginationChange', testEvent); + }); + + it('should call onPaginationChange on the component', () => { + expect(comp.onPaginationChange).toHaveBeenCalledWith(testEvent); + }); + }); + + describe('when onPageChange is triggered with an event', () => { + beforeEach(() => { + spyOn(comp.pageChange, 'emit'); + comp.onPageChange(testEvent); + }); + + it('should emit the value from the pageChange EventEmitter', fakeAsync(() => { + tick(1); + expect(comp.pageChange.emit).toHaveBeenCalled(); + expect(comp.pageChange.emit).toHaveBeenCalledWith(testEvent); + })); + }); + + describe('when onPageSizeChange is triggered with an event', () => { + beforeEach(() => { + spyOn(comp.pageSizeChange, 'emit'); + comp.onPageSizeChange(testEvent); + }); + + it('should emit the value from the pageSizeChange EventEmitter', fakeAsync(() => { + tick(1); + expect(comp.pageSizeChange.emit).toHaveBeenCalled(); + expect(comp.pageSizeChange.emit).toHaveBeenCalledWith(testEvent); + })); + }); + + describe('when onSortDirectionChange is triggered with an event', () => { + beforeEach(() => { + spyOn(comp.sortDirectionChange, 'emit'); + comp.onSortDirectionChange(testEvent); + }); + + it('should emit the value from the sortDirectionChange EventEmitter', fakeAsync(() => { + tick(1); + expect(comp.sortDirectionChange.emit).toHaveBeenCalled(); + expect(comp.sortDirectionChange.emit).toHaveBeenCalledWith(testEvent); + })); + }); + + describe('when onSortFieldChange is triggered with an event', () => { + beforeEach(() => { + spyOn(comp.sortFieldChange, 'emit'); + comp.onSortFieldChange(testEvent); + }); + + it('should emit the value from the sortFieldChange EventEmitter', fakeAsync(() => { + tick(1); + expect(comp.sortFieldChange.emit).toHaveBeenCalled(); + expect(comp.sortFieldChange.emit).toHaveBeenCalledWith(testEvent); + })); + }); + + describe('when onPaginationChange is triggered with an event', () => { + beforeEach(() => { + spyOn(comp.paginationChange, 'emit'); + comp.onPaginationChange(testEvent); + }); + + it('should emit the value from the paginationChange EventEmitter', fakeAsync(() => { + tick(1); + expect(comp.paginationChange.emit).toHaveBeenCalled(); + expect(comp.paginationChange.emit).toHaveBeenCalledWith(testEvent); + })); + }); +}); diff --git a/src/app/shared/object-detail/object-detail.component.ts b/src/app/shared/object-detail/object-detail.component.ts index bdbb360be6..dad5951e46 100644 --- a/src/app/shared/object-detail/object-detail.component.ts +++ b/src/app/shared/object-detail/object-detail.component.ts @@ -24,17 +24,44 @@ import { PaginationComponentOptions } from '../pagination/pagination-component-o templateUrl: './object-detail.component.html', animations: [fadeIn] }) - export class ObjectDetailComponent { + /** + * Pagination options object + */ @Input() config: PaginationComponentOptions; + + /** + * Sort options object + */ @Input() sortConfig: SortOptions; - @Input() hideGear = false; + + /** + * A boolean representing if to hide gear pagination icon + */ + @Input() hideGear = true; + + /** + * A boolean representing if to hide pagination when there is only a page + */ @Input() hidePagerWhenSinglePage = true; + + /** + * The list of objects to paginate + */ private _objects: RemoteData>; + + /** + * Setter for _objects property + * @param objects + */ @Input() set objects(objects: RemoteData>) { this._objects = objects; } + + /** + * Getter for _objects property + */ get objects() { return this._objects; } @@ -69,6 +96,10 @@ export class ObjectDetailComponent { */ @Output() sortDirectionChange: EventEmitter = new EventEmitter(); + /** + * An event fired when the pagination is changed. + * Event's payload equals to the newly selected sort direction. + */ @Output() paginationChange: EventEmitter = new EventEmitter(); /** @@ -76,23 +107,38 @@ export class ObjectDetailComponent { * Event's payload equals to the newly selected sort field. */ @Output() sortFieldChange: EventEmitter = new EventEmitter(); - data: any = {}; + + /** + * Emit pageChange event + */ onPageChange(event) { this.pageChange.emit(event); } + /** + * Emit pageSizeChange event + */ onPageSizeChange(event) { this.pageSizeChange.emit(event); } + /** + * Emit sortDirectionChange event + */ onSortDirectionChange(event) { this.sortDirectionChange.emit(event); } + /** + * Emit sortFieldChange event + */ onSortFieldChange(event) { this.sortFieldChange.emit(event); } + /** + * Emit paginationChange event + */ onPaginationChange(event) { this.paginationChange.emit(event); } diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts index 3f28d5e76b..e54ae58398 100644 --- a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.spec.ts @@ -1,16 +1,15 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; +import { ActivatedRoute, Router } from '@angular/router'; import { of as observableOf } from 'rxjs'; -import { ActivatedRoute, Router } from '@angular/router'; import { RouterStub } from '../../testing/router-stub'; +import { WrapperDetailElementComponent } from './wrapper-detail-element.component'; -import { WrapperGridElementComponent } from '../../object-grid/wrapper-grid-element/wrapper-grid-element.component'; - -let wrapperGridElementComponent: WrapperGridElementComponent; -let fixture: ComponentFixture; +let wrapperDetailElementComponent: WrapperDetailElementComponent; +let fixture: ComponentFixture; const queryParam = 'test query'; const scopeParam = '7669c72a-3f2a-451f-a3b9-9210e7a4c02f'; const activatedRouteStub = { @@ -20,14 +19,14 @@ const activatedRouteStub = { }) }; -describe('WrapperGridElementComponent', () => { +describe('WrapperDetailElementComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ WrapperGridElementComponent ], + declarations: [ WrapperDetailElementComponent ], providers: [ { provide: ActivatedRoute, useValue: activatedRouteStub }, { provide: Router, useClass: RouterStub }, - { provide: 'objectElementProvider', useFactory: (wrapperGridElementComponent)} + { provide: 'objectElementProvider', useFactory: (WrapperDetailElementComponent)} ], schemas: [ NO_ERRORS_SCHEMA ] @@ -35,12 +34,11 @@ describe('WrapperGridElementComponent', () => { })); beforeEach(async(() => { - fixture = TestBed.createComponent(WrapperGridElementComponent); - wrapperGridElementComponent = fixture.componentInstance; - + fixture = TestBed.createComponent(WrapperDetailElementComponent); + wrapperDetailElementComponent = fixture.componentInstance; })); - it('should show the wrapper element containing the cards',() => { - expect(fixture.debugElement.query(By.css('ds-collection-grid-element'))).toBeDefined(); + it('should show the wrapper element containing the detail object',() => { + expect(fixture.debugElement.query(By.css('ds-workspaceitem-my-dspace-result-detail-element'))).toBeDefined(); }) }); diff --git a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts index 0d3bfef3fe..b959505eea 100644 --- a/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts +++ b/src/app/shared/object-detail/wrapper-detail-element/wrapper-detail-element.component.ts @@ -5,18 +5,37 @@ import { GenericConstructor } from '../../../core/shared/generic-constructor'; import { rendersDSOType } from '../../object-collection/shared/dso-element-decorator'; import { ListableObject } from '../../object-collection/shared/listable-object.model'; +/** + * This component renders a wrapper for an object in the detail view. + */ @Component({ selector: 'ds-wrapper-detail-element', styleUrls: ['./wrapper-detail-element.component.scss'], templateUrl: './wrapper-detail-element.component.html' }) export class WrapperDetailElementComponent implements OnInit { + + /** + * The listable object. + */ @Input() object: ListableObject; + + /** + * The instance of the injector. + */ objectInjector: Injector; + /** + * Initialize instance variables + * + * @param {Injector} injector + */ constructor(private injector: Injector) { } + /** + * Initialize injector + */ ngOnInit(): void { this.objectInjector = Injector.create({ providers: [{ provide: 'objectElementProvider', useFactory: () => (this.object), deps:[] }], @@ -25,6 +44,9 @@ export class WrapperDetailElementComponent implements OnInit { } + /** + * Return class name for the object to inject + */ getDetailElement(): string { const f: GenericConstructor = this.object.constructor as GenericConstructor; return rendersDSOType(f, ViewMode.Detail); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts index bddf40d8c2..4fd40e7318 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts @@ -10,11 +10,24 @@ import { Metadata } from '../../../core/shared/metadata.utils'; selector: 'ds-my-dspace-result-list-element', template: `` }) - export class MyDSpaceResultListElementComponent, K extends DSpaceObject> extends AbstractListableElementComponent { + + /** + * The result element object + */ dso: K; + + /** + * The array index of the result element + */ dsoIndex: number; + /** + * Initialize instance variables + * + * @param {ListableObject} listable + * @param {number} index + */ public constructor(@Inject('objectElementProvider') public listable: ListableObject, @Inject('indexElementProvider') public index: number) { super(listable); diff --git a/src/app/shared/object-list/object-list.component.spec.ts b/src/app/shared/object-list/object-list.component.spec.ts index 7e0b704a19..12ad032e98 100644 --- a/src/app/shared/object-list/object-list.component.spec.ts +++ b/src/app/shared/object-list/object-list.component.spec.ts @@ -6,7 +6,7 @@ import { By } from '@angular/platform-browser'; describe('ObjectListComponent', () => { let comp: ObjectListComponent; let fixture: ComponentFixture; - const testEvent = {test: 'test'} + const testEvent = {test: 'test'}; beforeEach(async(() => { TestBed.configureTestingModule({ diff --git a/src/app/shared/pagination/pagination.component.ts b/src/app/shared/pagination/pagination.component.ts index 825365c8c1..573c6a94f7 100644 --- a/src/app/shared/pagination/pagination.component.ts +++ b/src/app/shared/pagination/pagination.component.ts @@ -79,7 +79,7 @@ export class PaginationComponent implements OnDestroy, OnInit { @Output() sortFieldChange: EventEmitter = new EventEmitter(); /** - * An event fired when the sort field is changed. + * An event fired when the pagination is changed. * Event's payload equals to the newly selected sort field. */ @Output() paginationChange: EventEmitter = new EventEmitter(); From 080e0bee73b24b292baa80337ff66d47e8d6af63 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 Apr 2019 18:29:44 +0200 Subject: [PATCH 119/205] Added tests and comments --- .../tasks/claimed-task-data.service.spec.ts | 108 +++++++++++++++ .../core/tasks/claimed-task-data.service.ts | 57 +++++++- .../tasks/models/claimed-task-object.model.ts | 3 + .../normalized-claimed-task-object.model.ts | 2 +- .../normalized-pool-task-object.model.ts | 2 +- .../models/normalized-task-object.model.ts | 2 +- .../tasks/models/pool-task-object.model.ts | 3 + .../core/tasks/models/task-object.model.ts | 3 + .../core/tasks/pool-task-data.service.spec.ts | 70 ++++++++++ src/app/core/tasks/pool-task-data.service.ts | 35 ++++- .../tasks/task-response-parsing.service.ts | 16 +++ src/app/core/tasks/tasks.service.spec.ts | 130 ++++++++++++++++++ src/app/core/tasks/tasks.service.ts | 56 +++++++- src/app/shared/mocks/mock-request.service.ts | 1 + 14 files changed, 475 insertions(+), 13 deletions(-) create mode 100644 src/app/core/tasks/claimed-task-data.service.spec.ts create mode 100644 src/app/core/tasks/pool-task-data.service.spec.ts create mode 100644 src/app/core/tasks/tasks.service.spec.ts diff --git a/src/app/core/tasks/claimed-task-data.service.spec.ts b/src/app/core/tasks/claimed-task-data.service.spec.ts new file mode 100644 index 0000000000..dbd180b1b3 --- /dev/null +++ b/src/app/core/tasks/claimed-task-data.service.spec.ts @@ -0,0 +1,108 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; + +import { Store } from '@ngrx/store'; + +import { getMockRequestService } from '../../shared/mocks/mock-request.service'; +import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { CoreState } from '../core.reducers'; +import { ClaimedTaskDataService } from './claimed-task-data.service'; +import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; + +describe('ClaimedTaskDataService', () => { + let service: ClaimedTaskDataService; + let options: HttpOptions; + const taskEndpoint = 'https://rest.api/task'; + const linkPath = 'claimedtasks'; + const requestService: any = getMockRequestService(); + const halService: any = new HALEndpointServiceStub(taskEndpoint); + const rdbService = {} as RemoteDataBuildService; + const notificationsService = {} as NotificationsService; + const http = {} as HttpClient; + const comparator = {} as any; + const dataBuildService = { + normalize: (object) => object + } as NormalizedObjectBuildService; + const objectCache = { + addPatch: () => { + /* empty */ + }, + getObjectBySelfLink: () => { + /* empty */ + } + } as any; + const store = {} as Store; + + function initTestService(): ClaimedTaskDataService { + return new ClaimedTaskDataService( + requestService, + rdbService, + dataBuildService, + store, + objectCache, + halService, + notificationsService, + http, + comparator + ); + } + + beforeEach(() => { + service = initTestService(); + options = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/x-www-form-urlencoded'); + options.headers = headers; + }); + + describe('approveTask', () => { + + it('should call postToEndpoint method', () => { + const scopeId = '1234'; + const body = { + submit_approve: 'true' + }; + + spyOn(service, 'postToEndpoint'); + requestService.prepareBody.and.returnValue(body); + + service.approveTask(scopeId); + + expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, body, scopeId, options); + }); + }); + + describe('rejectTask', () => { + + it('should call postToEndpoint method', () => { + const scopeId = '1234'; + const reason = 'test reject'; + const body = { + submit_reject: 'true', + reason + }; + + spyOn(service, 'postToEndpoint'); + requestService.prepareBody.and.returnValue(body); + + service.rejectTask(reason, scopeId); + + expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, body, scopeId, options); + }); + }); + + describe('returnToPoolTask', () => { + + it('should call deleteById method', () => { + const scopeId = '1234'; + + spyOn(service, 'deleteById'); + + service.returnToPoolTask(scopeId); + + expect(service.deleteById).toHaveBeenCalledWith(linkPath, scopeId, options); + }); + }); +}); diff --git a/src/app/core/tasks/claimed-task-data.service.ts b/src/app/core/tasks/claimed-task-data.service.ts index f1175d1b1c..52d4f0744f 100644 --- a/src/app/core/tasks/claimed-task-data.service.ts +++ b/src/app/core/tasks/claimed-task-data.service.ts @@ -14,12 +14,37 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { ProcessTaskResponse } from './models/process-task-response'; +/** + * The service handling all REST requests for ClaimedTask + */ @Injectable() export class ClaimedTaskDataService extends TasksService { + + /** + * The endpoint link name + */ protected linkPath = 'claimedtasks'; + + /** + * When true, a new request is always dispatched + */ protected forceBypassCache = true; + /** + * Initialize instance variables + * + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {NormalizedObjectBuildService} dataBuildService + * @param {Store} store + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {DSOChangeAnalyzer { super(); } - public approveTask(scopeId: string): Observable { + /** + * Make a request to approve the given task + * + * @param scopeId + * The task id + * @return {Observable} + * Emit the server response + */ + public approveTask(scopeId: string): Observable { const body = { submit_approve: 'true' }; return this.postToEndpoint(this.linkPath, this.requestService.prepareBody(body), scopeId, this.makeHttpOptions()); } - public rejectTask(reason: string, scopeId: string): Observable { + /** + * Make a request to reject the given task + * + * @param reason + * The reason of reject + * @param scopeId + * The task id + * @return {Observable} + * Emit the server response + */ + public rejectTask(reason: string, scopeId: string): Observable { const body = { submit_reject: 'true', reason @@ -48,7 +91,15 @@ export class ClaimedTaskDataService extends TasksService { return this.postToEndpoint(this.linkPath, this.requestService.prepareBody(body), scopeId, this.makeHttpOptions()); } - public returnToPoolTask(scopeId: string): Observable { + /** + * Make a request to return the given task to the pool + * + * @param scopeId + * The task id + * @return {Observable} + * Emit the server response + */ + public returnToPoolTask(scopeId: string): Observable { return this.deleteById(this.linkPath, scopeId, this.makeHttpOptions()); } diff --git a/src/app/core/tasks/models/claimed-task-object.model.ts b/src/app/core/tasks/models/claimed-task-object.model.ts index d0474a1aa8..212e75ed95 100644 --- a/src/app/core/tasks/models/claimed-task-object.model.ts +++ b/src/app/core/tasks/models/claimed-task-object.model.ts @@ -1,5 +1,8 @@ import { TaskObject } from './task-object.model'; +/** + * A model class for a ClaimedTask. + */ export class ClaimedTask extends TaskObject { } diff --git a/src/app/core/tasks/models/normalized-claimed-task-object.model.ts b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts index e6a9096cb4..c2c3f12bc4 100644 --- a/src/app/core/tasks/models/normalized-claimed-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-claimed-task-object.model.ts @@ -5,7 +5,7 @@ import { ClaimedTask } from './claimed-task-object.model'; import { ResourceType } from '../../shared/resource-type'; /** - * A model class for a NormalizedClaimedTaskObject. + * A normalized model class for a ClaimedTask. */ @mapsTo(ClaimedTask) @inheritSerialization(NormalizedTaskObject) diff --git a/src/app/core/tasks/models/normalized-pool-task-object.model.ts b/src/app/core/tasks/models/normalized-pool-task-object.model.ts index beb2d15e8c..22cda6ff9c 100644 --- a/src/app/core/tasks/models/normalized-pool-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-pool-task-object.model.ts @@ -5,7 +5,7 @@ import { mapsTo, relationship } from '../../cache/builders/build-decorators'; import { ResourceType } from '../../shared/resource-type'; /** - * A model class for a NormalizedPoolTaskObject. + * A normalized model class for a PoolTask. */ @mapsTo(PoolTask) @inheritSerialization(NormalizedTaskObject) diff --git a/src/app/core/tasks/models/normalized-task-object.model.ts b/src/app/core/tasks/models/normalized-task-object.model.ts index 7e0fb3f6bb..52c274e3a8 100644 --- a/src/app/core/tasks/models/normalized-task-object.model.ts +++ b/src/app/core/tasks/models/normalized-task-object.model.ts @@ -6,7 +6,7 @@ import { TaskObject } from './task-object.model'; import { DSpaceObject } from '../../shared/dspace-object.model'; /** - * An abstract model class for a DSpaceObject. + * An abstract normalized model class for a TaskObject. */ @mapsTo(TaskObject) @inheritSerialization(NormalizedDSpaceObject) diff --git a/src/app/core/tasks/models/pool-task-object.model.ts b/src/app/core/tasks/models/pool-task-object.model.ts index fcaf4309a1..8d98d3e1a5 100644 --- a/src/app/core/tasks/models/pool-task-object.model.ts +++ b/src/app/core/tasks/models/pool-task-object.model.ts @@ -1,5 +1,8 @@ import { TaskObject } from './task-object.model'; +/** + * A model class for a PoolTask. + */ export class PoolTask extends TaskObject { } diff --git a/src/app/core/tasks/models/task-object.model.ts b/src/app/core/tasks/models/task-object.model.ts index 1475bdb14a..97a1c9f59e 100644 --- a/src/app/core/tasks/models/task-object.model.ts +++ b/src/app/core/tasks/models/task-object.model.ts @@ -6,6 +6,9 @@ import { ListableObject } from '../../../shared/object-collection/shared/listabl import { RemoteData } from '../../data/remote-data'; import { Workflowitem } from '../../submission/models/workflowitem.model'; +/** + * An abstract model class for a TaskObject. + */ export class TaskObject extends DSpaceObject implements CacheableObject, ListableObject { /** diff --git a/src/app/core/tasks/pool-task-data.service.spec.ts b/src/app/core/tasks/pool-task-data.service.spec.ts new file mode 100644 index 0000000000..7f40c6e89c --- /dev/null +++ b/src/app/core/tasks/pool-task-data.service.spec.ts @@ -0,0 +1,70 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; + +import { Store } from '@ngrx/store'; + +import { getMockRequestService } from '../../shared/mocks/mock-request.service'; +import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { CoreState } from '../core.reducers'; +import { PoolTaskDataService } from './pool-task-data.service'; +import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; + +describe('PoolTaskDataService', () => { + let service: PoolTaskDataService; + let options: HttpOptions; + const taskEndpoint = 'https://rest.api/task'; + const linkPath = 'pooltasks'; + const requestService = getMockRequestService(); + const halService: any = new HALEndpointServiceStub(taskEndpoint); + const rdbService = {} as RemoteDataBuildService; + const notificationsService = {} as NotificationsService; + const http = {} as HttpClient; + const comparator = {} as any; + const dataBuildService = { + normalize: (object) => object + } as NormalizedObjectBuildService; + const objectCache = { + addPatch: () => { + /* empty */ + }, + getObjectBySelfLink: () => { + /* empty */ + } + } as any; + const store = {} as Store; + + function initTestService(): PoolTaskDataService { + return new PoolTaskDataService( + requestService, + rdbService, + dataBuildService, + store, + objectCache, + halService, + notificationsService, + http, + comparator + ); + } + + beforeEach(() => { + service = initTestService(); + options = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/x-www-form-urlencoded'); + options.headers = headers; + }); + + describe('claimTask', () => { + + it('should call postToEndpoint method', () => { + spyOn(service, 'postToEndpoint'); + const scopeId = '1234'; + service.claimTask(scopeId); + + expect(service.postToEndpoint).toHaveBeenCalledWith(linkPath, {}, scopeId, options); + }); + }); +}); diff --git a/src/app/core/tasks/pool-task-data.service.ts b/src/app/core/tasks/pool-task-data.service.ts index fb1d53420b..1a93450d4d 100644 --- a/src/app/core/tasks/pool-task-data.service.ts +++ b/src/app/core/tasks/pool-task-data.service.ts @@ -14,12 +14,37 @@ import { NormalizedObjectBuildService } from '../cache/builders/normalized-objec import { ObjectCacheService } from '../cache/object-cache.service'; import { NotificationsService } from '../../shared/notifications/notifications.service'; import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { ProcessTaskResponse } from './models/process-task-response'; +/** + * The service handling all REST requests for PoolTask + */ @Injectable() export class PoolTaskDataService extends TasksService { + + /** + * The endpoint link name + */ protected linkPath = 'pooltasks'; + + /** + * When true, a new request is always dispatched + */ protected forceBypassCache = true; + /** + * Initialize instance variables + * + * @param {RequestService} requestService + * @param {RemoteDataBuildService} rdbService + * @param {NormalizedObjectBuildService} dataBuildService + * @param {Store} store + * @param {ObjectCacheService} objectCache + * @param {HALEndpointService} halService + * @param {NotificationsService} notificationsService + * @param {HttpClient} http + * @param {DSOChangeAnalyzer { super(); } - public claimTask(scopeId: string): Observable { + /** + * Make a request to claim the given task + * + * @param scopeId + * The task id + * @return {Observable} + * Emit the server response + */ + public claimTask(scopeId: string): Observable { return this.postToEndpoint(this.linkPath, {}, scopeId, this.makeHttpOptions()); } } diff --git a/src/app/core/tasks/task-response-parsing.service.ts b/src/app/core/tasks/task-response-parsing.service.ts index d12b1b22f8..7445f9d267 100644 --- a/src/app/core/tasks/task-response-parsing.service.ts +++ b/src/app/core/tasks/task-response-parsing.service.ts @@ -11,17 +11,33 @@ import { ObjectCacheService } from '../cache/object-cache.service'; import { NormalizedObjectFactory } from '../cache/models/normalized-object-factory'; import { ErrorResponse, RestResponse, TaskResponse } from '../cache/response.models'; +/** + * Provides methods to parse response for a task request. + */ @Injectable() export class TaskResponseParsingService extends BaseResponseParsingService implements ResponseParsingService { protected objectFactory = NormalizedObjectFactory; protected toCache = false; + /** + * Initialize instance variables + * + * @param {GlobalConfig} EnvConfig + * @param {ObjectCacheService} objectCache + */ constructor(@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig, protected objectCache: ObjectCacheService,) { super(); } + /** + * Parses data from the tasks endpoints + * + * @param {RestRequest} request + * @param {DSpaceRESTV2Response} data + * @returns {RestResponse} + */ parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { if (this.isSuccessStatus(data.statusCode)) { return new TaskResponse( data.statusCode, data.statusText); diff --git a/src/app/core/tasks/tasks.service.spec.ts b/src/app/core/tasks/tasks.service.spec.ts new file mode 100644 index 0000000000..207a09225b --- /dev/null +++ b/src/app/core/tasks/tasks.service.spec.ts @@ -0,0 +1,130 @@ +import { getTestScheduler } from 'jasmine-marbles'; +import { TestScheduler } from 'rxjs/testing'; + +import { getMockRequestService } from '../../shared/mocks/mock-request.service'; +import { TasksService } from './tasks.service'; +import { RequestService } from '../data/request.service'; +import { TaskDeleteRequest, TaskPostRequest } from '../data/request.models'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { HALEndpointServiceStub } from '../../shared/testing/hal-endpoint-service-stub'; +import { TaskObject } from './models/task-object.model'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { DSOChangeAnalyzer } from '../data/dso-change-analyzer.service'; +import { ChangeAnalyzer } from '../data/change-analyzer'; +import { compare, Operation } from 'fast-json-patch'; +import { NormalizedTaskObject } from './models/normalized-task-object.model'; +import { HttpOptions } from '../dspace-rest-v2/dspace-rest-v2.service'; + +const LINK_NAME = 'test'; + +/* tslint:disable:max-classes-per-file */ +class TestTask extends TaskObject { +} + +class TestService extends TasksService { + protected linkPath = LINK_NAME; + protected forceBypassCache = true; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DSOChangeAnalyzer) { + super(); + } +} + +class NormalizedTestTaskObject extends NormalizedTaskObject { +} + +class DummyChangeAnalyzer implements ChangeAnalyzer { + diff(object1: NormalizedTestTaskObject, object2: NormalizedTestTaskObject): Operation[] { + return compare((object1 as any).metadata, (object2 as any).metadata); + } + +} +/* tslint:enable:max-classes-per-file */ + +describe('TasksService', () => { + let scheduler: TestScheduler; + let service: TestService; + const taskEndpoint = 'https://rest.api/task'; + const linkPath = 'testTask'; + const requestService = getMockRequestService(); + const halService: any = new HALEndpointServiceStub(taskEndpoint); + const rdbService = {} as RemoteDataBuildService; + const notificationsService = {} as NotificationsService; + const http = {} as HttpClient; + const comparator = new DummyChangeAnalyzer() as any; + const dataBuildService = { + normalize: (object) => object + } as NormalizedObjectBuildService; + const objectCache = { + addPatch: () => { + /* empty */ + }, + getObjectBySelfLink: () => { + /* empty */ + } + } as any; + const store = {} as Store; + + function initTestService(): TestService { + return new TestService( + requestService, + rdbService, + dataBuildService, + store, + objectCache, + halService, + notificationsService, + http, + comparator + ); + } + + beforeEach(() => { + scheduler = getTestScheduler(); + service = initTestService(); + const options: HttpOptions = Object.create({}); + let headers = new HttpHeaders(); + headers = headers.append('Content-Type', 'application/x-www-form-urlencoded'); + options.headers = headers; + + }); + + describe('postToEndpoint', () => { + + it('should configure a new TaskPostRequest', () => { + const expected = new TaskPostRequest(requestService.generateRequestId(), `${taskEndpoint}/${linkPath}`, {}); + scheduler.schedule(() => service.postToEndpoint('testTask', {}).subscribe()); + scheduler.flush(); + + expect(requestService.configure).toHaveBeenCalledWith(expected); + }); + }); + + describe('deleteById', () => { + + it('should configure a new TaskPostRequest', () => { + const scopeId = '1234'; + const expected = new TaskDeleteRequest(requestService.generateRequestId(), `${taskEndpoint}/${linkPath}/${scopeId}`, null); + scheduler.schedule(() => service.deleteById('testTask', scopeId).subscribe()); + scheduler.flush(); + + expect(requestService.configure).toHaveBeenCalledWith(expected); + }); + }); + +}); diff --git a/src/app/core/tasks/tasks.service.ts b/src/app/core/tasks/tasks.service.ts index 19ef81bd19..f39b144c6a 100644 --- a/src/app/core/tasks/tasks.service.ts +++ b/src/app/core/tasks/tasks.service.ts @@ -13,12 +13,23 @@ import { getResponseFromEntry } from '../shared/operators'; import { ErrorResponse, MessageResponse, RestResponse } from '../cache/response.models'; import { CacheableObject } from '../cache/object-cache.reducer'; +/** + * An abstract class that provides methods to handle task requests. + */ export abstract class TasksService extends DataService { public getBrowseEndpoint(options: FindAllOptions): Observable { return this.halService.getEndpoint(this.linkPath); } + /** + * Fetch a RestRequest + * + * @param requestId + * The base endpoint for the type of object + * @return Observable + * server response + */ protected fetchRequest(requestId: string): Observable { const responses = this.requestService.getByUUID(requestId).pipe( getResponseFromEntry() @@ -39,14 +50,32 @@ export abstract class TasksService extends DataServic return observableMerge(errorResponses, successResponses); } + /** + * Create the HREF for a specific submission object based on its identifier + * + * @param endpoint + * The base endpoint for the type of object + * @param resourceID + * The identifier for the object + */ protected getEndpointByIDHref(endpoint, resourceID): string { return isNotEmpty(resourceID) ? `${endpoint}/${resourceID}` : `${endpoint}`; } - protected getEndpointByMethod(endpoint: string, method: string): string { - return isNotEmpty(method) ? `${endpoint}/${method}` : `${endpoint}`; - } - + /** + * Make a new post request + * + * @param linkPath + * The endpoint link name + * @param body + * The request body + * @param scopeId + * The task id to be removed + * @param options + * The HttpOptions object + * @return Observable + * server response + */ public postToEndpoint(linkPath: string, body: any, scopeId?: string, options?: HttpOptions): Observable { const requestId = this.requestService.generateRequestId(); return this.halService.getEndpoint(linkPath).pipe( @@ -59,9 +88,21 @@ export abstract class TasksService extends DataServic distinctUntilChanged()); } - public deleteById(linkName: string, scopeId: string, options?: HttpOptions): Observable { + /** + * Delete an existing task on the server + * + * @param linkPath + * The endpoint link name + * @param scopeId + * The task id to be removed + * @param options + * The HttpOptions object + * @return Observable + * server response + */ + public deleteById(linkPath: string, scopeId: string, options?: HttpOptions): Observable { const requestId = this.requestService.generateRequestId(); - return this.halService.getEndpoint(linkName || this.linkPath).pipe( + return this.halService.getEndpoint(linkPath || this.linkPath).pipe( filter((href: string) => isNotEmpty(href)), distinctUntilChanged(), map((endpointURL: string) => this.getEndpointByIDHref(endpointURL, scopeId)), @@ -71,6 +112,9 @@ export abstract class TasksService extends DataServic distinctUntilChanged()); } + /** + * Create a new HttpOptions + */ protected makeHttpOptions() { const options: HttpOptions = Object.create({}); let headers = new HttpHeaders(); diff --git a/src/app/shared/mocks/mock-request.service.ts b/src/app/shared/mocks/mock-request.service.ts index ce09f6d85e..ae19c00a30 100644 --- a/src/app/shared/mocks/mock-request.service.ts +++ b/src/app/shared/mocks/mock-request.service.ts @@ -8,6 +8,7 @@ export function getMockRequestService(requestEntry$: Observable = generateRequestId: 'clients/b186e8ce-e99c-4183-bc9a-42b4821bdb78', getByHref: requestEntry$, getByUUID: requestEntry$, + prepareBody: jasmine.createSpy('prepareBody'), /* tslint:disable:no-empty */ removeByHrefSubstring: () => {} /* tslint:enable:no-empty */ From 25d955c8046d80b59fb617944961545143936649 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 2 Apr 2019 19:21:00 +0200 Subject: [PATCH 120/205] Added tests and comments --- .../my-dspace-item-status.component.spec.ts | 96 +++++++++++++++++++ .../my-dspace-item-status.component.ts | 18 +++- .../item-submitter.component.html | 2 +- .../item-submitter.component.spec.ts | 68 +++++++++++++ .../item-submitter.component.ts | 18 +++- 5 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts create mode 100644 src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts new file mode 100644 index 0000000000..f3b58665f5 --- /dev/null +++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.spec.ts @@ -0,0 +1,96 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { RemoteData } from '../../../../core/data/remote-data'; + +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; +import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; +import { EPersonMock } from '../../../testing/eperson-mock'; +import { MyDSpaceItemStatusComponent } from './my-dspace-item-status.component'; +import { MyDspaceItemStatusType } from './my-dspace-item-status-type'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; +import { By } from '@angular/platform-browser'; + +let component: MyDSpaceItemStatusComponent; +let fixture: ComponentFixture; + +let mockResultObject: PoolTask; + +const rdSumbitter = new RemoteData(false, false, true, null, EPersonMock); +const workflowitem = Object.assign(new Workflowitem(), { submitter: observableOf(rdSumbitter) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('MyDSpaceItemStatusComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [MyDSpaceItemStatusComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MyDSpaceItemStatusComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MyDSpaceItemStatusComponent); + component = fixture.componentInstance; + }); + + it('should display badge', () => { + const badge = fixture.debugElement.query(By.css('span')); + expect(badge).toBeDefined(); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.REJECTED; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.REJECTED); + expect(component.badgeClass).toBe('text-light badge badge-danger'); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.VALIDATION; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.VALIDATION); + expect(component.badgeClass).toBe('text-light badge badge-warning'); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.WAITING_CONTROLLER; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.WAITING_CONTROLLER); + expect(component.badgeClass).toBe('text-light badge badge-info'); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.IN_PROGRESS; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.IN_PROGRESS); + expect(component.badgeClass).toBe('text-light badge badge-primary'); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.ACCEPTED; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.ACCEPTED); + expect(component.badgeClass).toBe('text-light badge badge-success'); + }); + + it('should init badge content and class', () => { + component.status = MyDspaceItemStatusType.WORKFLOW; + fixture.detectChanges(); + expect(component.badgeContent).toBe(MyDspaceItemStatusType.WORKFLOW); + expect(component.badgeClass).toBe('text-light badge badge-info'); + }); +}); diff --git a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts index 2ae1bc2ef5..87bb95ebb5 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component.ts @@ -1,18 +1,34 @@ import { Component, Input, OnInit } from '@angular/core'; import { MyDspaceItemStatusType } from './my-dspace-item-status-type'; +/** + * This component represents a badge with mydspace item status + */ @Component({ selector: 'ds-mydspace-item-status', styleUrls: ['./my-dspace-item-status.component.scss'], templateUrl: './my-dspace-item-status.component.html' }) - export class MyDSpaceItemStatusComponent implements OnInit { + /** + * This mydspace item status + */ @Input() status: MyDspaceItemStatusType; + + /** + * This badge class + */ public badgeClass: string; + + /** + * This badge content + */ public badgeContent: string; + /** + * Initialize badge content and class + */ ngOnInit() { this.badgeContent = this.status; this.badgeClass = 'text-light badge '; diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html index 1f13fbfa2c..35ebb5f1bf 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.html @@ -1,3 +1,3 @@
    - {{'submission.workflow.tasks.generic.submitter' | translate}} : {{(submitter | async)?.name}} + {{'submission.workflow.tasks.generic.submitter' | translate}} : {{(submitter$ | async)?.name}}
    diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts new file mode 100644 index 0000000000..77460a3674 --- /dev/null +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.spec.ts @@ -0,0 +1,68 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { of as observableOf } from 'rxjs'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { cold } from 'jasmine-marbles'; + +import { RemoteData } from '../../../../core/data/remote-data'; +import { ItemSubmitterComponent } from './item-submitter.component'; +import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; +import { PoolTask } from '../../../../core/tasks/models/pool-task-object.model'; +import { EPersonMock } from '../../../testing/eperson-mock'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; +import { By } from '@angular/platform-browser'; + +let component: ItemSubmitterComponent; +let fixture: ComponentFixture; + +const compIndex = 1; + +let mockResultObject: PoolTask; + +const rdSumbitter = new RemoteData(false, false, true, null, EPersonMock); +const workflowitem = Object.assign(new Workflowitem(), { submitter: observableOf(rdSumbitter) }); +const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); +mockResultObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); + +describe('ItemSubmitterComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ItemSubmitterComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemSubmitterComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(async(() => { + fixture = TestBed.createComponent(ItemSubmitterComponent); + component = fixture.componentInstance; + })); + + beforeEach(() => { + component.object = mockResultObject; + fixture.detectChanges(); + }); + + it('should init submitter properly', () => { + expect(component.submitter$).toBeObservable(cold('(b|)', { + b: EPersonMock + })); + }); + + it('should show a badge with submitter name', () => { + const badge = fixture.debugElement.query(By.css('.badge')); + + expect(badge).toBeDefined(); + expect(badge.nativeElement.innerHTML).toBe(EPersonMock.name); + }); +}); diff --git a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts index b800686714..f752fa6f04 100644 --- a/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts +++ b/src/app/shared/object-collection/shared/mydspace-item-submitter/item-submitter.component.ts @@ -8,19 +8,31 @@ import { RemoteData } from '../../../../core/data/remote-data'; import { isNotEmpty, isNotUndefined } from '../../../empty.util'; import { Workflowitem } from '../../../../core/submission/models/workflowitem.model'; +/** + * This component represents a badge with submitter information. + */ @Component({ selector: 'ds-item-submitter', styleUrls: ['./item-submitter.component.scss'], templateUrl: './item-submitter.component.html' }) - export class ItemSubmitterComponent implements OnInit { + + /** + * The target object + */ @Input() object: any; - submitter: Observable; + /** + * The Eperson object + */ + submitter$: Observable; + /** + * Initialize submitter object + */ ngOnInit() { - this.submitter = (this.object.workflowitem as Observable>).pipe( + this.submitter$ = (this.object.workflowitem as Observable>).pipe( filter((rd: RemoteData) => (rd.hasSucceeded && isNotUndefined(rd.payload))), flatMap((rd: RemoteData) => rd.payload.submitter as Observable>), find((rd: RemoteData) => rd.hasSucceeded && isNotEmpty(rd.payload)), From af2a995e24503ca293def41d557118f6233900af Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 14:26:06 +0200 Subject: [PATCH 121/205] Fixed build error --- .../claimed-task-actions-return-to-pool.component.html | 2 +- .../claimed-task-actions-return-to-pool.component.spec.ts | 2 +- .../claimed-task-actions-return-to-pool.component.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html index 2002c55528..702ce75e7f 100644 --- a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html @@ -2,7 +2,7 @@ [className]="'btn btn-secondary ' + wrapperClass" ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}" [disabled]="processingReturnToPool" - (click)="returnToPool()"> + (click)="confirmReturnToPool()"> {{'submission.workflow.tasks.generic.processing' | translate}} {{'submission.workflow.tasks.claimed.return' | translate}} diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts index fcb370595a..d461d9e055 100644 --- a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.spec.ts @@ -56,7 +56,7 @@ describe('ClaimedTaskActionsReturnToPoolComponent', () => { it('should emit return to pool event', () => { spyOn(component.returnToPool, 'emit'); - component.confirmApprove(); + component.confirmReturnToPool(); fixture.detectChanges(); expect(component.returnToPool.emit).toHaveBeenCalled(); diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts index 381b8f1afe..1dfe91eb5b 100644 --- a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts @@ -24,9 +24,9 @@ export class ClaimedTaskActionsReturnToPoolComponent { @Output() returnToPool: EventEmitter = new EventEmitter(); /** - * Emit approve event + * Emit returnToPool event */ - confirmApprove() { + confirmReturnToPool() { this.returnToPool.emit(); } } From 7c48504c8224e2583f0e027a5ef9e0d4dfe6f596 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 15:27:08 +0200 Subject: [PATCH 122/205] renamed dspaceObject to indexableObject --- src/app/+my-dspace-page/my-dspace-result.model.ts | 2 +- .../+search-page/normalized-search-result.model.ts | 4 ++-- src/app/+search-page/search-result.model.ts | 2 +- .../+search-page/search-service/search.service.ts | 6 +++--- .../core/data/mydspace-response-parsing.service.ts | 12 ++++++------ src/app/core/shared/operators.ts | 2 +- .../dso-selector/dso-selector.component.html | 4 ++-- .../dso-selector/dso-selector.component.spec.ts | 2 +- ...my-dspace-result-detail-element.component.spec.ts | 4 ++-- .../item-detail-preview.component.html | 2 +- ...my-dspace-result-detail-element.component.spec.ts | 4 ++-- .../my-dspace-result-detail-element.component.ts | 2 +- ...my-dspace-result-detail-element.component.spec.ts | 4 ++-- ...my-dspace-result-detail-element.component.spec.ts | 4 ++-- ...my-dspace-result-detail-element.component.spec.ts | 4 ++-- ...tion-search-result-grid-element.component.spec.ts | 8 ++++---- ...nity-search-result-grid-element.component.spec.ts | 8 ++++---- ...item-search-result-grid-element.component.spec.ts | 12 ++++++------ .../search-result-grid-element.component.ts | 2 +- ...d-my-dspace-result-list-element.component.spec.ts | 4 ++-- .../item-list-preview.component.html | 2 +- ...m-my-dspace-result-list-element.component.spec.ts | 4 ++-- .../my-dspace-result-list-element.component.ts | 2 +- ...l-my-dspace-result-list-element.component.spec.ts | 4 ++-- ...m-my-dspace-result-list-element.component.spec.ts | 4 ++-- ...m-my-dspace-result-list-element.component.spec.ts | 4 ++-- ...tion-search-result-list-element.component.spec.ts | 8 ++++---- ...nity-search-result-list-element.component.spec.ts | 8 ++++---- ...item-search-result-list-element.component.spec.ts | 12 ++++++------ .../search-result-list-element.component.ts | 2 +- 30 files changed, 71 insertions(+), 71 deletions(-) diff --git a/src/app/+my-dspace-page/my-dspace-result.model.ts b/src/app/+my-dspace-page/my-dspace-result.model.ts index 5605c746e8..d300ed0bc8 100644 --- a/src/app/+my-dspace-page/my-dspace-result.model.ts +++ b/src/app/+my-dspace-page/my-dspace-result.model.ts @@ -9,7 +9,7 @@ export class MyDSpaceResult implements ListableObject { /** * The DSpaceObject that was found */ - dspaceObject: T; + indexableObject: T; /** * The metadata that was used to find this item, hithighlighted diff --git a/src/app/+search-page/normalized-search-result.model.ts b/src/app/+search-page/normalized-search-result.model.ts index cb1238936a..32f3217b54 100644 --- a/src/app/+search-page/normalized-search-result.model.ts +++ b/src/app/+search-page/normalized-search-result.model.ts @@ -9,8 +9,8 @@ export class NormalizedSearchResult implements ListableObject { /** * The UUID of the DSpaceObject that was found */ - @autoserializeAs(String, 'resultObject') - dspaceObject: string; + @autoserialize + indexableObject: string; /** * The metadata that was used to find this item, hithighlighted diff --git a/src/app/+search-page/search-result.model.ts b/src/app/+search-page/search-result.model.ts index ff865610c6..0354edbc6b 100644 --- a/src/app/+search-page/search-result.model.ts +++ b/src/app/+search-page/search-result.model.ts @@ -9,7 +9,7 @@ export class SearchResult implements ListableObject { /** * The DSpaceObject that was found */ - dspaceObject: T; + indexableObject: T; /** * The metadata that was used to find this item, hithighlighted diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index ed19f1df68..568bf0fed9 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -133,9 +133,9 @@ export class SearchService implements OnDestroy { const dsoObs: Observable> = sqrObs.pipe( map((sqr: SearchQueryResponse) => { return sqr.objects - .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.dspaceObject)) + .filter((nsr: NormalizedSearchResult) => isNotUndefined(nsr.indexableObject)) .map((nsr: NormalizedSearchResult) => { - return this.rdb.buildSingle(nsr.dspaceObject); + return this.rdb.buildSingle(nsr.indexableObject); }) }), switchMap((input: Array>>) => this.rdb.aggregate(input)), @@ -150,7 +150,7 @@ export class SearchService implements OnDestroy { const constructor: GenericConstructor = dsos.payload[index].constructor as GenericConstructor; co = getSearchResultFor(constructor, searchOptions.configuration); return Object.assign(new co(), object, { - dspaceObject: dsos.payload[index] + indexableObject: dsos.payload[index] }); } else { return undefined; diff --git a/src/app/core/data/mydspace-response-parsing.service.ts b/src/app/core/data/mydspace-response-parsing.service.ts index 076103158f..a6945e27b4 100644 --- a/src/app/core/data/mydspace-response-parsing.service.ts +++ b/src/app/core/data/mydspace-response-parsing.service.ts @@ -40,7 +40,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { const dsoSelfLinks = payload._embedded.objects .filter((object) => hasValue(object._embedded)) - .map((object) => object._embedded.resultObject) + .map((object) => object._embedded.indexableObject) .map((dso) => this.dsoParser.parse(request, { payload: dso, statusCode: data.statusCode, @@ -52,7 +52,7 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { const objects = payload._embedded.objects .filter((object) => hasValue(object._embedded)) .map((object, index) => Object.assign({}, object, { - resultObject: dsoSelfLinks[index], + indexableObject: dsoSelfLinks[index], hitHighlights: hitHighlights[index], _embedded: this.filterEmbeddedObjects(object) })); @@ -63,13 +63,13 @@ export class MyDSpaceResponseParsingService implements ResponseParsingService { protected filterEmbeddedObjects(object) { const allowedEmbeddedKeys = ['submitter', 'item', 'workspaceitem', 'workflowitem']; - if (object._embedded.resultObject && object._embedded.resultObject._embedded) { + if (object._embedded.indexableObject && object._embedded.indexableObject._embedded) { return Object.assign({}, object._embedded, { - resultObject: Object.assign({}, object._embedded.resultObject, { - _embedded: Object.keys(object._embedded.resultObject._embedded) + indexableObject: Object.assign({}, object._embedded.indexableObject, { + _embedded: Object.keys(object._embedded.indexableObject._embedded) .filter((key) => allowedEmbeddedKeys.includes(key)) .reduce((obj, key) => { - obj[key] = object._embedded.resultObject._embedded[key]; + obj[key] = object._embedded.indexableObject._embedded[key]; return obj; }, {}) }) diff --git a/src/app/core/shared/operators.ts b/src/app/core/shared/operators.ts index ce9740a0fc..29bb2f6b7d 100644 --- a/src/app/core/shared/operators.ts +++ b/src/app/core/shared/operators.ts @@ -75,7 +75,7 @@ export const toDSpaceObjectListRD = () => source.pipe( filter((rd: RemoteData>>) => rd.hasSucceeded), map((rd: RemoteData>>) => { - const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult) => searchResult.dspaceObject); + const dsoPage: T[] = rd.payload.page.map((searchResult: SearchResult) => searchResult.indexableObject); const payload = Object.assign(rd.payload, { page: dsoPage }) as PaginatedList; return Object.assign(rd, { payload: payload }); }) diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html index da6bfa40ba..662144823d 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.html +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.html @@ -13,8 +13,8 @@
  • diff --git a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts index f9d1567245..5ec553222b 100644 --- a/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts +++ b/src/app/shared/dso-selector/dso-selector/dso-selector.component.spec.ts @@ -27,7 +27,7 @@ describe('DSOSelectorComponent', () => { language: undefined })] }; - searchResult.dspaceObject = item; + searchResult.indexableObject = item; searchResult.hitHighlights = {}; const searchService = jasmine.createSpyObj('searchService', { search: observableOf(new RemoteData(false, false, true, undefined, new PaginatedList(undefined, [searchResult]))) diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts index 38c4f461cd..d3307721ff 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component.spec.ts @@ -52,7 +52,7 @@ const item = Object.assign(new Item(), { const rdItem = new RemoteData(false, false, true, null, item); const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); -mockResultObject.dspaceObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); +mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); describe('ClaimedMyDSpaceResultDetailElementComponent', () => { beforeEach(async(() => { @@ -75,7 +75,7 @@ describe('ClaimedMyDSpaceResultDetailElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html index 3cb4665884..10e7e98d87 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html @@ -24,4 +24,4 @@
    - + diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts index c48d755d61..b757879aac 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component.spec.ts @@ -17,7 +17,7 @@ const compIndex = 1; const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult(); mockResultObject.hitHighlights = {}; -mockResultObject.dspaceObject = Object.assign(new Item(), { +mockResultObject.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.title': [ @@ -68,7 +68,7 @@ describe('ItemMyDSpaceResultDetailElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts index 4017026396..47a44d3132 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/my-dspace-result-detail-element.component.ts @@ -24,7 +24,7 @@ export class MyDSpaceResultDetailElementComponent, K */ public constructor(@Inject('objectElementProvider') public detailable: ListableObject) { super(detailable); - this.dso = this.object.dspaceObject; + this.dso = this.object.indexableObject; } /** diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts index b108d48c33..0eea01aa1c 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-element.component.spec.ts @@ -52,7 +52,7 @@ const item = Object.assign(new Item(), { const rdItem = new RemoteData(false, false, true, null, item); const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); -mockResultObject.dspaceObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); +mockResultObject.indexableObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); describe('PoolMyDSpaceResultDetailElementComponent', () => { beforeEach(async(() => { @@ -75,7 +75,7 @@ describe('PoolMyDSpaceResultDetailElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts index 3c6505218e..f8bdbf9fd6 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component.spec.ts @@ -49,7 +49,7 @@ const item = Object.assign(new Item(), { } }); const rd = new RemoteData(false, false, true, null, item); -mockResultObject.dspaceObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); +mockResultObject.indexableObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); describe('WorkflowitemMyDSpaceResultDetailElementComponent', () => { beforeEach(async(() => { @@ -72,7 +72,7 @@ describe('WorkflowitemMyDSpaceResultDetailElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts index 830ab80cf1..301181e65c 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component.spec.ts @@ -49,7 +49,7 @@ const item = Object.assign(new Item(), { } }); const rd = new RemoteData(false, false, true, null, item); -mockResultObject.dspaceObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); +mockResultObject.indexableObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); describe('WorkspaceitemMyDSpaceResultDetailElementComponent', () => { beforeEach(async(() => { @@ -72,7 +72,7 @@ describe('WorkspaceitemMyDSpaceResultDetailElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts index e8f8b1330e..07f3960d55 100644 --- a/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component.spec.ts @@ -17,7 +17,7 @@ const truncatableServiceStub: any = { const mockCollectionWithAbstract: CollectionSearchResult = new CollectionSearchResult(); mockCollectionWithAbstract.hitHighlights = {}; -mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), { +mockCollectionWithAbstract.indexableObject = Object.assign(new Collection(), { metadata: { 'dc.description.abstract': [ { @@ -30,7 +30,7 @@ mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), { const mockCollectionWithoutAbstract: CollectionSearchResult = new CollectionSearchResult(); mockCollectionWithoutAbstract.hitHighlights = {}; -mockCollectionWithoutAbstract.dspaceObject = Object.assign(new Collection(), { +mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(), { metadata: { 'dc.title': [ { @@ -63,7 +63,7 @@ describe('CollectionSearchResultGridElementComponent', () => { describe('When the collection has an abstract', () => { beforeEach(() => { - collectionSearchResultGridElementComponent.dso = mockCollectionWithAbstract.dspaceObject; + collectionSearchResultGridElementComponent.dso = mockCollectionWithAbstract.indexableObject; fixture.detectChanges(); }); @@ -75,7 +75,7 @@ describe('CollectionSearchResultGridElementComponent', () => { describe('When the collection has no abstract', () => { beforeEach(() => { - collectionSearchResultGridElementComponent.dso = mockCollectionWithoutAbstract.dspaceObject; + collectionSearchResultGridElementComponent.dso = mockCollectionWithoutAbstract.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts index e111e624c5..567b2e1d0e 100644 --- a/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component.spec.ts @@ -17,7 +17,7 @@ const truncatableServiceStub: any = { const mockCommunityWithAbstract: CommunitySearchResult = new CommunitySearchResult(); mockCommunityWithAbstract.hitHighlights = {}; -mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), { +mockCommunityWithAbstract.indexableObject = Object.assign(new Community(), { metadata: { 'dc.description.abstract': [ { @@ -30,7 +30,7 @@ mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), { const mockCommunityWithoutAbstract: CommunitySearchResult = new CommunitySearchResult(); mockCommunityWithoutAbstract.hitHighlights = {}; -mockCommunityWithoutAbstract.dspaceObject = Object.assign(new Community(), { +mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), { metadata: { 'dc.title': [ { @@ -63,7 +63,7 @@ describe('CommunitySearchResultGridElementComponent', () => { describe('When the community has an abstract', () => { beforeEach(() => { - communitySearchResultGridElementComponent.dso = mockCommunityWithAbstract.dspaceObject; + communitySearchResultGridElementComponent.dso = mockCommunityWithAbstract.indexableObject; fixture.detectChanges(); }); @@ -75,7 +75,7 @@ describe('CommunitySearchResultGridElementComponent', () => { describe('When the community has no abstract', () => { beforeEach(() => { - communitySearchResultGridElementComponent.dso = mockCommunityWithoutAbstract.dspaceObject; + communitySearchResultGridElementComponent.dso = mockCommunityWithoutAbstract.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts index 0103fa5c49..655fd268a7 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component.spec.ts @@ -18,7 +18,7 @@ const truncatableServiceStub: any = { const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult(); mockItemWithAuthorAndDate.hitHighlights = {}; -mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { +mockItemWithAuthorAndDate.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.contributor.author': [ @@ -38,7 +38,7 @@ mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult(); mockItemWithoutAuthorAndDate.hitHighlights = {}; -mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), { +mockItemWithoutAuthorAndDate.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.title': [ @@ -78,7 +78,7 @@ describe('ItemSearchResultGridElementComponent', () => { describe('When the item has an author', () => { beforeEach(() => { - itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; + itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -90,7 +90,7 @@ describe('ItemSearchResultGridElementComponent', () => { describe('When the item has no author', () => { beforeEach(() => { - itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; + itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -102,7 +102,7 @@ describe('ItemSearchResultGridElementComponent', () => { describe('When the item has an issuedate', () => { beforeEach(() => { - itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; + itemSearchResultGridElementComponent.dso = mockItemWithAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -114,7 +114,7 @@ describe('ItemSearchResultGridElementComponent', () => { describe('When the item has no issuedate', () => { beforeEach(() => { - itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; + itemSearchResultGridElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts index 844d0bc165..0961dc96ee 100644 --- a/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts +++ b/src/app/shared/object-grid/search-result-grid-element/search-result-grid-element.component.ts @@ -18,7 +18,7 @@ export class SearchResultGridElementComponent, K exten public constructor(@Inject('objectElementProvider') public listableObject: ListableObject, private truncatableService: TruncatableService) { super(listableObject); - this.dso = this.object.dspaceObject; + this.dso = this.object.indexableObject; } /** diff --git a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts index c79812d10b..7c30b2ef8c 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component.spec.ts @@ -52,7 +52,7 @@ const item = Object.assign(new Item(), { const rdItem = new RemoteData(false, false, true, null, item); const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); -mockResultObject.dspaceObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); +mockResultObject.indexableObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem) }); describe('ClaimedMyDSpaceResultListElementComponent', () => { beforeEach(async(() => { @@ -75,7 +75,7 @@ describe('ClaimedMyDSpaceResultListElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html index 0aa93ab711..b2fc3812cd 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component.html @@ -28,5 +28,5 @@
    - + diff --git a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts index bcd323ae2a..1ffb1b5c13 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component.spec.ts @@ -17,7 +17,7 @@ const compIndex = 1; const mockResultObject: ItemMyDSpaceResult = new ItemMyDSpaceResult(); mockResultObject.hitHighlights = {}; -mockResultObject.dspaceObject = Object.assign(new Item(), { +mockResultObject.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.title': [ @@ -68,7 +68,7 @@ describe('ItemMyDSpaceResultListElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts index 4fd40e7318..b2a5bf14fd 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/my-dspace-result-list-element.component.ts @@ -31,7 +31,7 @@ export class MyDSpaceResultListElementComponent, K e public constructor(@Inject('objectElementProvider') public listable: ListableObject, @Inject('indexElementProvider') public index: number) { super(listable); - this.dso = this.object.dspaceObject; + this.dso = this.object.indexableObject; this.dsoIndex = this.index; } diff --git a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts index 3b2545e069..36eb8f253a 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component.spec.ts @@ -52,7 +52,7 @@ const item = Object.assign(new Item(), { const rdItem = new RemoteData(false, false, true, null, item); const workflowitem = Object.assign(new Workflowitem(), { item: observableOf(rdItem) }); const rdWorkflowitem = new RemoteData(false, false, true, null, workflowitem); -mockResultObject.dspaceObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); +mockResultObject.indexableObject = Object.assign(new PoolTask(), { workflowitem: observableOf(rdWorkflowitem) }); describe('PoolMyDSpaceResultListElementComponent', () => { beforeEach(async(() => { @@ -75,7 +75,7 @@ describe('PoolMyDSpaceResultListElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts index de2dfd73f4..2bcd4d46b0 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component.spec.ts @@ -49,7 +49,7 @@ const item = Object.assign(new Item(), { } }); const rd = new RemoteData(false, false, true, null, item); -mockResultObject.dspaceObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); +mockResultObject.indexableObject = Object.assign(new Workflowitem(), { item: observableOf(rd) }); describe('WorkflowitemMyDSpaceResultListElementComponent', () => { beforeEach(async(() => { @@ -72,7 +72,7 @@ describe('WorkflowitemMyDSpaceResultListElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts index f47c05c9e9..56ee33ddae 100644 --- a/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component.spec.ts @@ -49,7 +49,7 @@ const item = Object.assign(new Item(), { } }); const rd = new RemoteData(false, false, true, null, item); -mockResultObject.dspaceObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); +mockResultObject.indexableObject = Object.assign(new Workspaceitem(), { item: observableOf(rd) }); describe('WorkspaceitemMyDSpaceResultListElementComponent', () => { beforeEach(async(() => { @@ -72,7 +72,7 @@ describe('WorkspaceitemMyDSpaceResultListElementComponent', () => { })); beforeEach(() => { - component.dso = mockResultObject.dspaceObject; + component.dso = mockResultObject.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts index e897071a00..7f5aaf5d9c 100644 --- a/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/collection-search-result/collection-search-result-list-element.component.spec.ts @@ -17,7 +17,7 @@ const truncatableServiceStub: any = { const mockCollectionWithAbstract: CollectionSearchResult = new CollectionSearchResult(); mockCollectionWithAbstract.hitHighlights = {}; -mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), { +mockCollectionWithAbstract.indexableObject = Object.assign(new Collection(), { metadata: { 'dc.description.abstract': [ { @@ -30,7 +30,7 @@ mockCollectionWithAbstract.dspaceObject = Object.assign(new Collection(), { const mockCollectionWithoutAbstract: CollectionSearchResult = new CollectionSearchResult(); mockCollectionWithoutAbstract.hitHighlights = {}; -mockCollectionWithoutAbstract.dspaceObject = Object.assign(new Collection(), { +mockCollectionWithoutAbstract.indexableObject = Object.assign(new Collection(), { metadata: { 'dc.title': [ { @@ -63,7 +63,7 @@ describe('CollectionSearchResultListElementComponent', () => { describe('When the collection has an abstract', () => { beforeEach(() => { - collectionSearchResultListElementComponent.dso = mockCollectionWithAbstract.dspaceObject; + collectionSearchResultListElementComponent.dso = mockCollectionWithAbstract.indexableObject; fixture.detectChanges(); }); @@ -75,7 +75,7 @@ describe('CollectionSearchResultListElementComponent', () => { describe('When the collection has no abstract', () => { beforeEach(() => { - collectionSearchResultListElementComponent.dso = mockCollectionWithoutAbstract.dspaceObject; + collectionSearchResultListElementComponent.dso = mockCollectionWithoutAbstract.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts index 75d5966767..691a69dde4 100644 --- a/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/community-search-result/community-search-result-list-element.component.spec.ts @@ -17,7 +17,7 @@ const truncatableServiceStub: any = { const mockCommunityWithAbstract: CommunitySearchResult = new CommunitySearchResult(); mockCommunityWithAbstract.hitHighlights = {}; -mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), { +mockCommunityWithAbstract.indexableObject = Object.assign(new Community(), { metadata: { 'dc.description.abstract': [ { @@ -30,7 +30,7 @@ mockCommunityWithAbstract.dspaceObject = Object.assign(new Community(), { const mockCommunityWithoutAbstract: CommunitySearchResult = new CommunitySearchResult(); mockCommunityWithoutAbstract.hitHighlights = {}; -mockCommunityWithoutAbstract.dspaceObject = Object.assign(new Community(), { +mockCommunityWithoutAbstract.indexableObject = Object.assign(new Community(), { metadata: { 'dc.title': [ { @@ -63,7 +63,7 @@ describe('CommunitySearchResultListElementComponent', () => { describe('When the community has an abstract', () => { beforeEach(() => { - communitySearchResultListElementComponent.dso = mockCommunityWithAbstract.dspaceObject; + communitySearchResultListElementComponent.dso = mockCommunityWithAbstract.indexableObject; fixture.detectChanges(); }); @@ -75,7 +75,7 @@ describe('CommunitySearchResultListElementComponent', () => { describe('When the community has no abstract', () => { beforeEach(() => { - communitySearchResultListElementComponent.dso = mockCommunityWithoutAbstract.dspaceObject; + communitySearchResultListElementComponent.dso = mockCommunityWithoutAbstract.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts index 8567fc1782..8bbb621312 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-search-result-list-element.component.spec.ts @@ -18,7 +18,7 @@ const truncatableServiceStub: any = { const mockItemWithAuthorAndDate: ItemSearchResult = new ItemSearchResult(); mockItemWithAuthorAndDate.hitHighlights = {}; -mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { +mockItemWithAuthorAndDate.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.contributor.author': [ @@ -38,7 +38,7 @@ mockItemWithAuthorAndDate.dspaceObject = Object.assign(new Item(), { const mockItemWithoutAuthorAndDate: ItemSearchResult = new ItemSearchResult(); mockItemWithoutAuthorAndDate.hitHighlights = {}; -mockItemWithoutAuthorAndDate.dspaceObject = Object.assign(new Item(), { +mockItemWithoutAuthorAndDate.indexableObject = Object.assign(new Item(), { bitstreams: observableOf({}), metadata: { 'dc.title': [ @@ -78,7 +78,7 @@ describe('ItemSearchResultListElementComponent', () => { describe('When the item has an author', () => { beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; + itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -90,7 +90,7 @@ describe('ItemSearchResultListElementComponent', () => { describe('When the item has no author', () => { beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; + itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -102,7 +102,7 @@ describe('ItemSearchResultListElementComponent', () => { describe('When the item has an issuedate', () => { beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.dspaceObject; + itemSearchResultListElementComponent.dso = mockItemWithAuthorAndDate.indexableObject; fixture.detectChanges(); }); @@ -114,7 +114,7 @@ describe('ItemSearchResultListElementComponent', () => { describe('When the item has no issuedate', () => { beforeEach(() => { - itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.dspaceObject; + itemSearchResultListElementComponent.dso = mockItemWithoutAuthorAndDate.indexableObject; fixture.detectChanges(); }); diff --git a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts index 525d39e798..4194f2ae3e 100644 --- a/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/search-result-list-element.component.ts @@ -18,7 +18,7 @@ export class SearchResultListElementComponent, K exten public constructor(@Inject('objectElementProvider') public listable: ListableObject, private truncatableService: TruncatableService) { super(listable); - this.dso = this.object.dspaceObject; + this.dso = this.object.indexableObject; } /** From ceb0e7eeb48b70b747d0cfbfd56027d86df0644a Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 15:33:59 +0200 Subject: [PATCH 123/205] Fixed unexpected error exception during test --- src/app/core/shared/item.model.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 6cd5634fd0..c527fe0357 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -5,7 +5,7 @@ import { DSpaceObject } from './dspace-object.model'; import { Collection } from './collection.model'; import { RemoteData } from '../data/remote-data'; import { Bitstream } from './bitstream.model'; -import { hasValue, isNotEmpty } from '../../shared/empty.util'; +import { hasValue, isNotEmpty, isNotUndefined } from '../../shared/empty.util'; import { PaginatedList } from '../data/paginated-list'; export class Item extends DSpaceObject { @@ -90,7 +90,7 @@ export class Item extends DSpaceObject { */ getBitstreamsByBundleName(bundleName: string): Observable { return this.bitstreams.pipe( - filter((rd: RemoteData>) => !rd.isResponsePending), + filter((rd: RemoteData>) => !rd.isResponsePending && isNotUndefined(rd.payload)), map((rd: RemoteData>) => rd.payload.page), filter((bitstreams: Bitstream[]) => hasValue(bitstreams)), take(1), From 589b0ea63fcfd5c4bb898e1fe8c7901aeadcdc80 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 17:17:15 +0200 Subject: [PATCH 124/205] renamed dspaceObject to indexableObject --- src/app/core/data/search-response-parsing.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/core/data/search-response-parsing.service.ts b/src/app/core/data/search-response-parsing.service.ts index 790df0624b..9ab0104393 100644 --- a/src/app/core/data/search-response-parsing.service.ts +++ b/src/app/core/data/search-response-parsing.service.ts @@ -37,7 +37,7 @@ export class SearchResponseParsingService implements ResponseParsingService { const dsoSelfLinks = payload._embedded.objects .filter((object) => hasValue(object._embedded)) - .map((object) => object._embedded.resultObject) + .map((object) => object._embedded.indexableObject) // we don't need embedded collections, bitstreamformats, etc for search results. // And parsing them all takes up a lot of time. Throw them away to improve performance // until objs until partial results are supported by the rest api @@ -53,7 +53,7 @@ export class SearchResponseParsingService implements ResponseParsingService { const objects = payload._embedded.objects .filter((object) => hasValue(object._embedded)) .map((object, index) => Object.assign({}, object, { - resultObject: dsoSelfLinks[index], + indexableObject: dsoSelfLinks[index], hitHighlights: hitHighlights[index], // we don't need embedded collections, bitstreamformats, etc for search results. // And parsing them all takes up a lot of time. Throw them away to improve performance From 42fa67361af29dc7996ba3bc0e4efc84318f166f Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 18:13:55 +0200 Subject: [PATCH 125/205] Use search link to retrieve filter values --- .../search-facet-option.component.ts | 19 +++++++++++++++++-- .../search-facet-range-option.component.html | 4 ++-- ...earch-facet-selected-option.component.html | 4 ++-- .../search-facet-selected-option.component.ts | 11 +++++++++++ .../search-labels.component.html | 6 +++--- .../search-labels/search-labels.component.ts | 11 +++++++++++ src/app/+search-page/search-options.model.ts | 5 ++++- 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts index c4b339f464..fc08788c4b 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts @@ -70,7 +70,7 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { * Checks if a value for this filter is currently active */ private isChecked(): Observable { - return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.filterValue.value); + return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.getFacetValueFromSearchLink()); } /** @@ -86,11 +86,26 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { */ private updateAddParams(selectedValues: string[]): void { this.addQueryParams = { - [this.filterConfig.paramName]: [...selectedValues, this.filterValue.value], + [this.filterConfig.paramName]: [...selectedValues, this.getFacetValueFromSearchLink()], page: 1 }; } + /** + * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved + * Retrieve facet value from search link + */ + private getFacetValueFromSearchLink(): string { + const search = this.filterValue.search; + const hashes = search.slice(search.indexOf('?') + 1).split('&'); + const params = {}; + hashes.map((hash) => { + const [key, val] = hash.split('='); + params[key] = decodeURIComponent(val) + }); + + return params[this.filterConfig.paramName]; + } /** * Make sure the subscription is unsubscribed from when this component is destroyed */ diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html index b485fe0fd0..8e8ad9b4e3 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component.html @@ -1,8 +1,8 @@ - {{filterValue.value}} + {{filterValue.label}} {{filterValue.count}} - \ No newline at end of file + diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html index ba43bae100..5abd0810c7 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html @@ -2,5 +2,5 @@ [routerLink]="[getSearchLink()]" [queryParams]="removeQueryParams" queryParamsHandling="merge"> - {{selectedValue}} - \ No newline at end of file + {{normalizeFilterValue(selectedValue)}} + diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts index 5137bf8ffc..8b56c61c83 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts @@ -76,6 +76,17 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy { }; } + /** + * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved + * Strips operator from filter value + * e.g. 'test ,operator' => 'test' + * + * @param value + */ + normalizeFilterValue(value: string) { + return value.replace(/,[^,]*$/g, ''); + } + /** * Make sure the subscription is unsubscribed from when this component is destroyed */ diff --git a/src/app/+search-page/search-labels/search-labels.component.html b/src/app/+search-page/search-labels/search-labels.component.html index 61a5618dad..cac81e8717 100644 --- a/src/app/+search-page/search-labels/search-labels.component.html +++ b/src/app/+search-page/search-labels/search-labels.component.html @@ -2,11 +2,11 @@ diff --git a/src/app/+search-page/search-labels/search-labels.component.ts b/src/app/+search-page/search-labels/search-labels.component.ts index fd82de326c..c7ad9790ad 100644 --- a/src/app/+search-page/search-labels/search-labels.component.ts +++ b/src/app/+search-page/search-labels/search-labels.component.ts @@ -56,4 +56,15 @@ export class SearchLabelsComponent { getSearchLink() { return this.searchService.getSearchLink(); } + + /** + * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved + * Strips operator from filter value + * e.g. 'test ,operator' => 'test' + * + * @param value + */ + normalizeFilterValue(value: string) { + return value.replace(/,[^,]*$/g, ''); + } } diff --git a/src/app/+search-page/search-options.model.ts b/src/app/+search-page/search-options.model.ts index e56cec1724..59a6f0f93d 100644 --- a/src/app/+search-page/search-options.model.ts +++ b/src/app/+search-page/search-options.model.ts @@ -44,7 +44,10 @@ export class SearchOptions { } if (isNotEmpty(this.filters)) { this.filters.forEach((filter: SearchFilter) => { - filter.values.forEach((value) => args.push(`${filter.key}=${value},${filter.operator}`)); + filter.values.forEach((value) => { + const filterValue = value.includes(',') ? `${value}` : `${value},${filter.operator}`; + args.push(`${filter.key}=${filterValue}`) + }); }); } if (isNotEmpty(args)) { From 030dd20631bc023073637a0d96b322cd821f47e8 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 18:16:58 +0200 Subject: [PATCH 126/205] Fixed visualization of pagination info div --- src/app/shared/pagination/pagination.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html index 7336866e5c..cfdb95c8b9 100644 --- a/src/app/shared/pagination/pagination.component.html +++ b/src/app/shared/pagination/pagination.component.html @@ -1,7 +1,7 @@
    -
    +
    {{ 'pagination.showing.label' | translate }} {{ 'pagination.showing.detail' | translate:getShowingDetails(collectionSize)}}
    From f9b96dc6d57729c765afacb80985c1bf4b6b13d6 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 18:41:26 +0200 Subject: [PATCH 127/205] fixed test --- .../search-facet-option.component.spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index 279ce0f97a..f44dc0f69f 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -19,9 +19,10 @@ import { By } from '@angular/platform-browser'; describe('SearchFacetOptionComponent', () => { let comp: SearchFacetOptionComponent; let fixture: ComponentFixture; - const filterName1 = 'test name'; + const filterName1 = 'testname'; const value1 = 'testvalue1'; const value2 = 'test2'; + const operator = 'equals'; const value3 = 'another value3'; const mockFilterConfig = Object.assign(new SearchFilterConfig(), { name: filterName1, @@ -36,7 +37,7 @@ describe('SearchFacetOptionComponent', () => { label: value2, value: value2, count: 20, - search: '' + search: `http://test.org/api/discover/search/objects?f.${filterName1}=${value2},${operator}` }; const searchLink = '/search'; @@ -91,12 +92,12 @@ describe('SearchFacetOptionComponent', () => { fixture.detectChanges(); }); - describe('when the updateAddParams method is called wih a value', () => { + describe('when the updateAddParams method is called with a value', () => { it('should update the addQueryParams with the new parameter values', () => { comp.addQueryParams = {}; (comp as any).updateAddParams(selectedValues); expect(comp.addQueryParams).toEqual({ - [mockFilterConfig.paramName]: [value1, value.value], + [mockFilterConfig.paramName]: [value1, `${value2},${operator}`], page: 1 }); }); From bbc27febdd27208feff0b607451ef455ecf03b46 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Wed, 3 Apr 2019 18:42:36 +0200 Subject: [PATCH 128/205] refactored ItemDetailPreviewComponent --- src/app/+item-page/item-page.module.ts | 18 ++++++++ .../item-detail-preview.component.html | 41 +++++++++++++++---- .../item-detail-preview.component.ts | 11 +++++ src/app/shared/shared.module.ts | 18 -------- 4 files changed, 63 insertions(+), 25 deletions(-) diff --git a/src/app/+item-page/item-page.module.ts b/src/app/+item-page/item-page.module.ts index f56f45d753..06b4ecf1e4 100644 --- a/src/app/+item-page/item-page.module.ts +++ b/src/app/+item-page/item-page.module.ts @@ -5,6 +5,15 @@ import { SharedModule } from '../shared/shared.module'; import { ItemPageComponent } from './simple/item-page.component'; import { ItemPageRoutingModule } from './item-page-routing.module'; +import { MetadataUriValuesComponent } from './field-components/metadata-uri-values/metadata-uri-values.component'; +import { ItemPageAuthorFieldComponent } from './simple/field-components/specific-field/author/item-page-author-field.component'; +import { ItemPageDateFieldComponent } from './simple/field-components/specific-field/date/item-page-date-field.component'; +import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component'; +import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component'; +import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component'; +import { ItemPageSpecificFieldComponent } from './simple/field-components/specific-field/item-page-specific-field.component'; +import { FileSectionComponent } from './simple/field-components/file-section/file-section.component'; +import { CollectionsComponent } from './field-components/collections/collections.component'; import { FullItemPageComponent } from './full/full-item-page.component'; import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component'; import { EditItemPageModule } from './edit-item-page/edit-item-page.module'; @@ -19,6 +28,15 @@ import { EditItemPageModule } from './edit-item-page/edit-item-page.module'; declarations: [ ItemPageComponent, FullItemPageComponent, + MetadataUriValuesComponent, + ItemPageAuthorFieldComponent, + ItemPageDateFieldComponent, + ItemPageAbstractFieldComponent, + ItemPageUriFieldComponent, + ItemPageTitleFieldComponent, + ItemPageSpecificFieldComponent, + FileSectionComponent, + CollectionsComponent, FullFileSectionComponent ] }) diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html index 10e7e98d87..e7d960599e 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.html @@ -3,20 +3,47 @@
    - +

    + + {{('mydspace.results.no-title' | translate)}} +

    - - - + + + + {{mdValue.value}} + + + + {{('mydspace.results.no-date' | translate)}} + + + + + + {{mdValue.value}} + + + + {{('mydspace.results.no-authors' | translate)}} + +
    - - - + + + + {{mdValue.value}} + + + + {{('mydspace.results.no-abstract' | translate)}} + +
    diff --git a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts index 9f92f79859..d4244f2760 100644 --- a/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts +++ b/src/app/shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview.component.ts @@ -6,6 +6,7 @@ import { Item } from '../../../../core/shared/item.model'; import { MyDspaceItemStatusType } from '../../../object-collection/shared/mydspace-item-status/my-dspace-item-status-type'; import { fadeInOut } from '../../../animations/fade'; import { Bitstream } from '../../../../core/shared/bitstream.model'; +import { Metadata } from '../../../../core/shared/metadata.utils'; /** * This component show metadata for the given item object in the detail view. @@ -43,6 +44,16 @@ export class ItemDetailPreviewComponent { */ public thumbnail$: Observable; + /** + * Gets all matching metadata string values from hitHighlights or dso metadata, preferring hitHighlights. + * + * @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]]. + * @returns {string[]} the matching string values or an empty array. + */ + allMetadataValues(keyOrKeys: string | string[]): string[] { + return Metadata.allValues([this.object.hitHighlights, this.item.metadata], keyOrKeys); + } + /** * Initialize all instance variables */ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 00f357cda6..8ef7e7723e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -127,17 +127,8 @@ import { EditItemSelectorComponent } from './dso-selector/modal-wrappers/edit-it import { EditCommunitySelectorComponent } from './dso-selector/modal-wrappers/edit-community-selector/edit-community-selector.component'; import { EditCollectionSelectorComponent } from './dso-selector/modal-wrappers/edit-collection-selector/edit-collection-selector.component'; import { ItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; -import { ItemPageAuthorFieldComponent } from '../+item-page/simple/field-components/specific-field/author/item-page-author-field.component'; -import { ItemPageDateFieldComponent } from '../+item-page/simple/field-components/specific-field/date/item-page-date-field.component'; -import { ItemPageAbstractFieldComponent } from '../+item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component'; -import { ItemPageUriFieldComponent } from '../+item-page/simple/field-components/specific-field/uri/item-page-uri-field.component'; -import { ItemPageTitleFieldComponent } from '../+item-page/simple/field-components/specific-field/title/item-page-title-field.component'; -import { ItemPageSpecificFieldComponent } from '../+item-page/simple/field-components/specific-field/item-page-specific-field.component'; -import { FileSectionComponent } from '../+item-page/simple/field-components/file-section/file-section.component'; import { MetadataFieldWrapperComponent } from '../+item-page/field-components/metadata-field-wrapper/metadata-field-wrapper.component'; -import { CollectionsComponent } from '../+item-page/field-components/collections/collections.component'; import { MetadataValuesComponent } from '../+item-page/field-components/metadata-values/metadata-values.component'; -import { MetadataUriValuesComponent } from '../+item-page/field-components/metadata-uri-values/metadata-uri-values.component'; import { RoleDirective } from './roles/role.directive'; import { UserMenuComponent } from './auth-nav-menu/user-menu/user-menu.component'; import { ClaimedTaskActionsReturnToPoolComponent } from './mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component'; @@ -296,17 +287,8 @@ const ENTRY_COMPONENTS = [ ]; const SHARED_ITEM_PAGE_COMPONENTS = [ - CollectionsComponent, - FileSectionComponent, - ItemPageAuthorFieldComponent, - ItemPageDateFieldComponent, - ItemPageAbstractFieldComponent, - ItemPageUriFieldComponent, - ItemPageTitleFieldComponent, - ItemPageSpecificFieldComponent, MetadataFieldWrapperComponent, MetadataValuesComponent, - MetadataUriValuesComponent ]; const PROVIDERS = [ From 71c0c1b52c5999f4b26f39cfc009cc745f6be5ec Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Thu, 4 Apr 2019 12:43:17 +0200 Subject: [PATCH 129/205] Fixed an issue with place property removal from object that represent metadata section field on rest side --- .../submission-response-parsing.service.ts | 14 +++++++------- .../models/tag/dynamic-tag.component.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/core/submission/submission-response-parsing.service.ts b/src/app/core/submission/submission-response-parsing.service.ts index 20dfb43cbd..f5481ec274 100644 --- a/src/app/core/submission/submission-response-parsing.service.ts +++ b/src/app/core/submission/submission-response-parsing.service.ts @@ -27,16 +27,16 @@ export function isServerFormValue(obj: any): boolean { && obj.hasOwnProperty('value') && obj.hasOwnProperty('language') && obj.hasOwnProperty('authority') - && obj.hasOwnProperty('confidence') - && obj.hasOwnProperty('place')) + && obj.hasOwnProperty('confidence')) } /** * Export a function to normalize sections object of the server response * * @param obj + * @param objIndex */ -export function normalizeSectionData(obj: any) { +export function normalizeSectionData(obj: any, objIndex?: number) { let result: any = obj; if (isNotNull(obj)) { // If is an Instance of FormFieldMetadataValueObject normalize it @@ -49,14 +49,14 @@ export function normalizeSectionData(obj: any) { obj.language, obj.authority, (obj.display || obj.value), - obj.place, + obj.place || objIndex, obj.confidence, obj.otherInformation ); } else if (Array.isArray(obj)) { result = []; obj.forEach((item, index) => { - result[index] = normalizeSectionData(item); + result[index] = normalizeSectionData(item, index); }); } else if (typeof obj === 'object') { result = Object.create({}); @@ -141,9 +141,9 @@ export class SubmissionResponseParsingService extends BaseResponseParsingService // If entry is not an array, for sure is not a section of type form if (Array.isArray(entry)) { normalizedSectionData[metdadataId] = []; - entry.forEach((valueItem) => { + entry.forEach((valueItem, index) => { // Parse value and normalize it - const normValue = normalizeSectionData(valueItem); + const normValue = normalizeSectionData(valueItem, index); if (isNotEmpty(normValue)) { normalizedSectionData[metdadataId].push(normValue); } diff --git a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts index d61134347a..a44a20d4bd 100644 --- a/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts +++ b/src/app/shared/form/builder/ds-dynamic-form-ui/models/tag/dynamic-tag.component.ts @@ -168,7 +168,7 @@ export class DsDynamicTagComponent extends DynamicFormControlComponent implement } private addTagsToChips() { - if (!this.hasAuthority || !this.model.authorityOptions.closed) { + if (hasValue(this.currentValue) && (!this.hasAuthority || !this.model.authorityOptions.closed)) { let res: string[] = []; res = this.currentValue.split(','); From 1ceae322de6c6254203494ccf7a2a75b4f0392ed Mon Sep 17 00:00:00 2001 From: lotte Date: Fri, 5 Apr 2019 09:13:42 +0200 Subject: [PATCH 130/205] 61561: fixed ui issue for DS-4006 --- src/app/core/auth/auth-object-factory.ts | 5 +++++ src/app/core/auth/auth-type.ts | 3 ++- .../data/base-response-parsing.service.ts | 20 ------------------- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/src/app/core/auth/auth-object-factory.ts b/src/app/core/auth/auth-object-factory.ts index e37475d94c..02458f4e3e 100644 --- a/src/app/core/auth/auth-object-factory.ts +++ b/src/app/core/auth/auth-object-factory.ts @@ -4,6 +4,7 @@ import { NormalizedAuthStatus } from './models/normalized-auth-status.model'; import { NormalizedEPerson } from '../eperson/models/normalized-eperson.model'; import { NormalizedObject } from '../cache/models/normalized-object.model'; import { CacheableObject } from '../cache/object-cache.reducer'; +import { NormalizedGroup } from '../eperson/models/normalized-group.model'; export class AuthObjectFactory { public static getConstructor(type): GenericConstructor> { @@ -12,6 +13,10 @@ export class AuthObjectFactory { return NormalizedEPerson } + case AuthType.Group: { + return NormalizedGroup + } + case AuthType.Status: { return NormalizedAuthStatus } diff --git a/src/app/core/auth/auth-type.ts b/src/app/core/auth/auth-type.ts index 9a248da91f..f0460449ea 100644 --- a/src/app/core/auth/auth-type.ts +++ b/src/app/core/auth/auth-type.ts @@ -1,4 +1,5 @@ export enum AuthType { EPerson = 'eperson', - Status = 'status' + Status = 'status', + Group = 'group' } diff --git a/src/app/core/data/base-response-parsing.service.ts b/src/app/core/data/base-response-parsing.service.ts index 6102f930b0..334aadd379 100644 --- a/src/app/core/data/base-response-parsing.service.ts +++ b/src/app/core/data/base-response-parsing.service.ts @@ -26,7 +26,6 @@ export abstract class BaseResponseParsingService { } else if (Array.isArray(data)) { return this.processArray(data, requestUUID); } else if (isRestDataObject(data)) { - data = this.fixBadEPersonRestResponse(data); const object = this.deserialize(data); if (isNotEmpty(data._embedded)) { Object @@ -141,23 +140,4 @@ export abstract class BaseResponseParsingService { protected retrieveObjectOrUrl(obj: any): any { return this.toCache ? obj.self : obj; } - - // TODO Remove when https://jira.duraspace.org/browse/DS-4006 is fixed - // See https://github.com/DSpace/dspace-angular/issues/292 - private fixBadEPersonRestResponse(obj: any): any { - if (obj.type === ResourceType.EPerson) { - const groups = obj.groups; - const normGroups = []; - if (isNotEmpty(groups)) { - groups.forEach((group) => { - const parts = ['eperson', 'groups', group.uuid]; - const href = new RESTURLCombiner(this.EnvConfig, ...parts).toString(); - normGroups.push(href); - } - ) - } - return Object.assign({}, obj, { groups: normGroups }); - } - return obj; - } } From 174c4f63980aad705f4a1ee06a0bff271ce9da69 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 5 Apr 2019 12:34:35 +0200 Subject: [PATCH 131/205] Implemented authority facet Retrieve selected facet values as FacetValue objects fixed tests --- .../search-authority-filter.component.html | 27 +++++++++++++ .../search-authority-filter.component.scss | 23 +++++++++++ .../search-authority-filter.component.ts | 37 ++++++++++++++++++ .../search-facet-option.component.spec.ts | 39 +++++++++++++++++-- .../search-facet-option.component.ts | 28 +++++++------ ...earch-facet-selected-option.component.html | 2 +- ...ch-facet-selected-option.component.spec.ts | 8 +++- .../search-facet-selected-option.component.ts | 16 ++------ .../search-facet-filter.component.ts | 36 ++++++++++++----- .../search-labels.component.spec.ts | 18 ++++++++- .../search-labels/search-labels.component.ts | 8 ++-- src/app/+search-page/search-page.module.ts | 7 +++- .../search-service/filter-type.model.ts | 5 +++ 13 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html create mode 100644 src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss create mode 100644 src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html new file mode 100644 index 0000000000..6958c422e1 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html @@ -0,0 +1,27 @@ + diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss new file mode 100644 index 0000000000..33e354f2d8 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.scss @@ -0,0 +1,23 @@ +@import '../../../../../styles/variables.scss'; +@import '../../../../../styles/mixins.scss'; + +.filters { + a { + color: $body-color; + &:hover, &focus { + text-decoration: none; + } + span.badge { + vertical-align: text-top; + } + } + .toggle-more-filters a { + color: $link-color; + text-decoration: underline; + cursor: pointer; + } +} +::ng-deep em { + font-weight: bold; + font-style: normal; +} diff --git a/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts new file mode 100644 index 0000000000..eaa73aba26 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-authority-filter/search-authority-filter.component.ts @@ -0,0 +1,37 @@ +import { Component, OnInit } from '@angular/core'; + +import { FilterType } from '../../../search-service/filter-type.model'; +import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; +import { renderFacetFor } from '../search-filter-type-decorator'; +import { FacetValue } from '../../../search-service/facet-value.model'; + +@Component({ + selector: 'ds-search-authority-filter', + styleUrls: ['./search-authority-filter.component.scss'], + templateUrl: './search-authority-filter.component.html', + animations: [facetLoad] +}) + +/** + * Component that represents an authority facet for a specific filter configuration + */ +@renderFacetFor(FilterType.authority) +export class SearchAuthorityFilterComponent extends SearchFacetFilterComponent implements OnInit { + + /** + * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved + * Retrieve facet value from search link + */ + protected getFacetValue(facet: FacetValue): string { + const search = facet.search; + const hashes = search.slice(search.indexOf('?') + 1).split('&'); + const params = {}; + hashes.map((hash) => { + const [key, val] = hash.split('='); + params[key] = decodeURIComponent(val) + }); + + return params[this.filterConfig.paramName]; + } + +} diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index f44dc0f69f..dda72ed970 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -20,10 +20,12 @@ describe('SearchFacetOptionComponent', () => { let comp: SearchFacetOptionComponent; let fixture: ComponentFixture; const filterName1 = 'testname'; + const filterName2 = 'testAuthorityname'; const value1 = 'testvalue1'; const value2 = 'test2'; - const operator = 'equals'; + const operator = 'authority'; const value3 = 'another value3'; + const mockFilterConfig = Object.assign(new SearchFilterConfig(), { name: filterName1, type: FilterType.range, @@ -33,11 +35,27 @@ describe('SearchFacetOptionComponent', () => { minValue: 200, maxValue: 3000, }); + + const mockAuthorityFilterConfig = Object.assign(new SearchFilterConfig(), { + name: filterName2, + type: FilterType.authority, + hasFacets: false, + isOpenByDefault: false, + pageSize: 2 + }); + const value: FacetValue = { label: value2, value: value2, count: 20, - search: `http://test.org/api/discover/search/objects?f.${filterName1}=${value2},${operator}` + search: `` + }; + + const authorityValue: FacetValue = { + label: value2, + value: value2, + count: 20, + search: `http://test.org/api/discover/search/objects?f.${filterName2}=${value2},${operator}` }; const searchLink = '/search'; @@ -97,7 +115,22 @@ describe('SearchFacetOptionComponent', () => { comp.addQueryParams = {}; (comp as any).updateAddParams(selectedValues); expect(comp.addQueryParams).toEqual({ - [mockFilterConfig.paramName]: [value1, `${value2},${operator}`], + [mockFilterConfig.paramName]: [value1, value.value], + page: 1 + }); + }); + }); + + describe('when filter type is authority and the updateAddParams method is called with a value', () => { + it('should update the addQueryParams with the new parameter values', () => { + comp.filterValue = authorityValue; + comp.filterConfig = mockAuthorityFilterConfig; + fixture.detectChanges(); + + comp.addQueryParams = {}; + (comp as any).updateAddParams(selectedValues); + expect(comp.addQueryParams).toEqual({ + [mockAuthorityFilterConfig.paramName]: [value1, `${value2},${operator}`], page: 1 }); }); diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts index fc08788c4b..3b0e346cd6 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts @@ -8,6 +8,7 @@ import { SearchService } from '../../../../search-service/search.service'; import { SearchFilterService } from '../../search-filter.service'; import { SearchConfigurationService } from '../../../../search-service/search-configuration.service'; import { hasValue } from '../../../../../shared/empty.util'; +import { FilterType } from '../../../../search-service/filter-type.model'; @Component({ selector: 'ds-search-facet-option', @@ -70,7 +71,7 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { * Checks if a value for this filter is currently active */ private isChecked(): Observable { - return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.getFacetValueFromSearchLink()); + return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.getFacetValue()); } /** @@ -86,7 +87,7 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { */ private updateAddParams(selectedValues: string[]): void { this.addQueryParams = { - [this.filterConfig.paramName]: [...selectedValues, this.getFacetValueFromSearchLink()], + [this.filterConfig.paramName]: [...selectedValues, this.getFacetValue()], page: 1 }; } @@ -95,17 +96,22 @@ export class SearchFacetOptionComponent implements OnInit, OnDestroy { * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved * Retrieve facet value from search link */ - private getFacetValueFromSearchLink(): string { - const search = this.filterValue.search; - const hashes = search.slice(search.indexOf('?') + 1).split('&'); - const params = {}; - hashes.map((hash) => { - const [key, val] = hash.split('='); - params[key] = decodeURIComponent(val) - }); + private getFacetValue(): string { + if (this.filterConfig.type === FilterType.authority) { + const search = this.filterValue.search; + const hashes = search.slice(search.indexOf('?') + 1).split('&'); + const params = {}; + hashes.map((hash) => { + const [key, val] = hash.split('='); + params[key] = decodeURIComponent(val) + }); - return params[this.filterConfig.paramName]; + return params[this.filterConfig.paramName]; + } else { + return this.filterValue.value; + } } + /** * Make sure the subscription is unsubscribed from when this component is destroyed */ diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html index 5abd0810c7..5657bd224e 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html @@ -2,5 +2,5 @@ [routerLink]="[getSearchLink()]" [queryParams]="removeQueryParams" queryParamsHandling="merge"> - {{normalizeFilterValue(selectedValue)}} + {{selectedValue.label}} diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts index 545ba1d66b..f4b7ea1466 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts @@ -32,6 +32,12 @@ describe('SearchFacetSelectedOptionComponent', () => { const searchLink = '/search'; const selectedValues = [value1, value2]; + const facetValue = { + label: value2, + value: value2, + count: 1, + search: '' + }; const selectedValues$ = observableOf(selectedValues); let filterService; let searchService; @@ -76,7 +82,7 @@ describe('SearchFacetSelectedOptionComponent', () => { filterService = (comp as any).filterService; searchService = (comp as any).searchService; router = (comp as any).router; - comp.selectedValue = value2; + comp.selectedValue = facetValue; comp.selectedValues$ = selectedValues$; comp.filterConfig = mockFilterConfig; fixture.detectChanges(); diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts index 8b56c61c83..d713696550 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts @@ -6,6 +6,7 @@ import { SearchService } from '../../../../search-service/search.service'; import { SearchFilterService } from '../../search-filter.service'; import { hasValue } from '../../../../../shared/empty.util'; import { SearchConfigurationService } from '../../../../search-service/search-configuration.service'; +import { FacetValue } from '../../../../search-service/facet-value.model'; @Component({ selector: 'ds-search-facet-selected-option', @@ -19,7 +20,7 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy { /** * The value for this component */ - @Input() selectedValue: string; + @Input() selectedValue: FacetValue; /** * The filter configuration for this facet option @@ -71,22 +72,11 @@ export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy { */ private updateRemoveParams(selectedValues: string[]): void { this.removeQueryParams = { - [this.filterConfig.paramName]: selectedValues.filter((v) => v !== this.selectedValue), + [this.filterConfig.paramName]: selectedValues.filter((v) => v !== this.selectedValue.label), page: 1 }; } - /** - * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved - * Strips operator from filter value - * e.g. 'test ,operator' => 'test' - * - * @param value - */ - normalizeFilterValue(value: string) { - return value.replace(/,[^,]*$/g, ''); - } - /** * Make sure the subscription is unsubscribed from when this component is destroyed */ diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 8bdf36bf9d..5959a2331e 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -6,7 +6,7 @@ import { Subject, Subscription } from 'rxjs'; -import { switchMap, distinctUntilChanged, map, take } from 'rxjs/operators'; +import { switchMap, distinctUntilChanged, map, take, flatMap } from 'rxjs/operators'; import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Router } from '@angular/router'; @@ -57,7 +57,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { /** * List of subscriptions to unsubscribe from */ - private subs: Subscription[] = []; + protected subs: Subscription[] = []; /** * Emits the result values for this filter found by the current filter query @@ -67,8 +67,8 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { /** * Emits the active values for this filter */ - selectedValues$: Observable; - private collapseNextUpdate = true; + selectedValues$: Observable; + protected collapseNextUpdate = true; /** * State of the requested facets used to time the animation @@ -95,10 +95,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { this.filterValues$ = new BehaviorSubject(new RemoteData(true, false, undefined, undefined, undefined)); this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged()); - this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig); this.searchOptions$ = this.searchConfigService.searchOptions; this.subs.push(this.searchOptions$.subscribe(() => this.updateFilterValueList())); - const facetValues = observableCombineLatest(this.searchOptions$, this.currentPage).pipe( + const facetValues$ = observableCombineLatest(this.searchOptions$, this.currentPage).pipe( map(([options, page]) => { return { options, page } }), @@ -116,8 +115,17 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { ) }) ); + + this.selectedValues$ = observableCombineLatest( + this.filterService.getSelectedValuesForFilter(this.filterConfig), + facetValues$.pipe(flatMap((facetValues) => facetValues.values))).pipe( + map(([selectedValues, facetValues]) => { + return facetValues.payload.page.filter((facetValue) => selectedValues.includes(this.getFacetValue(facetValue))) + }) + ); + let filterValues = []; - this.subs.push(facetValues.subscribe((facetOutcome) => { + this.subs.push(facetValues$.subscribe((facetOutcome) => { const newValues$ = facetOutcome.values; if (this.collapseNextUpdate) { @@ -202,7 +210,10 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { if (isNotEmpty(data)) { this.router.navigate([this.getSearchLink()], { queryParams: - { [this.filterConfig.paramName]: [...selectedValues, data] }, + { [this.filterConfig.paramName]: [ + ...selectedValues.map((facet) => this.getFacetValue(facet)), + data + ] }, queryParamsHandling: 'merge' }); this.filter = ''; @@ -253,7 +264,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { return rd.payload.page.map((facet) => { return { displayValue: this.getDisplayValue(facet, data), - value: facet.value + value: this.getFacetValue(facet) } }) } @@ -265,6 +276,13 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { } } + /** + * Retrieve facet value + */ + protected getFacetValue(facet: FacetValue): string { + return facet.value; + } + /** * Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value * @param {FacetValue} facet The value of the facet as returned by the server diff --git a/src/app/+search-page/search-labels/search-labels.component.spec.ts b/src/app/+search-page/search-labels/search-labels.component.spec.ts index aada73673e..d28698764c 100644 --- a/src/app/+search-page/search-labels/search-labels.component.spec.ts +++ b/src/app/+search-page/search-labels/search-labels.component.spec.ts @@ -9,7 +9,6 @@ import { SearchServiceStub } from '../../shared/testing/search-service-stub'; import { Observable, of as observableOf } from 'rxjs'; import { Params } from '@angular/router'; import { ObjectKeysPipe } from '../../shared/utils/object-keys-pipe'; -import { SearchConfigurationService } from '../search-service/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../+my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../shared/testing/search-configuration-service-stub'; @@ -22,8 +21,11 @@ describe('SearchLabelsComponent', () => { const field1 = 'author'; const field2 = 'subject'; - const value1 = 'TestAuthor'; + const value1 = 'Test, Author'; + const normValue1 = 'Test, Author'; const value2 = 'TestSubject'; + const value3 = 'Test, Authority,authority'; + const normValue3 = 'Test, Authority'; const filter1 = [field1, value1]; const filter2 = [field2, value2]; const mockFilters = [ @@ -68,4 +70,16 @@ describe('SearchLabelsComponent', () => { }); }) }); + + describe('when normalizeFilterValue is called', () => { + it('should return properly filter value', () => { + let result: string; + + result = comp.normalizeFilterValue(value1); + expect(result).toBe(normValue1); + + result = comp.normalizeFilterValue(value3); + expect(result).toBe(normValue3); + }) + }); }); diff --git a/src/app/+search-page/search-labels/search-labels.component.ts b/src/app/+search-page/search-labels/search-labels.component.ts index c7ad9790ad..1f12311210 100644 --- a/src/app/+search-page/search-labels/search-labels.component.ts +++ b/src/app/+search-page/search-labels/search-labels.component.ts @@ -59,12 +59,14 @@ export class SearchLabelsComponent { /** * TODO to review after https://github.com/DSpace/dspace-angular/issues/368 is resolved - * Strips operator from filter value - * e.g. 'test ,operator' => 'test' + * Strips authority operator from filter value + * e.g. 'test ,authority' => 'test' * * @param value */ normalizeFilterValue(value: string) { - return value.replace(/,[^,]*$/g, ''); + // const pattern = /,[^,]*$/g; + const pattern = /,authority*$/g; + return value.replace(pattern, ''); } } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 482d2db8c1..f5e91c954d 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -28,6 +28,7 @@ import { SearchFacetOptionComponent } from './search-filters/search-filter/searc import { SearchFacetSelectedOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component'; import { SearchFacetRangeOptionComponent } from './search-filters/search-filter/search-facet-filter-options/search-facet-range-option/search-facet-range-option.component'; import { SearchSwitchConfigurationComponent } from './search-switch-configuration/search-switch-configuration.component'; +import { SearchAuthorityFilterComponent } from './search-filters/search-filter/search-authority-filter/search-authority-filter.component'; const effects = [ SearchSidebarEffects @@ -54,7 +55,8 @@ const components = [ SearchFacetOptionComponent, SearchFacetSelectedOptionComponent, SearchFacetRangeOptionComponent, - SearchSwitchConfigurationComponent + SearchSwitchConfigurationComponent, + SearchAuthorityFilterComponent ]; @NgModule({ @@ -82,7 +84,8 @@ const components = [ SearchBooleanFilterComponent, SearchFacetOptionComponent, SearchFacetSelectedOptionComponent, - SearchFacetRangeOptionComponent + SearchFacetRangeOptionComponent, + SearchAuthorityFilterComponent ], exports: components }) diff --git a/src/app/+search-page/search-service/filter-type.model.ts b/src/app/+search-page/search-service/filter-type.model.ts index d9b9629347..d5a338de6d 100644 --- a/src/app/+search-page/search-service/filter-type.model.ts +++ b/src/app/+search-page/search-service/filter-type.model.ts @@ -2,6 +2,11 @@ * Enumeration containing all possible types for filters */ export enum FilterType { + /** + * Represents authority facets + */ + authority = 'authority', + /** * Represents simple text facets */ From 5491534ff8754f0a032b9bc84aae73ee571d8de3 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 5 Apr 2019 12:45:49 +0200 Subject: [PATCH 132/205] Removed message board components --- .../message-board.component.html | 67 ----- .../message-board.component.scss | 56 ----- .../message-board/message-board.component.ts | 238 ------------------ .../message/message.component.html | 56 ----- .../message/message.component.scss | 54 ---- .../message/message.component.ts | 83 ------ .../claimed-task-actions.component.html | 6 - .../pool-task-actions.component.html | 7 - .../workflowitem-actions.component.html | 5 - .../workspaceitem-actions.component.html | 6 - src/app/shared/shared.module.ts | 4 - 11 files changed, 582 deletions(-) delete mode 100644 src/app/shared/message-board/message-board.component.html delete mode 100644 src/app/shared/message-board/message-board.component.scss delete mode 100644 src/app/shared/message-board/message-board.component.ts delete mode 100644 src/app/shared/message-board/message/message.component.html delete mode 100644 src/app/shared/message-board/message/message.component.scss delete mode 100644 src/app/shared/message-board/message/message.component.ts diff --git a/src/app/shared/message-board/message-board.component.html b/src/app/shared/message-board/message-board.component.html deleted file mode 100644 index 80a37d8d48..0000000000 --- a/src/app/shared/message-board/message-board.component.html +++ /dev/null @@ -1,67 +0,0 @@ - - - New - - - - - - - - - diff --git a/src/app/shared/message-board/message-board.component.scss b/src/app/shared/message-board/message-board.component.scss deleted file mode 100644 index b38edffbad..0000000000 --- a/src/app/shared/message-board/message-board.component.scss +++ /dev/null @@ -1,56 +0,0 @@ -@import '../../../styles/variables'; - -.modal-header { - background-color: #2B4E72; - color: white; -} - -.modal-footer { - width: 100%; - display: block; -} - -.modal-footer :not(:first-child){ - margin: 0 auto !important; -} - -.close { - position: relative; - top: 0; - right:0; -} - -textarea { - //resize: none; - margin-bottom: 15px; -} - -.chat -{ - list-style: none; - margin: 10px; - padding: 0; -} - -::-webkit-scrollbar-track -{ - -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); - background-color: #FAF5F5; -} - -::-webkit-scrollbar -{ - width: 12px; - background-color: #F5F5FC; -} - -::-webkit-scrollbar-thumb -{ - -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); - background-color: #555; -} - -.notification { - left: -20px; - top: -25px -} diff --git a/src/app/shared/message-board/message-board.component.ts b/src/app/shared/message-board/message-board.component.ts deleted file mode 100644 index e05db8a9d6..0000000000 --- a/src/app/shared/message-board/message-board.component.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; - -import { combineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; -import { - distinctUntilChanged, - filter, - find, - first, - flatMap, - map, - mergeMap, - reduce, - startWith, - withLatestFrom -} from 'rxjs/operators'; -import { select, Store } from '@ngrx/store'; -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { TranslateService } from '@ngx-translate/core'; - -import { Bitstream } from '../../core/shared/bitstream.model'; -import { MessageService } from '../../core/message/message.service'; -import { NotificationsService } from '../notifications/notifications.service'; -import { hasValue, isNotEmpty } from '../empty.util'; -import { Item } from '../../core/shared/item.model'; -import { RemoteData } from '../../core/data/remote-data'; -import { MessageDataResponse } from '../../core/message/message-data-response'; -import { AppState } from '../../app.reducer'; -import { getAuthenticatedUser } from '../../core/auth/selectors'; -import { EPerson } from '../../core/eperson/models/eperson.model'; - -@Component({ - selector: 'ds-message-board', - styleUrls: ['./message-board.component.scss'], - templateUrl: './message-board.component.html', - providers: [ - NgbActiveModal, - ] -}) - -export class MessageBoardComponent implements OnDestroy { - @Input() dso: any; - @Input() tooltipMessage: string; - @Output() refresh = new EventEmitter(); - - public item$: Observable; - public submitter$: Observable; - public user$: Observable; - public unreadMessages$: Observable = observableOf([]); - public modalRef: NgbModalRef; - public itemUUID$: Observable; - public messages$: Observable = observableOf([]); - public isSubmitter$: Observable; - public messageForm: FormGroup; - public processingMessage = false; - - private subs: Subscription[] = []; - private rememberEmitUnread = false; - private rememberEmitRead = false; - - constructor(private formBuilder: FormBuilder, - public msgService: MessageService, - private modalService: NgbModal, - private notificationsService: NotificationsService, - private store: Store, - private translate: TranslateService) { - } - - ngOnInit() { - // set formGroup - this.messageForm = this.formBuilder.group({ - textSubject: ['', Validators.required], - textDescription: ['', Validators.required] - }); - - this.user$ = this.store.pipe( - select(getAuthenticatedUser), - find((user: EPerson) => isNotEmpty(user)), - map((user: EPerson) => user)); - - this.item$ = this.dso.item.pipe( - find((rd: RemoteData) => (rd.hasSucceeded && isNotEmpty(rd.payload))), - map((rd: RemoteData) => rd.payload)); - - this.submitter$ = (this.dso.submitter as Observable>).pipe( - find((rd: RemoteData) => rd.hasSucceeded && isNotEmpty(rd.payload)), - map((rd: RemoteData) => rd.payload)); - - this.isSubmitter$ = combineLatest(this.user$, this.submitter$).pipe( - filter(([user, submitter]) => isNotEmpty(user) && isNotEmpty(submitter)), - map(([user, submitter]) => user.uuid === submitter.uuid)); - - this.messages$ = this.item$.pipe( - find((item: Item) => isNotEmpty(item)), - flatMap((item: Item) => item.getBitstreamsByBundleName('MESSAGE')), - filter((bitStreams: Bitstream[]) => isNotEmpty(bitStreams)), - startWith([]), - distinctUntilChanged()); - - this.unreadMessages$ = this.messages$.pipe( - filter((messages: Bitstream[]) => isNotEmpty(messages)), - flatMap((bitStream: Bitstream) => - observableOf(bitStream).pipe( - withLatestFrom(this.isUnread(bitStream)) - ) - ), - filter(([bitStream, isUnread]) => isUnread), - map(([bitStream, isUnread]) => bitStream), - reduce((acc: any, value: any) => [...acc, ...value], []), - startWith([]) - ); - - this.itemUUID$ = this.item$.pipe( - find((item: Item) => isNotEmpty(item)), - map((item: Item) => item.uuid)); - - } - - sendMessage(itemUUID) { - this.processingMessage = true; - const subject: string = this.messageForm.get('textSubject').value; - const description: string = this.messageForm.get('textDescription').value; - const body = { - uuid: itemUUID, - subject, - description - }; - this.subs.push( - this.msgService.createMessage(body).pipe( - first() - ).subscribe((res: MessageDataResponse) => { - this.processingMessage = false; - this.modalRef.dismiss('Send Message'); - if (res.hasSucceeded) { - // Refresh event - this.refresh.emit('read'); - this.notificationsService.success(null, - this.translate.get('submission.workflow.tasks.generic.success')); - } else { - this.notificationsService.error(null, - this.translate.get('submission.workflow.tasks.generic.error')); - } - }) - ); - } - - markAsUnread(msgUUID: string) { - if (msgUUID) { - const body = { - uuid: msgUUID - }; - this.subs.push( - this.msgService.markAsUnread(body).pipe( - find((res) => res.hasSucceeded) - ).subscribe((res) => { - if (!res.error) { - this.rememberEmitUnread = true; - this.rememberEmitRead = false; - } else { - this.notificationsService.error(null, this.translate.get('submission.workflow.tasks.generic.error')); - } - }) - ); - } - } - - emitRefresh() { - if (this.rememberEmitUnread && !this.rememberEmitRead) { - // Refresh event for Unread - this.refresh.emit('unread'); - } else if (!this.rememberEmitUnread && this.rememberEmitRead) { - // Refresh event for Read - this.refresh.emit('read'); - } - } - - markAsRead(msgUUID?: string) { - let ids$: Observable; - if (msgUUID) { - ids$ = observableOf([msgUUID]); - } else { - ids$ = this.unreadMessages$.pipe( - filter((messages: Bitstream[]) => isNotEmpty(messages)), - flatMap((message: Bitstream) => message.uuid), - reduce((acc: any, value: any) => [...acc, ...value], []), - startWith([]) - ) - } - - this.subs.push( - ids$.pipe( - filter((uuids) => isNotEmpty(uuids)), - mergeMap((uuid: any) => { - const body = { uuid }; - return this.msgService.markAsRead(body) - }) - ).subscribe((res: MessageDataResponse) => { - if (res.hasSucceeded) { - this.rememberEmitRead = true; - this.rememberEmitUnread = false; - } else { - this.notificationsService.error(null, this.translate.get('submission.workflow.tasks.generic.error')); - } - }) - ); - - } - - isUnread(m: Bitstream): Observable { - const accessioned = m.firstMetadataValue('dc.date.accessioned'); - const type = m.firstMetadataValue('dc.type'); - return this.isSubmitter$.pipe( - filter((isSubmitter) => isNotEmpty(isSubmitter)), - map((isSubmitter) => (!accessioned && - ((isSubmitter && type === 'outbound') || (!isSubmitter && type === 'inbound'))) - ), - startWith(false)); - } - - openMessageBoard(content) { - this.rememberEmitUnread = false; - this.rememberEmitRead = false; - this.markAsRead(); - this.modalRef = this.modalService.open(content, { size: 'lg' }); - this.modalRef.result.then((result) => { - this.emitRefresh(); - }, (reason) => { - this.emitRefresh(); - }); - } - - ngOnDestroy() { - this.subs - .filter((sub) => hasValue(sub)) - .forEach((sub) => sub.unsubscribe()); - } - -} diff --git a/src/app/shared/message-board/message/message.component.html b/src/app/shared/message-board/message/message.component.html deleted file mode 100644 index e6dfe7dcb5..0000000000 --- a/src/app/shared/message-board/message/message.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
  • - -
    -
    - - - {{m.firstMetadataValue('dc.date.issued') | date: 'dd/MM/yyyy HH:mm'}} - - - - {{'mydspace.messages.mark-as-unread' | translate}} - - - - {{'mydspace.messages.mark-as-read' | translate}} - - -
    - - - - - -
    - -
    - - - - - -
    - -
    - - - -
    - {{messageContent | async}} - -
    -
    - -
  • diff --git a/src/app/shared/message-board/message/message.component.scss b/src/app/shared/message-board/message/message.component.scss deleted file mode 100644 index f7812e795f..0000000000 --- a/src/app/shared/message-board/message/message.component.scss +++ /dev/null @@ -1,54 +0,0 @@ -$user_bg: #e6f2ff; -$other_bg: #EFEFEF; - -li { - margin-bottom: 10px; - padding-bottom: 5px; - width: 80%; -} - -.chat-body { - padding: 5px; - border-radius: 5px; -} - -li.float-left .chat-body { - background: $other_bg; -} - -li.float-right .chat-body { - background: $user_bg; -} - -li .chat-body p { - margin: 0; - color: #777777; -} - -.description { - color: #666666; - font-style: italic; - padding: 0 15px; -} - -.pointer { - cursor: pointer; -} - -.max250 { - max-width: 250px; -} - -/deep/ .float-right div[class^="clamp-"] -.content:after { - background: linear-gradient(to right, rgba(255, 255, 255, 0), $user_bg 70%) !important; -} - -/deep/ .float-left div[class^="clamp-"] -.content:after { - background: linear-gradient(to right, rgba(255, 255, 255, 0), $other_bg 70%) !important; -} - -.truncatable { - clear: both; -} diff --git a/src/app/shared/message-board/message/message.component.ts b/src/app/shared/message-board/message/message.component.ts deleted file mode 100644 index 0119b662ab..0000000000 --- a/src/app/shared/message-board/message/message.component.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; - -import { Observable, of as observableOf } from 'rxjs'; -import { first, flatMap } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; - -import { Bitstream } from '../../../core/shared/bitstream.model'; -import { MessageService } from '../../../core/message/message.service'; -import { isNull } from '../../empty.util'; - -@Component({ - selector: 'ds-message', - styleUrls: ['./message.component.scss'], - templateUrl: './message.component.html' -}) - -export class MessageComponent implements OnInit { - @Input() m: Bitstream; - @Input() isLast: boolean; - @Input() isSubmitter: boolean; - - @Output() emitUnread = new EventEmitter(); - @Output() emitRead = new EventEmitter(); - - public showUnread: boolean; - public showRead: boolean; - public showMessage = false; - - private _messageContent: Observable = null; - private loadingDescription = false; - - constructor(private cdr: ChangeDetectorRef, - private msgService: MessageService, - private translate: TranslateService) { - } - - ngOnInit() { - const type = this.m.firstMetadataValue('dc.type'); - - if (this.isLast) { - if ((this.isSubmitter && type === 'outbound') - || (!this.isSubmitter && type === 'inbound')) { - this.showUnread = true; - this.showRead = false; - } - } else { - this.showUnread = false; - this.showRead = false; - } - } - - toggleDescription() { - this.showMessage = !this.showMessage; - this.cdr.detectChanges(); - } - - get messageContent(): Observable { - if (isNull(this._messageContent) && !this.loadingDescription) { - this.loadingDescription = true; - this._messageContent = this.msgService.getMessageContent(this.m.content).pipe( - first(), - flatMap((res) => { - this._messageContent = res.payload ? observableOf(res.payload) : this.translate.get('mydspace.messages.no-content'); - this.loadingDescription = false; - return this._messageContent; - })); - } - return this._messageContent; - } - - markAsRead() { - this.emitRead.emit(true); - this.showUnread = true; - this.showRead = false; - } - - markAsUnread() { - this.emitUnread.emit(true); - this.showUnread = false; - this.showRead = true; - } - -} diff --git a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html index 6ebec7d1ae..4b9b93e7e3 100644 --- a/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html @@ -18,9 +18,3 @@ - - - diff --git a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html index 7ab3c12ea4..6f4ffffad3 100644 --- a/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html +++ b/src/app/shared/mydspace-actions/pool-task/pool-task-actions.component.html @@ -6,10 +6,3 @@ {{'submission.workflow.tasks.generic.processing' | translate}} {{'submission.workflow.tasks.pool.claim' | translate}} - - - - diff --git a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html index b19299e164..e69de29bb2 100644 --- a/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html +++ b/src/app/shared/mydspace-actions/workflowitem/workflowitem-actions.component.html @@ -1,5 +0,0 @@ - - diff --git a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html index 02c3384fcd..6c3a047f73 100644 --- a/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html +++ b/src/app/shared/mydspace-actions/workspaceitem/workspaceitem-actions.component.html @@ -13,12 +13,6 @@ {{'submission.workflow.generic.delete' | translate}} - - -