From 81b4c22758d7f071ef84b83ac1ad4d9f425efab9 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 7 Oct 2016 10:41:29 -0400 Subject: [PATCH 001/323] fix up gtfs api to use s3 directory --- pom.xml | 2 +- .../controllers/api/GtfsApiController.java | 32 +++++++++++-------- .../manager/persistence/FeedStore.java | 3 +- .../datatools/manager/utils/FeedUpdater.java | 4 +-- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/pom.xml b/pom.xml index b409fd409..d02dccd18 100644 --- a/pom.xml +++ b/pom.xml @@ -162,7 +162,7 @@ com.conveyal gtfs-api - 0.2-SNAPSHOT + 0.4-SNAPSHOT diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java index 00c104a6a..d93cf8cba 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java @@ -6,6 +6,7 @@ import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; +import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.FeedUpdater; import com.conveyal.gtfs.api.ApiMain; import com.conveyal.gtfs.api.Routes; @@ -26,23 +27,30 @@ public class GtfsApiController { public static FeedUpdater feedUpdater; private static AmazonS3Client s3 = new AmazonS3Client(); public static ApiMain gtfsApi; - public static String directory; + public static String cacheDirectory; + public static String bucketFolder; public static void register (String apiPrefix) throws IOException { // store list of GTFS feed eTags here List eTagList = new ArrayList<>(); + cacheDirectory = DataManager.getConfigPropertyAsText("application.data.gtfs") + "/cache/api/"; + File dir = new File(cacheDirectory); + if (!dir.isDirectory()) { + dir.mkdirs(); + } + // check for use extension... String extensionType = DataManager.getConfigPropertyAsText("modules.gtfsapi.use_extension"); if ("mtc".equals(extensionType)){ LOG.info("Using extension " + extensionType + " for service alerts module"); feedBucket = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_bucket"); - directory = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_download_prefix"); + bucketFolder = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_download_prefix"); - gtfsApi.initialize(feedBucket, directory); + gtfsApi.initialize(feedBucket, bucketFolder, cacheDirectory); - eTagList.addAll(registerS3Feeds(feedBucket, directory)); + eTagList.addAll(registerS3Feeds(feedBucket, cacheDirectory)); // set feedUpdater to poll for new feeds at specified frequency (in seconds) feedUpdater = new FeedUpdater(eTagList, 0, DataManager.getConfigProperty("modules.gtfsapi.update_frequency").asInt()); @@ -52,17 +60,14 @@ else if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.enab LOG.warn("No extension provided for GTFS API"); if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { feedBucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); - directory = "gtfs/cache/"; + bucketFolder = FeedStore.s3Prefix; } else { feedBucket = null; - directory = DataManager.getConfigPropertyAsText("application.data.gtfs") + "/cache/api/"; - File dir = new File(directory); - if (!dir.isDirectory()) { - dir.mkdirs(); - } } - gtfsApi.initialize(feedBucket, directory); + // TODO: update other gtfs api conditions to use + LOG.info("Initializing gtfs-api for bucket {}/{} and cache dir {}", feedBucket, bucketFolder, cacheDirectory); + gtfsApi.initialize(feedBucket, bucketFolder, cacheDirectory); } // check for load on startup @@ -70,15 +75,14 @@ else if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.enab LOG.warn("Loading all feeds into gtfs api (this may take a while)..."); // use s3 if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { - eTagList.addAll(registerS3Feeds(feedBucket, directory)); + eTagList.addAll(registerS3Feeds(feedBucket, cacheDirectory)); // set feedUpdater to poll for new feeds at specified frequency (in seconds) feedUpdater = new FeedUpdater(eTagList, 0, DataManager.config.get("modules").get("gtfsapi").get("update_frequency").asInt()); } // else, use local directory else { - String dir = DataManager.config.get("application").get("data").get("gtfs").asText(); - gtfsApi.initialize(null, dir); + gtfsApi.initialize(null, cacheDirectory); // iterate over latest feed versions for (FeedSource fs : FeedSource.getAll()) { diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index dfa74f1c1..9ad2e89fa 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -44,7 +44,7 @@ public class FeedStore { /** An optional AWS S3 bucket to store the feeds */ private String s3Bucket; - private String s3Prefix = "gtfs/"; + public static final String s3Prefix = "gtfs/"; /** An AWS credentials file to use when uploading to S3 */ private String s3CredentialsFilename; @@ -93,7 +93,6 @@ public List getAllFeeds () { public File getFeed (String id) { // local storage if (path != null) { - System.out.println(path + "/" + id); File feed = new File(path, id); if (!feed.exists()) return null; // don't let folks get feeds outside of the directory diff --git a/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java b/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java index 7458c5292..8e2fd3ecd 100644 --- a/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java +++ b/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java @@ -54,14 +54,14 @@ public void run() { LOG.info("Fetching feeds..."); LOG.info("Current eTag list " + eTags.toString()); - ObjectListing gtfsList = s3.listObjects(GtfsApiController.feedBucket, GtfsApiController.directory); + ObjectListing gtfsList = s3.listObjects(GtfsApiController.feedBucket, GtfsApiController.cacheDirectory); Boolean feedsUpdated = false; for (S3ObjectSummary objSummary : gtfsList.getObjectSummaries()) { String eTag = objSummary.getETag(); if (!eTags.contains(eTag)) { String keyName = objSummary.getKey(); - if (keyName.equals(GtfsApiController.directory)){ + if (keyName.equals(GtfsApiController.cacheDirectory)){ continue; } LOG.info("Updating feed " + keyName); From 52cd63a0b51187f9139bd3b68418468cf6f508e0 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 7 Oct 2016 11:15:21 -0400 Subject: [PATCH 002/323] icon for sidebar --- src/main/client/assets/application_icon.png | Bin 0 -> 24953 bytes src/main/client/common/components/Sidebar.js | 12 +++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 src/main/client/assets/application_icon.png diff --git a/src/main/client/assets/application_icon.png b/src/main/client/assets/application_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..01e7be5be4c62849fed765b16603a0268c37c944 GIT binary patch literal 24953 zcmaI7cRZWX+XsBxO6{VkQZ;Jty=&B__N*1PH?bwCsS8i?@7@)cmiW(HO@G%=UpK#y|ICGe zb0su1%Q`LDT<|Am+T zmP7XcoGS`4BYJ7t|BtEv^9aPx<=g)OF8JYppzr1f;ynli_S4DR9RT34qpPWI9=@iI2GtotBo*?l1QQPH3x((x+d)e!07bIXmHsmXlT37>N4U@OCKs z&Y%bjx_Hhq^ILchMy zIaD1Ydwn5BOo4R1LJX(@0Bvb{k?qJw_05-G+4EzM540U0#?th3DtyYN1OjEBa$8tx?ZPm)g?2esBH zJ%n_^MiFDJ+U<|rZTEs&Yw5ptY7_h$AQf_&<&W%wIG||`-m43m+*(b{j8hc4V3DBVioo8u@ds%Q;U*w(L#|Eq*LcV_DCE17qBaC0_Yq?6ar5Tgz7f zH~<{(o|KzQS~q4xsHF(UxF1l)b-Pgzf2P0W_1e(y(UON@L#e46F~CTF3mGaizfx_D z@=_&mB}H7~)YX?hyq?drfvVn7gdk7(9oq1OOMW8EbDdz#~ zA9mk|oC{XUYc|p^0D~D(Jof}?!>?L^YeBY7W=$jw8o3vM3jQ>mR5Q*1nkrBLoyp(F zX2Fjj^34P<1hw_3EjYypFUOlt{?6-DHEkpW>0CVRrX?XXeoXu zBo@%gX~uQK?vv00YCNf{@4hL2!R~~M)55d zq}ZkS7MyR)aZVo!E;AK&zPWq+;>LEx5*e-Fs;*H$SMk_rMZ%@((8$8vbnakKNK-Mr zAb32S&4h#i$h}j(;b<~aiLe+aRpW1X9@lw|{3BcdG}4V!Xej6zJynV&- zXEasZI7kfm7IHo_eo^4~tLSD5pr+jQ@&=>_P8g?ZLPh|5+Bjzpz}b%f7$*e`9=UTX z3X+FYTYwTZu*2?2C+Yr$+K*BKh8t-Iy-tabx-{5{0TU%OGB+2)vkXp=N-*Z>5Fr1+ zaydo(29gmYuhl5M0Kf&rl!yt7UQ-YPgLRly`wWMGOu3#Iew(u!+0Kf*G zFHhNX+A#Y9YEPAfSZX+(u2q3*j{f+kNH~WSd`cS4VM!$7A`L3UYvaVX4zDzX93us$ z;2k`Rg5)RHFPDX)RjcY9r&CYYck=e-tAfu4?5k(No@i&JO573jTCT3gFyU%-eZ2+6RJ>f z6-)v02gZ@th7z0<7jD}qzZFjmjDf1C${psBETIDKa6J-GWT8!Y7513XSyf?RCDHZVKNjY~OKlusmr5GA$Iva6mpo`4pbSuDmW2R9}i!>6t*xQb`L#yZmGcr0F{9qm`ozr zU2w&S09@7vEbYxDi#i1Z+&co~Czn{Cn!tvc1tT}^asY6li2IFI@$hjF55-I#`bu48t8;-Stw*uE*&BV#J}1|eO25AIUzaF7=)v&Xrn>X052$4vTNRQ zuCbe|125iU_A57U4TXT#5%7?j2r|Cpq3g8iL-Ix^*FS^tZ0<*ssorrY193C=f(aQ!Z zg8fnvVL0EZrH6D}55bfroUPw-pH{4E_o+vU@cGucFkw3_C>^ab5zoEXq=ayVuh(1D zsQ{pC&8hQb&m78=5lgA2U0<4uxlRK<{YHZxC0f!3OpMUT_FP3C-|Lj%Q`Z`Z8wr6i zJ&-Ys6|LD6A2NSV0Kg{>%ig5L!;h~3U)Imv%`wdoOCJuP_~_%Ut!mCSMMC(80$9?) z<|Y!qzUyhNoa#E@Cl%n;8&~-gS2yQe(>>x59rPepa=lYIESN>4|X;; zTcqrO?J41D`;-_Wpj)&4sC#-vu6{Iu7zmWZYvm)C{V9AvnDF+@BKofpKj#AOv|x%X zM_@cnu9E;TruiU4sFriB+?qpdTM`Ju`A#ogS`ALoAh<5-LQ?#Jpxe{y@2Aw)U*-pMEpvn$N;pQkh0Xf!4d>&4f%LL+RCY8 z>~2>;m)|4C)W`>i@w^G5)lNm}l{t2e6%g^E2}J)D3Lq$nouWCY=QcU;bp1T4DnfhW z=3*iR*6{ENCQNgwR2?7r7^+y7`y(Hj6{$Sr6GEmYQg6|vCB+e~4ZQG2ua09iU>KLC zG0o5VLhVTHpmb8~tbBu;9n0KZ7ycT_2}3}-fAfijOVzdOu0TLeZrq%JRNOdyQ#MAb zVs*H|aM5}BJr5Ly&6V-nQh?_MG;coDxB?u?-={VW=9i&J@6PXJ-)=Kci16w0S)!)> z#Sy;*4dGGSoQoj{V*tK;yV#FmWAM~-d{Vu5ZVetQPXKQj5Y;M75XCV6K!U}A@&!xQ zZ&x&d3KuXq0GSh)!;{n->Nq|BPCk^w+PvWsDK?MGN38}nTv@*Qg5`Gp=+%?bL7H(H z1WJw)O_LH(27yscjk-pE2!J}9kq`>n!29HY!AG%(TL@Qcf{H!1vA16D`ffTC`n16m zlBs;mAYAgn%g+ktHm8mh12ZqeZ> zl!77<{**Vpgm0eJcF#w^_n)0?P)0GsdF7wkiu7YOlw&h|%6jA~YEY|VO?Lgg{TiIa zz?QRn0Lqz!^9*pJkuG?-k(4xqA5uhnL1}$Id77>%{295+qz?%S4fJ$#KMQtNoB-wwSmw?zWm#{ z{^-0M-e7L!{vGn-g^*zRE(y6-K_K$Bjs8f%oN;BuBsQ-l7PV#9J%aA6*rPb! zHg&2h*14Q*x48W_3#i%Guem-d7@<5st4FHpf+2iMW+Lq)Vr{yC!;=HKGIa05Vz*#M zKIP5U)O!-FQpTHuBIN-pF@A1nvzSfK&jWKQiBQ-H`B2I_=Yzt}E0@9K9H{z&PvqpZ zBJ*3^G`9h@t3TQw5r_+V8Z&)0%ZE3oDv@JWoS2sP-gc9tRaDvcXyyG``S7I#9%z84 zj6pr`E2_CKvmL0q8!_AGT9UlTM?hohP`ok}@s%^@zQi+3^?l4^*yCuY+2H-}LD0~E zUzUK>EfnJlPWMbuG)qGciSa5T;g*UROe+9?p5mgOGAr=c%SC1-APqMF`O;N|#Nl%CJ>^y2U z9n2b@MkkO12&#}6>HHHB*%t0D6XCyKATYBkdA-J@>I-$bDE+wyksms>UB^XMPU|+* zoTTd~hOPA9nZZ;tOS6Ubn^sIG92GNU8VeyD#=7wx6AQYI(({AYJ9U5^Ec9cWYQFo=qR(5x*AKzMW(pD`-}3~LqFTi8#n{D zljRNafoY`qg@YGrdO|S|ZQEuv%_lGxiyeB`n zXTL9F*-le#^AH`~vtyJS$9ntTV&66SomB&zD-+nM{83CL5^!_Z&l8hu>C~9MVFN7B zNgUeRpy~Bhq1OX$8#L+8K}V3&_e7a4)Tj0>CtaRk_+RgBEmUE^f}sEgAyG>pMLR|c zEpI)3&eyg<@$Ee29fIsnUiR0MsI&nozT{e)w!$2pScD2(_x z=@O5PN&0b{+lLYbi0IMFxvY7)cBH`E%on;c2ZAx~hl%_*d#LqBg>#)`z7B5g8qOHG z8$Sprn74tuH4Wa*M%6^VF5?Lj&qgjf8*g2dGzzHwt*>WQtk&3e3Vje8IaK&Sc!L5s zd*)=HJ^sm!dUu;VAL*=RpE~?=Z#lg^1-tG{^2b54!b{NH4doL0--0z;}f3 zfJH~gQ{>T{${agO%w-wk%daZtRSTXP8T;oeFtN3*X%b3#q zB(&Es{|7m6RNsOQkh-A$_D<;O(xbb4irN)O$|E0}rM%zp?@5S-pX-Pb@B7ayfjD@< za>KLQE=@M!Z;vEuf%9Q(?i5goyhws$GIajT_PGVb_{(`8s15l{ez{N6xSOG5?mHYv z>y*tREQkesrSiIR%V$zmfH94hD|FKE-vOkn&OZ##G;8{ZdX-5%gRPQxC$iqlW(0TS z-&%OX+5JxJ!2&ku*O*c&g{a{DI>5SZ2)pH7FZ=lUyDsHBLCQvg^TG-H{Q93$ zonIG5liBbBGWnAbUVp6m2$vZ7F{5!L)UwGk`AGgw!hZNImkV|wbSPtAqkwvMh1lQ0 z{EGb7HnoJ1Hp2R3h{!1uA++lBeFm~`4u*rAXG%f`E3R)0r6ifn59qA0;_c7@Q-BKUf4 z#VZY0TLSA*Y$p{Qx%v2NmunmSc4%gXDl7wo{oUXsd+IB2?~kO0gmlf%#fys9Tujr? zFsu#40NHqv7y(B)cAZsedHSpM-OjhHM}zkl3Lz&DY`|rA;b}nL3cSn4EK#^dgo)ykF1~{3OgP1Tw~L)H|1=HCw)^%?7PBa zShnL$n$urgU_zvXe638qA|dd^cT}m;{ZuANy_-3bw?h$r_1W8|nvjnIbl)La*12Htb0YF8cJ0 z3?7gcCULGoFIwFJYE+d)b-jxij66VPI?TTEuYt1v%@)7EFKT{8|IvFT()u|)s^C1Q8{<^%-rv=mp85vzL#`}w6m0a+&Xyww9A_t zGZW6(9Y2tegA;HmYF7CW`jk|qGq?7qqQ|2KepoD;ey2shC!tlUp^2_zY8$FL^30b9 z>Yu=_q{Q1N>#QhS-}c=i25*Dd@|q)K)-80U@R1;jR~4S3=oWcruVq~<5vz~I*nzBg zu3nXE>27Z`Md7im@_3TI?YC5Z?`8@ocY10FNMX-jFFWSF(a_i) z*}ef%oobb9G26OTwd@>g#%L~z)K9H>iCG=O#%nzLp~>8G7*HBJ`-8fA4=?Q;I7jf76+O6BFFPFx`dSPW9I!EeC%L9#v10LJKZDl8~jIHD+TvUp0wBgz!n zz+@w-AwI%dtg==&(D*Mn0qXpU(@Boi?kt=4H_BHe$pC zostho!k65FZ+i%tEBn3=rO@`z9E_Z>GI8ZQ3{4KrgSvHa`4KD$CZ*4_|5@kNv)g>| zp7XSUaWUynN0Z0`l*`OHz2UGs2Gd1YPjo?S$hl{B>YriVT55_rucWL<50(hQK~Jy` zb&KM+1_?Vl?y8s6)PI3M|=dE0}q>FqhTF2lUe(amJ-;Y|G2 zccP&Jme^Gu1S6%Fs!Y;qf#rft!kuMzV*5`aa}b%zhpXmMB;8du?@QB)uY5HEUQigj z9k6S4K(-JyU)H^XRwfydxa4KUfX>1)!^Z@Z;bewAmf0<%4uBK$i;DSVTpR^&j?}79 zmkGx9${z&@H!^qsCk_;E(|06;aL`x3PIPuXNQRtQY6<5?iqph-WO97nye)n&*H-(8 zVa8;~G~3+|*l99M>S#ctVwem#>IQqjZ65cOO4~yJd&0>2LcwY~={jdUABo@(0>0uF zlEB&lhj}w0?F#SY_d>+hl%I99%#Zz7MXh+vT}0l4m-Feq1o>9Ats}Ytf@UsS=5B1o z0_=iB4OOOWlYAz(Jgu}kR?A>JPK6)%6FPtOgRCw}hM%=^2`6bPX}21k=|ftXKC0{& zSNdE(_Y<)MYVOj7;$NFG=|-3~oRPj0c&Q@#jMJl<*YfIi+10KAfrb8P?9^*$@_o|P zU}iIyba9v>rzk=9%n_MXLMlsiQrY#)^+&&TNR;q~Si{zs1$Dd32&p~Bm-Q*{eZRLz zXTO0lUG!&I(+Rh9%vf9ppa*p{F$9bU2`qKIXlGB@eK`Ocb0s9w3(Q6qgHJ;SeKo;% z?nMe2-Cci2*A#1n4)zVw3?JUYt}>B)pSC7g+v5;m|K9oimFUocV%n2v#x*Up;AolQ z_|wHVwhXu4sAT_`nIt*-SlRW{)2N;~LE`PNmPbs!M9uGdfuF`4Y7F%kCmKA%-o?o5 zAIv*vKBjF7F3Z32?!V3prFfg&{7l_u`Fvha>MnqCyf#HrL!FKiE z4Q?l0L8kZgz21M4{Zt0ie<1w9LLo+MPjsAHvMG0kWMcSiJw?Lsi{;nm2Q)PvJOlh+z zvi{)#&yP`9Qf1TOn*Contr@L^8p@tdgsUJ{)34?6=PBhf_st$mR}XN zv!CwVmkJ~4HexqQL8-sGt)SCbl0Q*Va_(_4GeB{j_!Y*{5P8vY6&XyaJ4?9mqm>w_ zbV9-2W*|2N$ueCQfgLwSAmD`N>Tuou?W7QUs>w53dg8h1whL}YrZx0Jzjifb>s;f@ zECI>;k!CZ%o&WcTeZ(Oe-#e9?;^?>hT(>6+KaWqE{> zCXqC?U|U7IvGt9GQEV>z<3@8NTNCbE0NMFE$*-AaD?NqmaB2@UXWE>bLsr4;cXRZn z4?#^@q5d<6s!2&+w4*5biIz)Zyk!tS`h_f=ewlqe6H3)73Qy^JC~fCFD7oxBB};RZb&5_>V* z{(4Fr=NX4+V^qeX&&-wTFvAkxM25b*sS9|w4Z5M3AZ?DUA8{H_mvb7!^gWvuTf@S( zicVYX=+nOH{^lOuzH`bW>*+Xt=9`Ru)O`GtUu-tI=C0eSSdqCbu(pRZpOYwBUI`1* zmL2&aPFtUuW_FEyF7BQ_%aYLZI4?sJXyG@3p&#p?dvitjW+Q{61emw)LE^Xq*^PZEk3lX#%1-!e@K z<(MCp_37K+Nc#9zcTtGDIy*ovS|IO2{>T2$?P8v};V*F(RDhKH+z{Sv%y;UQq=H3I zJ^ND)_T(P;){hm=P}jbE!!UGk-{Nf4S+jkJoj}W%hyFYJxD3od(_!zh_JoC}zw<}i ziCYJeU(_qGZ?JiJ-wPM z#&kc}UUL}%1x#k98_K~X=y#G+p#iIXL!8}4lL+0 zQUp#^tx)mOx6N?pH=hqy)ju@DmChLEbJmD`sro6U;1dx^DUTE8SuII5Re-TAX6=a? zL(*idayf3I5|!*Ff?2ZF0Ws0iMuD2C4E?;^c0#9!@!jE1{Tl1Fcw6)PUXR=5^rJ*g zbyY$n+o~ZgL5`$h_k{*u)KFMfHu}FOglCSsOU>PqsSbDVf2eYoy)5>OqvE0Un>1Os zg=zq0^;u}rd45OoB(?u~j)g12M!=8?u)Ldn|DXgCD33Ec%!nnG4#Yt#j(Z0=BrV+O zTSol&lZT$`p4qsiLwhE;o};Q7T%HlZ$M`SC++K^j!{Q;jhPHLZsS}DY%Ab4h;)b|l z4HoNe=Jg!s+tD|*$t5h_+544D{d}xy{GMy4hQ1Xb^z^NSSzv?x7^8GJ4R|%$@3NGmw6~Hs2h}YwTY{=dAmlU0pG-*2nSDAlNih9brb%QniKS3 z{aqQ_p0y6@jZoOkQz0hyV^FL8H`!WtRi%i|nu{}<9!{4#Hdz6^IGAII^_^cd{ugqv zli;iRHNC4mbc!&2x7yHKf*mRfpM_;QUQ0ZCmR5r@h|>0%wo$rvTBQV@{+npu3$Z5m zIc%`~Rqfp!`&@x@Y1R2`jZFLWNmja_R!vIiLL!P$`5WuwV%(K*Jq!o zq+ArGFA!Q&TrkF?dF(vtBiSv~0&p_W+E19>mntQldo*8dgeilwvL3MXnYIXD(2z6I zOtwt(lLMx1TVf9c9uQbFE&s?>%|~{->E^3G8fvcnZgTN^T&Do$M`=Ui-j<0$(>+DC zL>3-Po{DrJyBJDkrCB@sTc=NcZ#Yz2*0Saqte8oXoeuMq-hG6VtI}y+CYhS+X-$7q zI;wy8wZ+Xn>Y~BYtcmU9v|7~>6`^`$DOu8Ow6{9(`##I|@GtY912vhfEy^;rr$=eL zWWaU?%XD=7ub`naMCa6NR}(Fw4r$kxCfXK>2^5y%VT){3>d_y&H={nG3cGLqK0xhb z`yZDb7hJnBDN3@HUNU1RXQ!|ttf4$ow<-Pb-IR^V#e&)>gCbGAl)^B(qb}6s9`!xO z^6g>8yO6%+sjDu3zdZWn+tZSXBN@C=?cRmN z=+D`&O$N|!(XZH_%KoksVj+xkpDN^S(~V@eDt2F1`6xX?AJ`gud*0?E$YGpAbJU3d zO@F#Eg3t;+VeozClgHX=J(tDUhL^qy@IpzugM8YEid)W$FiC|gnvBWR4Mn+v+)XWO zuDg3*{rWq^N`uNrB8(_)NguVA&Fus&aom^~csXA+?bceGOJpBQA)&k%eR|L#J(g4; z9yJh%HMX47H!8p42&E?iLQ(l%D_ZW-eMa6lT^SxL>{?BYI~~^d#X$AuCwgoxBJMU* zJeAei=a@NR*$bH3Ly6bSiP7N?zY02RTPfPPEy^f+pGd|A;BfJije{6}O&a&= z`NVl4v)%X__dI&wT+rar3+1dx&#f5a;-{aI`*=~R$B)f$0v!eqH4EN2cp8mL*J954 zt4VTdHJ4BChBL&}a?AJ#0$C-IZy=wqB2V4XG($H<9ALJ6Xh*T z5EXaYtFACB7Ofl;Rq5nQ`HqfRYA?7QWOl~SRsK{xy4cKrK<$G4*&>W-IQ8;L93AJE zJC@g=om70@DlxNIpZSh~N8?!#bt&tk=-p>-lvaw!-lkT_>ubn~mSEw~lSj4+xXAou zOx=}EzH{c3T41T{q2W;`i=%>L#Ey*l%b!y2RQ@6nA4R69Yv$j0xnGIRn9CY5e!aJQ zezTlCq*3-frw>U)VPojAVHr9tVIy-DI$`Rg|6^Uu&+la+3^f$I;Jhrj^*i^VdAHc# zj1WFL^%dSFv5s0hR|Qed@R`XA)gP#xC|{<02a@TrFT*1GEt>VO#QJE9f>fm~A%{-Q zjSs@Ls&^tjPE1!`EgYG^J;*fBFrFojQr|MiKQ{z>4=CU{(9f|5jPAaEzEC^y08)P8 z$>X{LQAj95RYkaxbuC$5p6^-pukRlV^cxr}L=QK2*&;|ga)Oj2o8Erqv5Tnt#eqNp zLhw}6fuJ$r&V66=mvZ;5#fygRm)d^0^-ND8%9Tf#;+K+_uxqa%EsIhw~G9__$EuVWU8bnd#4w7VPg${orgh!0u7x>7q za$2O#m|{eqwbDup@`=tj-j5=($bi}}u3Ooi`X|W*F#sQuNdO-L^CAq-*~^t|Id$vE zBF3YJ%$GysI@hdo7aLIMJhQ>7kFJZKF)4`oac#eo0m|5kTWS(ORnp%4{1v>SiPkq_ zXNH8#Lx-EbnnZ~;#Ugp@5<@c{x2>!dJO)gjeJhf#>>KjVhlPG{Xrx6U$FKByEg~Ob z(w^#bcLv}%QwOkUdPVm?v&;MpegyUBt3g7Yqg7akM*{Th$RB`RI1@%V7g(swfVLnN+{1B4RC|uTdEAzsj zGDeCC_!{RYunC^vofU#tP7DbSA+FhlU(;kt*5{ot{;w&>%P;oM2P{2GX}?Ze17qyV z=AYyd7t(aNM*^${6wt-&J)(s#gAfmH3ZKwn3TbxrTY}^pOVlYe;4O^|g+`bqVEedC z&(>zL2x*67iLa?SkQ}`q9l6R=!4+uwN=EK!%9ys) zb7AecVUb!Nhkwk>_xc0gg0gev%nry`C_69ROvT~UZdRb8%gt>ISnhEk3d%eOT zV`H0Sl?`@+Njmp9vcK2D?1pp^C~LKKaTwm<0)R)3P&FhNY4Gs900G25*ZCd9o8YBZ zA@5svFYcPZ^uRE@3-K+6xsy2wbng+K5ZCJ`)|B_PP~&oK z7qBJ_jDjdB{W#Z~Rjl9|-}Ie7b2%Q(e8;ar`>_^>clN5Qt8Tj4gF(IhTjR zqomPwz#z>C_sw94fOSx{^eJh2eAzM86$TAEDSDEYb_=V)r$=3nW&8yl`1GJTgRqBj z2hs?QKdT&7e`iXVbSSbz_Jrq$0#p2JzDhX*Vv-7hMLI3x-JLWR+(uVh{6orI`Neqe zK%@in>O#uA)zOlP52jb&D~i5*;fE9*Y!cZz-@>Kv`2M^IA_3fxBx8p*VG~Xl6pK3f zH+F%sEkr74D^##bXWCs$s=dxKK?f%Cxh`zrsSstZXjUOUuMgck+$Qt#YKD;3Xtp3w z;YhY-b`ji4FW~YV5=Mm*tWW-QrKB3;sk&2|;&HNsy=xiv`l)7Kt7)ax+cnzN^d%?L zVJA-ev#xfCezMzvfl*y{YLQ!l**OdF71rAX6)Y}r@^z5lQ4*N9eR={!>Ez#;2~z3w zCvB#qf_fJ*HnOG&(EF-XXNpkvYU|vrml;XOj^vGETKr*ss8}jFB4<9)v6&gWtw6ve%EPgMh-?dCxh_1D2?|S+1`+4a6jIElJJ3_^@ z=wj}Zdws%E6G;O`;TdTTa}v#!uNKUJPzfx&i<{=63QWllit!A5bk+MYnGipaEn_(_ z6Ws)BDD3+JsecRhu!6R z)V!Udf%H;#a&y>BIwN2puIz{^{l>jp)u6h{0`0aI#L=qn(>MI6DpV$CNB{>en2F%D zyh-|6!coLG&AC3ThH=cewv_d)YFva;L?J_-dl@A;phpYX^Ud2ZtjivSebfAqkZX2h zD@NRT%iVJD>7p{*f})@UQ20)q3|Q5Lj&q-IoXQ7sH4|yH?6*;auAn&Ry%lp@d}*=P z7`rW7dSBp!NGP91dFpP@&8(wOSXl+Sch#GW`7raVG%{%zU|%#P=zz^o2#K%%~o-nuJ;ZHNYBI!yXx#9BY_QUR#wT zpL`Pp-OXUo#dV&j;wyH=mSZuF&yHn%%)As8<%y$U+! zFVajf)(kj(A;g6%uC-S4_WExPkfu8cqMzl>ebFJ4qNlPfjU`X4q4zD!6oG&FX~GON z3y-C>M8^*AAX9Pr`Q&>K$f}RKctL$tU-3_@xv2)yz7+0?C2;d9!}`FH1E~2p40Eml z7$?_HX%Avk1X$-fYD4A|Em|g|NmvOgp2%p~5P(IO26wqP*9~K&^eb{6NIXaQO^Vj9_pyidmov+W(+}A1@X__Dz3*sHc zP{0|%o(>6fhIP5qsvd5+9E@z|{1@`Dn1Q%?>}y0iKUhy-ptn~%Q$fOBWA9ME(R)=x zG`(zNrAPO_Y04GlR`YaTV#@QO{(cml1^2?a1gb7%1niZpx_j0Rwr%>qkQwyd559Ua z=|tbGVca2yl<0k3E>|xpY}vf&L}A0TpacSp|7Cd@O!3FZ1m2#!pIM)h19;KHx$iD* zaU0!ad-Lvp^hF4dDnNcdVrb_MwT1%a?{#}2}V4NyXZ3Yy(|46bW~9@PlB2VzkF zqUAfD4s_B8%XWZ^1`z7pk{c2;e1x1Jq2lcsn!a&sel_qXKhthN(R77sX68H1OLr((J5uj<5h2udfctAuw&<3SIe!MKq!3NK8BDg+J6o}QE?`2;R?~P6V zLNIXejpAU zKC=nVTLs-rDx~M@MR}nRN0)*p)(Ca(D_r_j%S31!#0~6{rP(F~7PI&5%SNMTunDpL zB`ct8B%o{~k3rdDhh!SDX_u&4)SGQXRDw;aekYwGz)2@72d}(#&?Da7)iK`$-LWjC z9ctzGcW>W!N;(6po}8MH|1v)I2Z)s7GUok{Y+zxhS)R<`Y>YmB%&g5eeh?24oIm%z zOqOTz$O&mdus4@Sb&3QKZi_nS8d}LEhp81&@v28nt}cQ>ky^PwE`?ElR>?p{l{!?a zKB5Uzny*Uq33Rns><+C{u#r$&mQS_r0rcn(P;p%wXVV`nq>3k0asIH5m`*he4c~aDN7wS(TD@d9e$UjSk6{ zqs#4UokZn<*{uFZMewqI1_Ql~MnE&y0@&FH^Cgh_VRTrYU+4E%CK3z{Rul~DLZA=- z3IxMh`K6$(*WLUEW_P;AO`)rwih5j(LpYTfTeh7eVLJG>l(renyph7ivU0 z{&QuM=&qb83V7$E^Ae6xyQG_MnQ?&Z` z45n!es!s6;;%SuiOG=(KnNWYf8U&W$wucjDZ+R8;OIlChpbc|Y81}m8Q}USJiEUgu zBdD;CmsyhGl3ANA!Y6V-#UkMci^fjg-tnKjpkwNd%%^G?iw7n{j4vG=%pfq=zrRyI zQ9oMPdnjtLG|AWh z_p!x|2StP34*l4pt{jl*rLE{&)uWdL70mmZ_s{Tr@EHu zHNrVJcM`7VyabAW_jo21rl#*EgCOTaXE%@Xb&8H}4mVG@Ef)5H&H2w?D4#!YU0#F^ zBfvc`G$zPOh~D(mCUOXDTp=qJo^%kl0em(4no#A?l6 z8_GUpr zHvDJ?2oc_@9+$I-wWy9m{IwP`zoIxhkq)fw$IUE*d4AX#w7pUI4vh6OB$r%R(*H8R` zLsb9ZpS}FE?DKk+)MaRl@ZH$l^|_Ys4C~D(E2X9DQBO$qXB!!Ct)*ZQP}GO*#93Rl zt7Pml?ChmPibQp(Z{N#nMP&#*w$P9~DY`J;p*McyT@k^8=3v}BVg?HYFHFMJ$LkFk zRlXzGk(KNYORZ6y8`TX?EXmGe@Ep8s5&RK>Tp|c)Xdh`w&#&q^yYz2~|H|jG;TBDq zr{NN86*$<3Hy7SyB*F!Ofi>*8gSo7t$IT-1YFm5Jp`!UPB_$pkV)Sao$ZTy$|Mt0m z){#IphNhYT>b)a@NN)&tA}L^P!OU^HwcK^j|9C zt$OoRQOGTIIAc$vBFRl{Hj}jD5<3ENP$AcLjgI`K=7?L$c^majE@OO3VAe%_?DsS; z{Vc&DYVFA#Mlu4`#Ox9~?&?yyfIlrji8aBCIMLUe5n#=y1cpv#l==GWD&-xknIn?|@Iw4J05@jlcq&<#%{XK!VPquAw;0WV z4*rN1>DjO?#M?F|Mr~oM7GB81I@p^e7w+&ldKrY)o+ZtYNPpX#{s_mUQFNp<$o|v3 zo=@gZGC~fv0p!Cd=>EpVte7|1kk=PSe|&)Fpm^_dGpk-Ct43c{yRRJF%)Oi~@WHs* zZ?THVC$1O^lYg*+x>-okXt0%Syg$X8ykZ1cYUD4h0&w@8TU-A8L%L%7VQokZ=X~m6 zSEVaK_w9<;O9X1aMgb4{=G+-@cS#h(Otq>Ho#cj*T&9h!0>>@ea!8t=(dxv7_V*9| z?G+8C9W9KmtwOfC)9vkNt9r4axRT6cp8ahbm7(8FUu!;Bkp|r?RPW4Gg1alW+Hw?^0inGI8z#KIQFNXedBCaYjuw@mS>f1uxddn?znOVdJ>8@Fr|;UsQbf$s z>KWF?S{<0_KlWY%d#tSmX)6d^ zaF1R#e0YlAo7-HdevE=iqbW_OYU40r>$QuPH&&+oSxov=)MmDJ)=* zTI9!cxX=&hI9kxKG{J{Mc9evEW0FF2uB&RkL~!#a0McwJGfAyn8;2`hzdh7_`sd9$ z$G;sOb&2ePM+OIvN{&bDbX%%6SJ2 z!exSDldq#WXl4~)Q5-1b>&{untzN8gepFKcF7CGd4GXP3w{5+7Ys3X6kP^R`E+?$V3;i8vorYZmhpPn7Q0ZV^hHJ(2JF&s-}pzM|vR0PZT~wW51w4g~cY#wgME>}sE`Qz7LXOL0@SnVutU8?Bk|B)N&kwY8S` zqE2rm>^D%r#2>taY{gmD`cGc%+A-2qHE0=oens@oRt@06mA{H*c&rhC8+WRfgZARD z<-yFCrJ2u1e^$&p8Bre4`wh zN*Mf?Pw(*;k63kfDxa0COmI^Ii(h`2-cK;$!0o^_Te`f|8jVUz)nn(M?a`=i5svrMsoTvN7HaTJB@Y}!j zzQoM5K`z|>@LB3$6~Wuc;e_;48e1zTkEYk7cj_-LlzFO$v9RX~tO|5PnJ6peeAX26 z%xp7UbcdAtA!18v`jjE}b`AMl`76I$5l*Ifb+cAL2yxx2w}+8URHvnJvJ>PIySO;T zNZe8BG(6>Be$GCeZBl1b6aH~0n&0IPb&+>r(f6lz>6XBeIy7Ry^=EVaoJHq=JSP0U&GNrQXsDKcmJEex2x4yMC)wKTNe4jUl%^@k1@oi2~-j}Y2C8au1j>yfWUbj zCn699Fk|LM&N1z(=!_s$sCkpql|Nqd0F>xQ6MyW0`z!x-4^z5wy$N7nyF76tNEfWl zO8V-Lx?*}iC1Z?>jKRxvdHaYR$v5mL(WzJ_3cKH(tyO|R5Lcad@3|T*pZ#L9u#Q`? zY83p{STYJ`3GQbt@3Q5*{A&j}23r8{kz%e~Bdc!xZTQ{_c&<3+`>SmLyLc9W8$zuf zescVbbw`bY-GVuzeg^4>=(L__O0D$$fQlm#AXucMWYu0`2Fv;f9S~<+veCM5fNsy& zDc2}H&pkwE%WLrOg-kcR5+#5;Z^5(Z#^~756oO{p;n(Hv3hnQI2EFOR)O=qu7VP2c z0Gs2?z?Q}(D$^S{KGm`BPxBak34SIFJhlJ^$)Ix^cXS;=!8iOx<@>Sm*X3ZqlY%@! zY+7D0`#}mKWjuH!VibF%BC?|c#dU&r8+PQr-{YZRmrfeb%w8Dy3VG%4e7z6~1eBKw zIMudkHkRk(X+TJCv|7~06gaZzGF0(-^gA+pJg1dnt9PAdeXTMM9RgQs@>3ne z!#UdD*^v2HsLU~;i+-N(ai3@)7y@u@rNLi{95>D1(~tvdOO{(BFGhs(#cw#cr`CQH zdPChsV!^Ed>!_TVII~n^E3+rK6Wq3N3^p`ufeV;4_YaTw4=nSNEH<2J5Hz+ z9d&6A_{)!hr49eWptL7?Yw?zK)S0K*-Q_OO0%3j8!D-W872$4OHlUR?9jjU5P-X*P zrbGl{ijpwKA-L*Ct+DyAAtE4!Mlt&c3Zm%@FZ%YJUDCCy4gD}B?&)ZJ^Bd^j(4R`u zyM~(qLlfImttR(~2K={T%6HO^^&j57o#>?f$7TU7@#Vzy&_0+KN0GyT8Bv~m6*f8#M_fo(fY(YOtnN!&>2Ka+OLYC4u{u$^(r3goxz)!KJ)rc;?_ERR zIt@5gCW=Ij`b&_pzs9k}4?=YIIha)0VQfRR7r#C8Va~c|Gg`rqk(mbNeP%bqTJWnZ zlOxiyL+(=5Q&9c7(T}XlR*9nAJCzJ!+?Qv%+c$ZSUD>sCpBn5-sVe0g3QWHLfe-Gw z8t!~{wd!tD>xLaGFjq!o&p0pP7Y3dNLBUJKbF!)k-NqXWca)xit!~+wIz?7)0dnDi zUuBNf*KUto3=8XRG`W!(j?M0*DNfQbx|Zo5XHkoSx5KG0TXd_Y40t>s<_$LMS!KaC zzZp9;)uskZoD}kiFWLu}MGtbkwOxQrsQBJJ*Qpq?kjze=p5 z8LEJ6pD1YP*C>L<#O&Fc)bM@lOEu5s)Ps?{>jyQA0j@>a@b(=4IG0Gq-hTSXj;IEd zUoB|DsFIfH?p5KG1|SeGbi3(-bJ4<`GUJ{rg$s*zs-*Uem;UdY@^}QT24@JHV)-l% ze;XrK_@$O>i>Wt6v2OWG+dPlH?Elxk_j17nE&plc7qh!ETKQeQp>c=*5<#0hlCYc6 zJ$-llD6g~v7JaKL&UjwI+%RO3!26Ksp_V%zgG-9}sD}s+AS%mE1xKTr_9_Riu{Uo# z4A<(<qY5R(~tciLd_>@_QkEGT@#+$^q{;i)p zLkBc7Z^?xI%$?+bEybUC_|ze0mUk^zHKi`y_TwnKD#@+HvA6WWmo~lMpZL{fKKnGz zAZ&(5@iYfr__g`98wsVAuxQMJtWRt(%ldT?!oNEN*pJ(;{`+e?PC5TvmL6j9bu6z# z1PgW6d#_6U?TN_0CL^rnvBm{m{y1z)TTOdN}^+5tQURk6!}+;IdjuDz)pvGsL;4 zi2kX?1#u2INUaH~GOp!%46#JimlguXxhwthS?RfcdWvhekwr-i6iZ#n=8U8O!u6*Z z4;o+-XtddId)W299wfV2jh=J(OEoB+Fv=W&eqkL!GI-sb$-@vmjU+a8S?RiFqQh=_ zq0l#Btc<^`lpfQU?oC2~JlU~Hj-qGM0`uQ}2-JCq1FV3P)R_ir!#SOrCtc5N>MLs! z<)+L8E+Xr)9IQH;--(hfv z4ZW2x9f5*SAy;GO7lTQcIj-I2mv?C4EbP4-a#4xYfnR&g{*CZw%m((TI>Dk=&y2&9 zqcL#2y_Lt_BuC;S*r<)104cIxuL)h**5@eVpX@19gI!zBqJDxZ-Kw;A-4hCKNjqjc zJ&iGG(frXNP%}-1I=cs-8Fb4h8Yf5DQ+Pu4svJgH5Zb-sx{U>udwLGvdxfW{gLM9| zn#akH!9iltOTQs&P}84d`*&F7;D?J%f0DUBRi4*V7U!=6gKC!Lr+BXTaxVOuHrCTLxWg?N$*&3*+%M)SC zps}ovML4w$6n78l?g0ou=bTi@HeqYvs$iWZ?Pla}msp=_s(JDU=SqR*hsD!{`(!ai zhv9J!N-zJBwyM<=%5c9(#S{DVW4bK8okF)S@yic~HIE8sy|+@Z4xugQU`|)EfyM1| zqdzx2C94JRL0B;p2zZ3c5aOeN0SBx1Q8W9RqeS`HpaL=-%Wp7}9!pSMR`GQrkNw7W zEY2su35&&dCWD6E2U)D#s zoIL68h4TN8K>;EO^0Tiu*TX4E50yGgiKq9%ZB=kGks(*yeb%KAJ06Y@-ovpeVYv{v z2&VQBfHHro0CAS2&%xS_7gAblc5>{zePy%Cd)uhx1Lp}VGJNye2}Uw3@&0lbt5(YV zZ?dZ2aRr;1IV8<@OMnqf!>M z+=E@(gS3(bDk$HiJmtFpGh094%0h8(67+UocH?%hh0G+lQMFh3efp({8yqt;zIp%K zc2zDl`=iUqW)rjII(>lcoL~4&1&@aCutq(^jEQ%8gtjc^(hRmCT}b2lIJK07^zryc zoX1CU)_CkeSzyWcrLB+iXvJQicJDL$_gA##(#xMKgVxXyC=Ds0CRlJMLsSbLNk~o4 zFKch#qigun^Q>A?9FpwG>XoT=v1<_wB{gkB`Y`#HtRN9|)}P#k?XdL1r0Q$~2k*D5 zd>BQ5u1L$c*+5XlaTmRvo$+|<=*(P-R;^l?{gk~RgM=n$o~#gWcyS$>j{FA2IG;)i zsVhTsi*@D|=m2jFmxiN7*8x2S(2>|1@!aFLeuz2re|Aq~OcU6?aD*HBO=w}x_}uS@ z;(H07DM)Q&^?;3N8*I+86SvpU@Vz~rgVd5{jxj7p<*i*d7Q}$7kd#Bh^rtyjRFv~H zJu!=lTwD!x<@+SL)~{6egA~vBr6k$*?Q`~o(mh~Mzzoeh6h&&0%9PdQ$jigsoAL@+ zGNGAYvkNy80YrfKxT)Oz&2HVeE*OAKSsr$!GsWlr{UBdSmhrmwF^|-%)-vb&A=<_V zFHX>w>yFRWU{!6-MN*k5@qVphyGvN3{P&;@^z;55Xb<%Ed2feufVMR4(g;E{2?;0y@PLO_@7C?8h1iY9t=Ks-?`Hj7kKv?LP>u+F z>blPF%o)-YFe|I!9=HQ^BmB&w?ZK;)RN%mpaoz4Vw#jdq`>d+!S`7Zqf+zkqZL6Rs!(y7w(O<<(K-J&sedNP ziAo5SmdF3QRzD&T%W}CpN``&rTtn^udq)pJF+y$7$HQ}c*s!OMC4Uwwytx97K|K(+ z<;eiqZ%KrZeLV!L2FLjE@I@GsK}awyuSjJ}O3nD>V6>z;=zK3FN z2cP&-!s>HrT^W3Ir1BEn{#^jfn)49&CHE7cnR0bxDVIcsMO)}#?iy@2$K_^@3t@(u zSh_$d9A2x`4Y^(Md1j;<+=;Ak1m<8?lcX~Q8@nf*Blb@>L|SWou_@65=L36wh4?%Rnv|c;);y|tU{H@n@vy?2Rp@R$ zk?*INgRT{8RBdPW8er+U;efj{JY$vf46t8ri*qi<)KDc48o2GZ$+Y;8fe0muugmq2 z#Gs(y0L`x~4lkY9s`y;(EUw#zOPy2X5)&fWZoHznNl+GJfbZO{ul{LgCL0ds9bf3Tfzrd0_Z}LkR%9`V@VS}9DX=n88%CuCFMQ1(N7E8RiFc1D^`qrkJ@uvVBvYus~$R}r|LXd zuzWBgrYTD*5VOmmMrnH-`@l@e#yA7Q-U9)CNDU1rJhU)>^_&!J_}7pPnT%pN?1nNM zGs6F)v+*XJ=YLQ#2h-*pGacAx!YwTAxLVve@%GT)I8cTS8eo-FpEH4kBTRP6?C}vZ zG?_W{!Y@Z4(L{KR_E;pGKJ_6Z%S7Z7^6)D%Ca_^rNC2X3fuM(BZoLn_Yf;gf!x6bD z*1_y3K}3&4PKq>l@9C7oM9ia2z!9LrqAbXSXj|Ag zYKsG-MJOp{8sF37g~iIT#7%yj7Q7$14p4PrzpCYh#PJl{su!)DqCM_^h>8DmjM(66 z7W{qLev%N(&_{R^q5(L1YVphi<&UUiM`#``fWTFd%%QWCldmSibpe$_2F1*6pw)HC zi%Z$8j5=nAwi6&>z(B(Gk^;uF_C_9&UpOWOJJ&MI99k)`F;D|qNz1zy z0U=1r0suie7(uo$LQ5CmF8knY4Y`}7y}Ndkm=F9pc#{21(c}I0)`B~Qpf25pzUuf5 zoDOq9b0baF3WpidBv!c}|C(^wuzt9a>HfQ?wcxwW+I3}{cD%Sck@<`UZ7|o6U>@d{ zoLadwi+wkHduTLAq;>WFXD1b3yO0GO)OWnzOP}ZG*@ogBlv?WFZ77xcuV`szrgi|sQ~67I1IO}fR!$u*9-UVAYi3!XCBmMwfsZV zv}B2a>MC?w#qE|yI2t$QnDmxF+Dx(&tv3P%t3urdgWPKbuXyqMpxqBbqnh*6 zFgInc`w>&EZt$%B$k8QMOnjKHc=l z_M!#Ft}Y0`B>$L3qP81F-et}6c6R=QESJM!7E#MXK7t7~(_yX31%h5&zxH_h(NY_<<>whe*QA;Qki_Ph zhDZhZa9T2*2KF6k_bZ00PK?$}_IA~xBocJ6+smnv)j zM-vUl9+Vm2zi?=$Npq#)#DQ9tL{5vbSq5Ke)DZ*Vrd&@`L;2@c#9KV)$x{!zV0WRL ziy3^DhOB{Kp5m@&Dpr6r!4rKR^lD@V0X1yl2k?)3*f~4ViSaAoIrCX9w47MSxr%Ou zo_m=`VKKlrNlb}ou$8mP6JR$IejX5j)KT*pq)&q<4I#}3WW>$Pz$ppoS*->&)YGtb zJUfh)vg?ucZtY$x#C&S;ELRQ=&P!(Lf^(>pfgv&^9IVUjF+m6C4`t zyq$bwdE0|OgVJ(@?k8P=S#>mwHAwyHJ5Q!Vxx$M05P6ShmHTshP!ui zfaJ(<#1OcURepAKX*+b_ajX!PZmh~Ad}2~_kMdM`uCt1LF+)uC`VgvArk-D%`>pPw zl-6XP;0#n)x4ly1Av0tO#oJ!RjWh$jHzDdZh3mBByIBJFRupO6+7k*J&m8*z8nh-5 zP>a0L*%!HFf zNE9zX0>S;;4U^1A;G4NS^O{0A;UGOdxS@rZ4)#eYL4c2>1}V)k4!JNWaWzlR(fJ&5 z*@i5*?p<*rdA?u0WyA$$FNtvNl04RW1_ta~cILc(*Jq*D`KTVD?GMKUgR2{41?kaPxhl^@WaoQVODFBYf|xv zkRqW=pe8~aJ4D6za?}P(oK{FZuZWG%Ut-!@kp1cE10BcH*s!|X)%vjgCr1tCWn-8-xiGth;nuuJwCHP zdzvhfmVt{NZYJlZ@1KxEx`#_cm<=i{)uq9TOYoaD71YyPZkH_EY5~O$(ikjEwEjnH zG@1|r2K-o1BlW~QJ)q=!GPN^tby=XM~r_vKX=v}Pw6l9U%^)~+H&LGoBrbFEAGQ-0m zZN$94W)=4TW}`dt`P##I=>}RdKL}q;81k4RO+Q%FG_Dp3Lh_S^YqO>uTZ$tQS9qBM z?yiR)YqVX|177|MB}KTe{9qE0+~o?v)dpx921B19`4TzXDk1j+(a=7;PsOUVkvL>! zVER#_AE6T(7W&JM`LD3Ag(wiNrXMV8;&MoCK!Z>R2=Mw5%icBJxX3;Ygbyo4oBMVg zCn5DFNiDuU1(jiMU;J8&rePTy!t$f5uT zp$Cv=wfa(upOTzWDC7mlAWTOF-NvC+BiakH7M%@1!#7GKJ^Y^q0mR4SIje@-6o$Mr z3vdfRZY6NCg5fd}QR+&8wTn-|iC`U(o^wG>FOj4%_Mh1SM32%YgHa%MSlFkP+OmAK z04YZT2~v}u1>M2eR0n7mVJpG|eTN)PE(AVwvr2KL&f{PzAUG{o5c@9m6jDSgFp9N(=ET3e59EF-rN07XaxV(PzkPe z!zVMMG?<;X=cm39Vo3zLi*cgA^1=D=&jmG$M3e~Fc|gJRlPc-qBU~;>Yj2>hid^mA zSxEZt4Y|aH47_>9<}e*M^~Y}0#s7dPBVg50qEk;WSu|qB|sBRmg@1j5FSH) z|1prk?VcOT+zOXW!1MM|fG}Dl%#<~m1K0;vLXW6iK#ZEDETKdzRP;_QaHk}@R5GPT z8$*O;^{AxcL|prJQ0j{$BS?`sc}*t|oh2g@Y83K@EmBOOZ~j0uvu}qqf<+lXf46vn zM1EnVwPk|EQ|6rutQs~xn3wG#%@h*vp33v)Q<3|ZRvSk?7QK|mCZX0a;Ik4koc;|} zmfB```u>;>@}kl&HAg`t2ZAcknPB7Bu&k~|%+11)oeK4q)5Fd_R#^Qb%GI|HpBZI2 ze<~;JWsPLQL{_PfFjwew;PstWg5}a?C41lvHw8_hVOc%oN9+dQrV>+EzS5qJS9$!c z&B~0lq}U4f3kW5~4xJz+d!Wl4{FQe3Z2x$hkzc*_hRHFgdcXsHoQ!*nQWV*&j5^Fm zrhWy>As9n7BE06W)9I8h(oiszOsP^*GQ45yefBkVF_l;3{PG^E@(W1$!2-bq83&5* zQDD?li$p5E(%(p#6b&-;&eXJ&DmI5~gR!$vzkR5kjr78704h5Djc3+BLT~wH$MO-I zgO8(5XDz>j%)4gGR)s`f!+cK6*H0cG<;eATG}_IdFD7}tMoFJ4Mc?3LH7$Od>+BLc zXWp*+dDqG1->a0xs5U<0%;mp3rvr`DL?Sc5`sh?WD%7|co3SloZY;oLMrzXBTsakj z*3l+^#62#(MQDhsk24axdg;k>IHosb*FEEY$ms|co008(Y=;@r=hx5M-58}9TxPZ( zYGep}ooS0c=~>x?C*Y$_C7gwh^YX6KQgYIDL}3W24Q}G>(k!H=?|-h{POe2&dK#ZP zCz4Qp)|M6dC3dxJa_IakMkb}WVf1htN~Sj#l~GG#90y|IK;(O#g2JBT<#|d4#)HExO0T)xHu^+joNzIg4YCgW81#@B { browserHistory.push('/home') }} > -
G
+
{this.props.expanded ?
GTFS Data
Manager
: null From acebe9cc255f4a5135819dc9bbded4ca6557e715 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Sat, 8 Oct 2016 20:14:18 -0400 Subject: [PATCH 003/323] first pass at reporter ui --- src/main/client/common/components/Loading.js | 17 ++ src/main/client/gtfs/actions/feed.js | 10 + .../gtfs/actions/{gtfsFilter.js => filter.js} | 11 +- src/main/client/gtfs/actions/patterns.js | 47 ++++ src/main/client/gtfs/actions/routes.js | 29 ++ src/main/client/gtfs/actions/stops.js | 104 ++++++++ .../gtfs/containers/GlobalGtfsFilter.js | 10 +- src/main/client/gtfs/reducers/feed.js | 70 +++++ .../reducers/{gtfsFilter.js => filter.js} | 20 +- src/main/client/gtfs/reducers/index.js | 18 +- src/main/client/gtfs/reducers/patterns.js | 73 +++++ src/main/client/gtfs/reducers/routes.js | 58 ++++ src/main/client/gtfs/reducers/stops.js | 86 ++++++ src/main/client/gtfs/util/graphql.js | 130 +++++++++ src/main/client/main.js | 23 +- .../manager/components/FeedVersionReport.js | 194 ++++++++++++++ .../manager/components/FeedVersionViewer.js | 252 ++++++++---------- .../reporter/components/FeedLayout.js | 46 ++++ .../reporter/components/PageLayout.js | 42 +++ .../reporter/components/PatternLayout.js | 83 ++++++ .../reporter/components/RouteLayout.js | 72 +++++ .../reporter/components/StopLayout.js | 127 +++++++++ .../components/reporter/containers/Feed.js | 31 +++ .../reporter/containers/Patterns.js | 43 +++ .../components/reporter/containers/Routes.js | 36 +++ .../components/reporter/containers/Stops.js | 51 ++++ 26 files changed, 1511 insertions(+), 172 deletions(-) create mode 100644 src/main/client/common/components/Loading.js create mode 100644 src/main/client/gtfs/actions/feed.js rename src/main/client/gtfs/actions/{gtfsFilter.js => filter.js} (89%) create mode 100644 src/main/client/gtfs/actions/patterns.js create mode 100644 src/main/client/gtfs/actions/routes.js create mode 100644 src/main/client/gtfs/actions/stops.js create mode 100644 src/main/client/gtfs/reducers/feed.js rename src/main/client/gtfs/reducers/{gtfsFilter.js => filter.js} (80%) create mode 100644 src/main/client/gtfs/reducers/patterns.js create mode 100644 src/main/client/gtfs/reducers/routes.js create mode 100644 src/main/client/gtfs/reducers/stops.js create mode 100644 src/main/client/gtfs/util/graphql.js create mode 100644 src/main/client/manager/components/FeedVersionReport.js create mode 100644 src/main/client/manager/components/reporter/components/FeedLayout.js create mode 100644 src/main/client/manager/components/reporter/components/PageLayout.js create mode 100644 src/main/client/manager/components/reporter/components/PatternLayout.js create mode 100644 src/main/client/manager/components/reporter/components/RouteLayout.js create mode 100644 src/main/client/manager/components/reporter/components/StopLayout.js create mode 100644 src/main/client/manager/components/reporter/containers/Feed.js create mode 100644 src/main/client/manager/components/reporter/containers/Patterns.js create mode 100644 src/main/client/manager/components/reporter/containers/Routes.js create mode 100644 src/main/client/manager/components/reporter/containers/Stops.js diff --git a/src/main/client/common/components/Loading.js b/src/main/client/common/components/Loading.js new file mode 100644 index 000000000..a8f122270 --- /dev/null +++ b/src/main/client/common/components/Loading.js @@ -0,0 +1,17 @@ +import React from 'react' +import { Row, Col } from 'react-bootstrap' +import Icon from 'react-fa' + + +export default class Loading extends React.Component { + + render () { + return ( + + +

+ +
+ ) + } +} diff --git a/src/main/client/gtfs/actions/feed.js b/src/main/client/gtfs/actions/feed.js new file mode 100644 index 000000000..017c0379a --- /dev/null +++ b/src/main/client/gtfs/actions/feed.js @@ -0,0 +1,10 @@ +// import axios from 'axios' + +import { compose, feed } from '../../gtfs/util/graphql' + +export function fetchFeed (feedId) { + return { + type: 'FETCH_GRAPHQL_FEED', + // payload: axios.get(compose(feed, { feedId: feedId })) + } +} diff --git a/src/main/client/gtfs/actions/gtfsFilter.js b/src/main/client/gtfs/actions/filter.js similarity index 89% rename from src/main/client/gtfs/actions/gtfsFilter.js rename to src/main/client/gtfs/actions/filter.js index c114caa0b..0731b4444 100644 --- a/src/main/client/gtfs/actions/gtfsFilter.js +++ b/src/main/client/gtfs/actions/filter.js @@ -41,13 +41,20 @@ export function updateGtfsFilter (activeProject, user) { } } -export const setPermissionFilter = (permission) => { +export const updatePermissionFilter = (permission) => { return { - type: 'SET_GTFS_PERMISSION_FILTER', + type: 'UPDATE_GTFS_PERMISSION_FILTER', permission } } +export const updateDateTimeFilter = (props) => { + return { + type: 'UPDATE_GTFS_DATETIME_FILTER', + props + } +} + export const addActiveFeed = (feed) => { return { type: 'ADD_ACTIVE_FEED', diff --git a/src/main/client/gtfs/actions/patterns.js b/src/main/client/gtfs/actions/patterns.js new file mode 100644 index 000000000..7b95f6b0d --- /dev/null +++ b/src/main/client/gtfs/actions/patterns.js @@ -0,0 +1,47 @@ +import fetch from 'isomorphic-fetch' + +import { compose, patterns } from '../../gtfs/util/graphql' + +function fetchingPatterns (feedId, routeId) { + return { + type: 'FETCH_GRAPHQL_PATTERNS', + feedId, + routeId + } +} + +function receivePatterns (feedId, data) { + return { + type: 'FETCH_GRAPHQL_PATTERNS_FULFILLED', + data + } +} + + +export function fetchPatterns (feedId, routeId) { + return function (dispatch, getState) { + dispatch(fetchingPatterns(feedId, routeId)) + return fetch(compose(patterns, {feedId, routeId})) + .then((response) => { + return response.json() + }) + .then(json => { + dispatch(receivePatterns(feedId, json)) + }) + } +} + +export function updateRouteFilter (routeId) { + return { + type: 'PATTERN_ROUTE_FILTER_CHANGE', + payload: routeId + } +} + +export function patternRouteFilterChange (feedId, routeData) { + return function (dispatch) { + const newRouteId = (routeData && routeData.route_id) ? routeData.route_id : null + dispatch(updateRouteFilter(newRouteId)) + dispatch(fetchPatterns(feedId, newRouteId)) + } +} diff --git a/src/main/client/gtfs/actions/routes.js b/src/main/client/gtfs/actions/routes.js new file mode 100644 index 000000000..868288e3a --- /dev/null +++ b/src/main/client/gtfs/actions/routes.js @@ -0,0 +1,29 @@ +import fetch from 'isomorphic-fetch' +import { compose, routes } from '../../gtfs/util/graphql' + +function fetchingRoutes (feedId) { + return { + type: 'FETCH_GRAPHQL_ROUTES', + feedId + } +} + +function receiveRoutes (feedId, data) { + return { + type: 'FETCH_GRAPHQL_ROUTES_FULFILLED', + data + } +} + +export function fetchRoutes (feedId) { + return function (dispatch, getState) { + dispatch(fetchingRoutes()) + return fetch(compose(routes, { feedId: feedId })) + .then((response) => { + return response.json() + }) + .then(json => { + dispatch(receiveRoutes(feedId, json)) + }) + } +} diff --git a/src/main/client/gtfs/actions/stops.js b/src/main/client/gtfs/actions/stops.js new file mode 100644 index 000000000..77271cc7a --- /dev/null +++ b/src/main/client/gtfs/actions/stops.js @@ -0,0 +1,104 @@ +import fetch from 'isomorphic-fetch' + +import { compose, stopsFiltered } from '../../gtfs/util/graphql' +import { updateDateTimeFilter } from './filter' +import { fetchPatterns } from './patterns' + +function fetchingStops (feedId, routeId, patternId, date, from, to) { + return { + type: 'FETCH_GRAPHQL_STOPS', + feedId, + routeId, + patternId, date, from, to + } +} + +function receiveStops (feedId, routeId, patternId, data) { + return { + type: 'FETCH_GRAPHQL_STOPS_FULFILLED', + feedId, + routeId, + patternId, + data + } +} + +export function fetchStopsWithFilters (feedId) { + return function (dispatch, getState) { + const state = getState() + const {date, from, to} = state.gtfs.filter.dateTimeFilter + const {routeFilter, patternFilter} = state.gtfs.stops + dispatch(fetchingStops(feedId, routeFilter, patternFilter, date, from, to)) + return fetch(compose(stopsFiltered(feedId, routeFilter, patternFilter, date, from, to), {feedId, routeFilter, patternFilter, date, from, to})) + .then((response) => { + return response.json() + }) + .then(json => { + dispatch(receiveStops(feedId, routeFilter, patternFilter, json)) + }) + } +} + +export function fetchStops (feedId, routeId, patternId, date, from, to) { + return function (dispatch, getState) { + dispatch(fetchingStops(feedId, routeId, patternId, date, from, to)) + return fetch(compose(stopsFiltered(feedId, routeId, patternId, date, from, to), {feedId, routeId, patternId, date, from, to})) + .then((response) => { + return response.json() + }) + .then(json => { + dispatch(receiveStops(feedId, routeId, patternId, json)) + }) + } +} + +export function updateStopRouteFilter (routeId) { + return { + type: 'STOP_ROUTE_FILTER_CHANGE', + payload: routeId + } +} + +export function updateStopPatternFilter (patternId) { + return { + type: 'STOP_PATTERN_FILTER_CHANGE', + payload: patternId + } +} + +export function stopDateTimeFilterChange (feedId, props) { + return function (dispatch, getState) { + dispatch(updateDateTimeFilter(props)) + dispatch(fetchStopsWithFilters(feedId)) + } +} + +export function stopPatternFilterChange (feedId, patternData) { + return function (dispatch, getState) { + const state = getState() + const {date, from, to} = state.gtfs.filter.dateTimeFilter + const patternId = (patternData && patternData.pattern_id) ? patternData.pattern_id : null + const routeId = (patternData && patternData.route_id) ? patternData.route_id : null + dispatch(updateStopPatternFilter(patternId)) + + const routeFilter = state.gtfs.stops.routeFilter + if (!routeFilter && patternId) { + dispatch(updateStopRouteFilter(routeId)) + } + if (patternId) { + dispatch(fetchStops(feedId, null, patternId, date, from, to)) + } else if (routeFilter) { // fetch stops for route if route filter set and pattern filter set to null + dispatch(fetchStops(feedId, routeFilter, null, date, from, to)) + } + } +} + +export function stopRouteFilterChange (feedId, routeData) { + return function (dispatch, getState) { + const routeId = (routeData && routeData.route_id) ? routeData.route_id : null + dispatch(updateStopRouteFilter(routeId)) + dispatch(updateStopPatternFilter(null)) + dispatch(fetchPatterns(feedId, routeId, null)) + dispatch(fetchStopsWithFilters(feedId)) + } +} diff --git a/src/main/client/gtfs/containers/GlobalGtfsFilter.js b/src/main/client/gtfs/containers/GlobalGtfsFilter.js index e47bd7355..42ffd2cd1 100644 --- a/src/main/client/gtfs/containers/GlobalGtfsFilter.js +++ b/src/main/client/gtfs/containers/GlobalGtfsFilter.js @@ -4,13 +4,13 @@ import { connect } from 'react-redux' import GtfsFilter from '../components/GtfsFilter' import { addActiveFeed, removeActiveFeed, addAllActiveFeeds, - removeAllActiveFeeds, setPermissionFilter, updateGtfsFilter } from '../actions/gtfsFilter' + removeAllActiveFeeds, updatePermissionFilter, updateGtfsFilter } from '../actions/filter' const mapStateToProps = (state, ownProps) => { return { - activeFeeds: state.gtfsFilter.activeFeeds, - allFeeds: state.gtfsFilter.allFeeds, - loadedFeeds: state.gtfsFilter.loadedFeeds, + activeFeeds: state.gtfs.filter.activeFeeds, + allFeeds: state.gtfs.filter.allFeeds, + loadedFeeds: state.gtfs.filter.loadedFeeds, user: state.user, project: state.projects.active } @@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { let filter = initialProps.permissionFilter || 'view-feed' - dispatch(setPermissionFilter(filter)) + dispatch(updatePermissionFilter(filter)) if (initialProps.project && initialProps.user) dispatch(updateGtfsFilter(initialProps.project, initialProps.user)) }, diff --git a/src/main/client/gtfs/reducers/feed.js b/src/main/client/gtfs/reducers/feed.js new file mode 100644 index 000000000..fc5ce29cb --- /dev/null +++ b/src/main/client/gtfs/reducers/feed.js @@ -0,0 +1,70 @@ +import update from 'react-addons-update' + + +const defaultState = { + fetchStatus: { + fetched: false, + fetching: false, + error: false + }, + data: [] +} + +const feedStatKeyDescription = { + feed_id: 'Feed Id', + feed_publisher_name: 'Publisher', + feed_publisher_url: 'Publisher URL', + feed_lang: 'Language Code', + feed_version: 'Feed Version', + route_count: 'Number of Routes in Feed', + stop_count: "Number of Stops in Feed" +} + +export default function reducer(state=defaultState, action) { + + switch (action.type) { + case 'FETCH_GRAPHQL_FEED_PENDING': + return { + fetchStatus: { + fetched: false, + fetching: true, + error: false + }, + data: [] + } + break + case 'FETCH_GRAPHQL_FEED_REJECTED': + return update(state, { + fetchStatus: { + $set: { + fetched: false, + fetching: false, + error: true + } + } + }) + break + case 'FETCH_GRAPHQL_FEED_FULFILLED': + let feedData = action.payload.data.feeds[0], + feedStats = [] + const feedKeys = Object.keys(feedData) + for (let i = 0; i < feedKeys.length; i++) { + feedStats.push({ + statName: feedStatKeyDescription[feedKeys[i]], + statValue: feedData[feedKeys[i]] + }) + } + return { + fetchStatus: { + fetched: true, + fetching: false, + error: false + }, + data: feedStats + } + break + default: + return state + } + +} diff --git a/src/main/client/gtfs/reducers/gtfsFilter.js b/src/main/client/gtfs/reducers/filter.js similarity index 80% rename from src/main/client/gtfs/reducers/gtfsFilter.js rename to src/main/client/gtfs/reducers/filter.js index f1c2adca6..c6dd28720 100644 --- a/src/main/client/gtfs/reducers/gtfsFilter.js +++ b/src/main/client/gtfs/reducers/filter.js @@ -1,16 +1,30 @@ import update from 'react-addons-update' +import moment from 'moment' const gtfsFilter = (state = { allFeeds: [], activeFeeds: [], loadedFeeds: [], - permissionFilter: 'view-feed' + permissionFilter: 'view-feed', + dateTimeFilter: { + date: moment().format('YYYY-MM-DD'), + from: 0, + to: 86399 + } }, action) => { // console.log(action) - let activeFeeds + let activeFeeds, dateTimeFilter switch (action.type) { - case 'SET_GTFS_PERMISSION_FILTER': + case 'UPDATE_GTFS_PERMISSION_FILTER': return update(state, {permissionFilter: {$set: action.permission}}) + case 'UPDATE_GTFS_DATETIME_FILTER': + dateTimeFilter = {...state.dateTimeFilter} + for (let key in action.props) { + dateTimeFilter[key] = action.props[key] + } + return update(state, { + dateTimeFilter: {$set: dateTimeFilter} + }) case 'UPDATE_GTFS_FILTER': let userFeeds = [] if (action.user.permissions.isProjectAdmin(action.activeProject.id)) { diff --git a/src/main/client/gtfs/reducers/index.js b/src/main/client/gtfs/reducers/index.js index 268aeb483..353d59d30 100644 --- a/src/main/client/gtfs/reducers/index.js +++ b/src/main/client/gtfs/reducers/index.js @@ -1 +1,17 @@ -export gtfsFilter from './gtfsFilter' +import { combineReducers } from 'redux' + +import filter from './filter' +import feed from './feed' +// import pageLayout from './pageLayout' +import patterns from './patterns' +import routes from './routes' +import stops from './stops' + +export default combineReducers({ + filter, + feed, + // pageLayout, + patterns, + routes, + stops +}) diff --git a/src/main/client/gtfs/reducers/patterns.js b/src/main/client/gtfs/reducers/patterns.js new file mode 100644 index 000000000..cd8d0ab6f --- /dev/null +++ b/src/main/client/gtfs/reducers/patterns.js @@ -0,0 +1,73 @@ +import update from 'react-addons-update' + +import { getRouteName } from '../../editor/util/gtfs' + +const defaultState = { + routeFilter: null, + fetchStatus: { + fetched: false, + fetching: false, + error: false + }, + data: [] +} + +export default function reducer (state = defaultState, action) { + switch (action.type) { + case 'FETCH_GRAPHQL_PATTERNS': + return update(state, + { + fetchStatus: { + $set: { + fetched: false, + fetching: true, + error: false + } + }, + data: {$set: []} + } + ) + case 'FETCH_GRAPHQL_PATTERNS_REJECTED': + return update(state, { + fetchStatus: { + $set: { + fetched: false, + fetching: false, + error: true + } + } + }) + case 'FETCH_GRAPHQL_PATTERNS_FULFILLED': + let allRoutes = action.data.routes + let allPatterns = [] + + for (let i = 0; i < allRoutes.length; i++) { + const curRouteId = allRoutes[i].route_id + const curRouteName = getRouteName(allRoutes[i]) + + for (let j = 0; j < allRoutes[i].patterns.length; j++) { + let curPattern = allRoutes[i].patterns[j] + curPattern.route_id = curRouteId + curPattern.route_name = curRouteName + allPatterns.push(curPattern) + } + } + + return update(state, + { + fetchStatus: { + $set: { + fetched: true, + fetching: false, + error: false + } + }, + data: {$set: allPatterns} + } + ) + case 'PATTERN_ROUTE_FILTER_CHANGE': + return update(state, {routeFilter: { $set: action.payload }}) + default: + return state + } +} diff --git a/src/main/client/gtfs/reducers/routes.js b/src/main/client/gtfs/reducers/routes.js new file mode 100644 index 000000000..dd76f3a91 --- /dev/null +++ b/src/main/client/gtfs/reducers/routes.js @@ -0,0 +1,58 @@ +import update from 'react-addons-update' + +import { getRouteName } from '../../editor/util/gtfs' + + +const defaultState = { + fetchStatus: { + fetched: false, + fetching: false, + error: false + }, + data: [] +} + +export default function reducer (state = defaultState, action) { + switch (action.type) { + case 'FETCH_GRAPHQL_ROUTES': + return { + fetchStatus: { + fetched: false, + fetching: true, + error: false + }, + data: [] + } + break + case 'FETCH_GRAPHQL_ROUTES_REJECTED': + return update(state, { + fetchStatus: { + $set: { + fetched: false, + fetching: false, + error: true + } + } + }) + break + case 'FETCH_GRAPHQL_ROUTES_FULFILLED': + let newRoutes = [] + for (let i = 0; i < action.data.routes.length; i++) { + let curRoute = action.data.routes[i] + curRoute.route_name = getRouteName(curRoute) + newRoutes.push(curRoute) + } + return { + fetchStatus: { + fetched: true, + fetching: false, + error: false + }, + data: newRoutes + } + break + default: + return state + } + +} diff --git a/src/main/client/gtfs/reducers/stops.js b/src/main/client/gtfs/reducers/stops.js new file mode 100644 index 000000000..a5a43fdd0 --- /dev/null +++ b/src/main/client/gtfs/reducers/stops.js @@ -0,0 +1,86 @@ +import update from 'react-addons-update' + +import { getRouteName } from '../../editor/util/gtfs' + +const defaultState = { + routeFilter: null, + patternFilter: null, + fetchStatus: { + fetched: false, + fetching: false, + error: false + }, + data: [] +} + +export default function reducer (state = defaultState, action) { + switch (action.type) { + case 'FETCH_GRAPHQL_STOPS': + return update(state, + { + fetchStatus: { + $set: { + fetched: false, + fetching: true, + error: false + } + }, + data: {$set: []} + } + ) + case 'FETCH_GRAPHQL_STOPS_FULFILLED': + let allRoutes = action.data.routes || [] + let allPatterns = [] + let allStops = action.data.stops || [] + + for (let i = 0; i < allRoutes.length; i++) { + let route = allRoutes[i] + const curRouteId = route.route_id + const curRouteName = getRouteName(route) + + for (let j = 0; j < route.patterns.length; j++) { + let pattern = allRoutes[i].patterns[j] + pattern.route_id = curRouteId + pattern.route_name = curRouteName + allPatterns.push(pattern) + for (let k = 0; k < pattern.stops.length; k++) { + let stop = pattern.stops[k] + allStops.push(stop) + } + } + } + + for (let i = 0; i < allStops.length; i++) { + if (allStops[i].stats) { + for (let key in allStops[i].stats) { + allStops[i][key] = allStops[i].stats[key] + } + } + if (allStops[i].transferPerformance) { + for (let key in allStops[i].transferPerformance) { + allStops[i][key] = allStops[i].transferPerformance[key] + } + } + } + + return update(state, + { + fetchStatus: { + $set: { + fetched: true, + fetching: false, + error: false + } + }, + data: {$set: allStops} + } + ) + case 'STOP_ROUTE_FILTER_CHANGE': + return update(state, {routeFilter: { $set: action.payload }}) + case 'STOP_PATTERN_FILTER_CHANGE': + return update(state, {patternFilter: { $set: action.payload }}) + default: + return state + } + +} diff --git a/src/main/client/gtfs/util/graphql.js b/src/main/client/gtfs/util/graphql.js new file mode 100644 index 000000000..4aebb3e8d --- /dev/null +++ b/src/main/client/gtfs/util/graphql.js @@ -0,0 +1,130 @@ +export function compose (query, variables) { + return `/api/manager/graphql?query=${encodeURIComponent(query)}&variables=${encodeURIComponent(JSON.stringify(variables))}` +} + +export const feed = ` +query feedQuery($feedId: [String]) { + feeds (feed_id: $feedId) { + feed_id, + feed_publisher_name, + feed_publisher_url, + feed_lang, + feed_version, + route_count, + stop_count + } +} +` + +export const routes = ` +query routeQuery($feedId: [String]) { + routes(feed_id: $feedId) { + route_id + route_short_name + route_long_name, + route_desc, + route_url, + trip_count, + pattern_count + } +} +` + +export const patterns = ` +query patternsQuery($feedId: [String], $routeId: [String]) { + routes (feed_id: $feedId, route_id: $routeId) { + route_id, + route_short_name, + route_long_name, + patterns { + pattern_id, + name, + stop_count, + trip_count + } + } +} +` + +export const stops = () => { + return ` + query allStopsQuery($feedId: [String]) { + stops(feed_id: $feedId) { + stops { + stop_id, + stop_name, + stop_name, + stop_code, + stop_desc, + stop_lon, + stop_lat, + zone_id, + stop_url, + stop_timezone + } + } + } + ` +} + +export const allStops = ` +query allStopsQuery($feedId: [String]) { + stops(feed_id: $feedId) { + stops { + stop_id, + stop_name, + stop_name, + stop_code, + stop_desc, + stop_lon, + stop_lat, + zone_id, + stop_url, + stop_timezone + } + } +} +` + +// TODO: add back in patternId filter +export const stopsFiltered = (feedId, routeId, patternId, date, from, to) => { + const hasFrom = typeof from !== 'undefined' && from !== null + const hasTo = typeof to !== 'undefined' && to !== null + let query = ` + query filteredStopsQuery( + ${feedId ? '$feedId: [String],' : ''} + ${routeId ? '$routeId: [String],' : ''} + ${patternId ? '$patternId: [String],' : ''} + ${date ? '$date: String,' : ''} + ${hasFrom ? '$from: Long,' : ''} + ${hasTo ? '$to: Long' : ''} + ) { + stops(${feedId ? 'feed_id: $feedId,' : ''} ${routeId ? 'route_id: $routeId,' : ''} ${patternId ? 'pattern_id: $patternId,' : ''}) { + stop_id, + stop_name, + stop_name, + stop_code, + stop_desc, + stop_lon, + stop_lat, + ${date && hasFrom && hasTo + ? ` + stats(date: $date, from: $from, to: $to){ + headway, + tripCount + }, +# transferPerformance(date: $date, from: $from, to: $to){ +# fromRoute, +# toRoute, +# bestCase +# }, + ` + : ''} + # zone_id, + # stop_url, + # stop_timezone + } + } + ` + return query +} diff --git a/src/main/client/main.js b/src/main/client/main.js index b8442fda3..51cb90138 100644 --- a/src/main/client/main.js +++ b/src/main/client/main.js @@ -3,20 +3,18 @@ import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import { createStore, applyMiddleware, combineReducers } from 'redux' import thunkMiddleware from 'redux-thunk' -import { browserHistory, hashHistory } from 'react-router' +import { browserHistory } from 'react-router' import { syncHistoryWithStore, routerReducer } from 'react-router-redux' // import promise from 'redux-promise' import createLogger from 'redux-logger' -import { checkExistingLogin } from './manager/actions/user' import App from './common/containers/App' - import 'react-virtualized/styles.css' import config from 'json!yaml!../../../config.yml' -if(config.modules.gtfsplus && config.modules.gtfsplus.enabled) { +if (config.modules.gtfsplus && config.modules.gtfsplus.enabled) { config.modules.gtfsplus.spec = require('json!yaml!../../../gtfsplus.yml') } config.modules.editor.spec = require('json!yaml!../../../gtfs.yml') @@ -30,14 +28,12 @@ var lang = requireAll(require.context('json!yaml!../../../i18n', true, /.yml/)) // is an array containing all the matching modules config.messages = {} config.messages.all = lang -const languageId = localStorage.getItem('lang') ? localStorage.getItem('lang') : navigator.language || navigator.userLanguage +const languageId = window.localStorage.getItem('lang') ? localStorage.getItem('lang') : navigator.language || navigator.userLanguage config.messages.active = lang.find(l => l.id === languageId) || lang.find(l => l.id === 'en-US') console.log('config', config) window.DT_CONFIG = config - - import * as managerReducers from './manager/reducers' import * as adminReducers from './admin/reducers' import * as alertsReducers from './alerts/reducers' @@ -45,7 +41,7 @@ import * as signsReducers from './signs/reducers' import * as gtfsPlusReducers from './gtfsplus/reducers' import * as editorReducers from './editor/reducers' -import * as gtfsReducers from './gtfs/reducers' +import gtfs from './gtfs/reducers' const logger = createLogger({duration: true, collapsed: true}) const store = createStore( @@ -56,20 +52,15 @@ const store = createStore( ...signsReducers, ...gtfsPlusReducers, ...editorReducers, - ...gtfsReducers, - routing: routerReducer + // ...reportReducers, + routing: routerReducer, + gtfs }), applyMiddleware(thunkMiddleware, logger) ) console.log('initial store', store.getState()) -// Every time the state changes, log it -// Note that subscribe() returns a function for unregistering the listener -let unsubscribe = store.subscribe(() => { - // console.log('store updated', store.getState()) -}) - const appHistory = syncHistoryWithStore(browserHistory, store) ReactDOM.render( diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js new file mode 100644 index 000000000..7691d544e --- /dev/null +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -0,0 +1,194 @@ +import React, {Component, PropTypes} from 'react' +import { Row, Col, Image, Button, Panel, Label, Tabs, Tab, Glyphicon, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' +import moment from 'moment' +import Icon from 'react-fa' +import numeral from 'numeral' + +import EditableTextField from '../../common/components/EditableTextField' +import { VersionButtonToolbar } from './FeedVersionViewer' +import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' + +import Feed from './reporter/containers/Feed' +import Patterns from './reporter/containers/Patterns' +import Routes from './reporter/containers/Routes' +import Stops from './reporter/containers/Stops' + +const dateFormat = 'MMM. DD, YYYY' +const timeFormat = 'h:MMa' + +export default class FeedVersionReport extends Component { + + static propTypes = { + version: PropTypes.object, + versions: PropTypes.array, + feedVersionIndex: PropTypes.number, + // versionSection: PropTypes.string, + + isPublic: PropTypes.bool, + hasVersions: PropTypes.bool, + // listView: PropTypes.bool, + + // newNotePosted: PropTypes.func, + // notesRequested: PropTypes.func, + // fetchValidationResult: PropTypes.func, + feedVersionRenamed: PropTypes.func, + // downloadFeedClicked: PropTypes.func, + // loadFeedVersionForEditing: PropTypes.func + } + constructor (props) { + super(props) + this.state = {tab: 'feed'} + } + getVersionDateLabel (version) { + const now = +moment() + const future = version.validationSummary && version.validationSummary.startDate > now + const expired = version.validationSummary && version.validationSummary.endDate < now + return version.validationSummary + ? + : null + } + selectTab (tab) { + this.setState({tab}) + } + render () { + const version = this.props.version + const messages = getComponentMessages('FeedVersionReport') + + if (!version) return

{getMessage(messages, 'noVersionsExist')}

+ + const fsCenter = version.validationSummary && version.validationSummary.bounds + ? `${(version.validationSummary.bounds.east + version.validationSummary.bounds.west) / 2},${(version.validationSummary.bounds.north + version.validationSummary.bounds.south) / 2}` + : null + const fsOverlay = fsCenter + ? `pin-l-bus(${fsCenter})/` + : '' + const mapUrl = fsCenter + ? `https://api.mapbox.com/v4/mapbox.light/${fsOverlay}${fsCenter},6/1000x800@2x.png?access_token=${getConfigProperty('mapbox.access_token')}` + : '' + const versionHeader = ( +
+

+ {/* Name Display / Editor */} + {version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0 + ? + : version.validationSummary.errorCount > 0 + ? + : + } + {this.props.isPublic + ? {version.name} + : this.props.feedVersionRenamed(version, value)} + /> + } + +

+ Version published {moment(version.updated).fromNow()} +
+ ) + const tableOptions = { + striped: true, + search: true, + hover: true, + exportCSV: true, + // maxHeight: '500px', + pagination: true, + options: { + paginationShowsTotal: true + } + } + return {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} + > + + + + + + {`${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} + {' '} + + {this.getVersionDateLabel(version)} + + + } + > + {getMessage(messages, 'validDates')} + + + this.selectTab(key)} + id='uncontrolled-tab-example' + bsStyle='pills' + > + + + +

{numeral(version.validationSummary.agencyCount).format('0 a')}

+

{getMessage(messages, 'agencyCount')}

+ + +

{numeral(version.validationSummary.routeCount).format('0 a')}

+

{getMessage(messages, 'routeCount')}

+ + +

{numeral(version.validationSummary.tripCount).format('0 a')}

+

{getMessage(messages, 'tripCount')}

+ + +

{numeral(version.validationSummary.stopTimesCount).format('0 a')}

+

{getMessage(messages, 'stopTimesCount')}

+ +
+
+ + this.selectTab(key)} + tableOptions={tableOptions} + /> + + + this.selectTab(key)} + tableOptions={tableOptions} + /> + + + this.selectTab(key)} + tableOptions={tableOptions} + /> + +
+
+
+
+ } +} diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index 9b4992abd..ad49399d2 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -1,26 +1,24 @@ import React, {Component, PropTypes} from 'react' -import { Row, Col, Table, Image, ButtonGroup, Button, Panel, Label, Glyphicon, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Row, Col, Button, Panel, Label, Glyphicon, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' import { LinkContainer } from 'react-router-bootstrap' import Icon from 'react-fa' -import numeral from 'numeral' import GtfsValidationViewer from './validation/GtfsValidationViewer' import GtfsValidationExplorer from './validation/GtfsValidationExplorer' +import FeedVersionReport from './FeedVersionReport' import NotesViewer from './NotesViewer' import ConfirmModal from '../../common/components/ConfirmModal' -import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsPlusVersionSummary from '../../gtfsplus/containers/ActiveGtfsPlusVersionSummary' import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' -const dateFormat = 'MMM. DD, YYYY' -const timeFormat = 'h:MMa' - export default class FeedVersionViewer extends Component { static propTypes = { version: PropTypes.object, versions: PropTypes.array, + feedVersionIndex: PropTypes.number, + versionSection: PropTypes.string, isPublic: PropTypes.bool, hasVersions: PropTypes.bool, @@ -33,85 +31,20 @@ export default class FeedVersionViewer extends Component { downloadFeedClicked: PropTypes.func, loadFeedVersionForEditing: PropTypes.func } - getVersionDateLabel (version) { - const now = +moment() - const future = version.validationSummary && version.validationSummary.startDate > now - const expired = version.validationSummary && version.validationSummary.endDate < now - return version.validationSummary - ? - : null - } render () { const version = this.props.version const messages = getComponentMessages('FeedVersionViewer') if (!version) return

{getMessage(messages, 'noVersionsExist')}

- const fsCenter = version.validationSummary && version.validationSummary.bounds - ? `${(version.validationSummary.bounds.east + version.validationSummary.bounds.west) / 2},${(version.validationSummary.bounds.north + version.validationSummary.bounds.south) / 2}` - : null - const fsOverlay = fsCenter - ? `pin-l-bus(${fsCenter})/` - : '' - const mapUrl = fsCenter - ? `https://api.mapbox.com/v4/mapbox.light/${fsOverlay}${fsCenter},6/1000x800@2x.png?access_token=${getConfigProperty('mapbox.access_token')}` - : '' - const versionHeader = ( -
-

- {/* Name Display / Editor */} - {version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0 - ? - : version.validationSummary.errorCount > 0 - ? - : - } - {this.props.isPublic - ? {version.name} - : this.props.feedVersionRenamed(version, value)} - /> - } - -

- Version published {moment(version.updated).fromNow()} -
- ) - - if (this.props.listView) { + // List view of feed versions return ( - List of feed versions}> - - {this.props.versions - ? this.props.versions.map(v => { - return ( - - {v.name} - {' '} - - {this.getVersionDateLabel(v)} - - - - ) - }) - : - No versions - - } - - + ) @@ -126,78 +59,17 @@ export default class FeedVersionViewer extends Component { return ( - - - Version summary - Validation issues - {isModuleEnabled('gtfsplus') - ? GTFS+ for this version - : null - } - Version comments - - + {!this.props.versionSection - ? {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} - > - - - - - - {`${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} - {' '} - - {this.getVersionDateLabel(version)} - - - } - > - {getMessage(messages, 'validDates')} - - - - -

{numeral(version.validationSummary.agencyCount).format('0 a')}

-

{getMessage(messages, 'agencyCount')}

- - -

{numeral(version.validationSummary.routeCount).format('0 a')}

-

{getMessage(messages, 'routeCount')}

- - -

{numeral(version.validationSummary.tripCount).format('0 a')}

-

{getMessage(messages, 'tripCount')}

- - -

{numeral(version.validationSummary.stopTimesCount).format('0 a')}

-

{getMessage(messages, 'stopTimesCount')}

- -
-
-
-
+ ? : this.props.versionSection === 'issues' ? + + + Version summary + + + Validation issues + + {isModuleEnabled('gtfsplus') + ? + GTFS+ for this version + + : null + } + + Version comments + + + + ) + } +} + +class VersionList extends Component { + static propTypes = { + versions: PropTypes.array + } + getVersionDateLabel (version) { + const now = +moment() + const future = version.validationSummary && version.validationSummary.startDate > now + const expired = version.validationSummary && version.validationSummary.endDate < now + return version.validationSummary + ? + : null + } + render () { + return List of feed versions}> + + {this.props.versions + ? this.props.versions.map(v => { + return ( + + {v.name} + {' '} + + {this.getVersionDateLabel(v)} + + + + ) + }) + : + No versions + + } + + + } +} diff --git a/src/main/client/manager/components/reporter/components/FeedLayout.js b/src/main/client/manager/components/reporter/components/FeedLayout.js new file mode 100644 index 000000000..37f981e44 --- /dev/null +++ b/src/main/client/manager/components/reporter/components/FeedLayout.js @@ -0,0 +1,46 @@ +import React from 'react' +import { Grid, Alert } from 'react-bootstrap' +import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' + +import Loading from '../../../../common/components/Loading' + + +export default class FeedLayout extends React.Component { + + static propTypes = {} + componentWillMount () { + this.props.onComponentMount(this.props) + } + + render () { + return ( + + + + {(this.props.feed.fetchStatus.fetching || + !this.props.feed.fetchStatus.fetched) && + + } + + {this.props.feed.fetchStatus.error && + + An error occurred while trying to fetch the data + + } + + {this.props.feed.fetchStatus.fetched && + + + Statistic + Value + + } + + + ) + } +} diff --git a/src/main/client/manager/components/reporter/components/PageLayout.js b/src/main/client/manager/components/reporter/components/PageLayout.js new file mode 100644 index 000000000..2ac64f18d --- /dev/null +++ b/src/main/client/manager/components/reporter/components/PageLayout.js @@ -0,0 +1,42 @@ +import React from 'react' +import { Grid, PageHeader, Nav, NavItem } from 'react-bootstrap' + +import 'react-bootstrap-table/dist/react-bootstrap-table.min.css' +import 'react-select/dist/react-select.css' + +import Feed from '../containers/Feed' +import Patterns from '../containers/Patterns' +import Routes from '../containers/Routes' +import Stops from '../containers/Stops' + + +export default class PageLayout extends React.Component { + + render () { + console.log(this.props) + return ( + + Reports + + {this.props.activeTab === 'feed' && + + } + {this.props.activeTab === 'routes' && + + } + {this.props.activeTab === 'patterns' && + + } + {this.props.activeTab === 'stop' && + + } + + ) + } +} diff --git a/src/main/client/manager/components/reporter/components/PatternLayout.js b/src/main/client/manager/components/reporter/components/PatternLayout.js new file mode 100644 index 000000000..64f98938b --- /dev/null +++ b/src/main/client/manager/components/reporter/components/PatternLayout.js @@ -0,0 +1,83 @@ +import React, { Component, PropTypes } from 'react' +import { Row, Col, Alert, Button } from 'react-bootstrap' +import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' +import Select from 'react-select' + +import Loading from '../../../../common/components/Loading' + +export default class PatternLayout extends Component { + static propTypes = { + onComponentMount: PropTypes.func, + patternRouteFilterChange: PropTypes.func, + + routes: PropTypes.object, + patterns: PropTypes.object, + tableOptions: PropTypes.object + } + constructor (props) { + super(props) + } + componentWillMount () { + this.props.onComponentMount(this.props) + } + render () { + const self = this + + return ( +
+ + {this.props.routes.fetchStatus.fetched && + + + + + + + + - - +
+ + + + - - - + + } + {this.props.stops.fetchStatus.fetching && } + {this.props.stops.fetchStatus.error && + + An error occurred while trying to fetch the data + + } {this.props.stops.fetchStatus.fetched && ID Name # of Trips - cell >= 0 ? Math.round(cell / 60) : 'N/A'} dataField='headway'>Headway + Headway {/*Best Headway*/} {/*Network Importance*/} diff --git a/src/main/client/manager/components/reporter/containers/Feed.js b/src/main/client/manager/components/reporter/containers/Feed.js index be7caae7e..22bf0d681 100644 --- a/src/main/client/manager/components/reporter/containers/Feed.js +++ b/src/main/client/manager/components/reporter/containers/Feed.js @@ -4,20 +4,18 @@ import { connect } from 'react-redux' import { fetchFeed } from '../../../../gtfs/actions/feed' import FeedLayout from '../components/FeedLayout' - const mapStateToProps = (state, ownProps) => { return { - feed: state.feed + feed: state.gtfs.feed } } const mapDispatchToProps = (dispatch, ownProps) => { + const feedId = ownProps.version.feedSource.id return { onComponentMount: (initialProps) => { - console.log('feed mount') - if(!initialProps.feed.fetchStatus.fetched) { - console.log('do dispatch fetch feed') - dispatch(fetchFeed()) + if (!initialProps.feed.fetchStatus.fetched) { + dispatch(fetchFeed(feedId)) } } } diff --git a/src/main/client/manager/components/reporter/containers/Patterns.js b/src/main/client/manager/components/reporter/containers/Patterns.js index fbd48cb68..58768017b 100644 --- a/src/main/client/manager/components/reporter/containers/Patterns.js +++ b/src/main/client/manager/components/reporter/containers/Patterns.js @@ -2,11 +2,10 @@ import React from 'react' import { connect } from 'react-redux' import PatternLayout from '../components/PatternLayout' -import { fetchPatterns, patternRouteFilterChange } from '../../../../gtfs/actions/patterns' +import { patternRouteFilterChange, patternDateTimeFilterChange } from '../../../../gtfs/actions/patterns' import { stopPatternFilterChange } from '../../../../gtfs/actions/stops' import { fetchRoutes } from '../../../../gtfs/actions/routes' - const mapStateToProps = (state, ownProps) => { return { routes: state.gtfs.routes, @@ -18,7 +17,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { const feedId = ownProps.version.feedSource.id return { onComponentMount: (initialProps) => { - if(!initialProps.routes.fetchStatus.fetched) { + if (!initialProps.routes.fetchStatus.fetched) { dispatch(fetchRoutes(feedId)) } // if(!initialProps.patterns.fetchStatus.fetched) { @@ -31,7 +30,10 @@ const mapDispatchToProps = (dispatch, ownProps) => { viewStops: (row) => { dispatch(stopPatternFilterChange(feedId, row)) ownProps.selectTab('stops') - } + }, + patternDateTimeFilterChange: (props) => { + dispatch(patternDateTimeFilterChange(feedId, props)) + }, } } diff --git a/src/main/client/manager/components/reporter/containers/Stops.js b/src/main/client/manager/components/reporter/containers/Stops.js index d63c19fdc..8ad396bf8 100644 --- a/src/main/client/manager/components/reporter/containers/Stops.js +++ b/src/main/client/manager/components/reporter/containers/Stops.js @@ -20,9 +20,9 @@ const mapDispatchToProps = (dispatch, ownProps) => { const feedId = ownProps.version.feedSource.id return { onComponentMount: (initialProps) => { - // if(!initialProps.routes.fetchStatus.fetched) { - // dispatch(fetchRoutes(feedId)) - // } + if (!initialProps.routes.fetchStatus.fetched) { + dispatch(fetchRoutes(feedId)) + } // if(!initialProps.patterns.fetchStatus.fetched) { // dispatch(fetchPatterns(feedId, null)) // } diff --git a/src/main/client/manager/components/validation/GtfsValidationExplorer.js b/src/main/client/manager/components/validation/GtfsValidationExplorer.js index b802e9b1b..08b83c81e 100644 --- a/src/main/client/manager/components/validation/GtfsValidationExplorer.js +++ b/src/main/client/manager/components/validation/GtfsValidationExplorer.js @@ -35,7 +35,7 @@ export default class GtfsValidationExplorer extends Component { } } - render() { + render () { const version = this.props.version const messages = getComponentMessages('GtfsValidationExplorer') diff --git a/src/main/client/manager/components/validation/GtfsValidationSummary.js b/src/main/client/manager/components/validation/GtfsValidationSummary.js index d333693d2..492f23e8d 100644 --- a/src/main/client/manager/components/validation/GtfsValidationSummary.js +++ b/src/main/client/manager/components/validation/GtfsValidationSummary.js @@ -1,21 +1,36 @@ -import React from 'react' -import { Panel, Table, Glyphicon, Button } from 'react-bootstrap' -import { browserHistory } from 'react-router' +import React, { Component, PropTypes } from 'react' +import { Table, Glyphicon, Button } from 'react-bootstrap' +import Icon from 'react-fa' +// import { connect } from 'react-redux' +import { LinkContainer } from 'react-router-bootstrap' -export default class GtfsValidationSummary extends React.Component { +import Loading from '../../../common/components/Loading' +export default class GtfsValidationSummary extends Component { + static propTypes = { + version: PropTypes.object, + feedVersionIndex: PropTypes.number, + fetchValidationResult: PropTypes.func + } constructor (props) { super(props) this.state = { expanded: false } } - + componentWillMount () { + if (!this.props.version.validationResult) { + this.props.fetchValidationResult() + } + } componentWillReceiveProps (nextProps) { - if(!nextProps.validationResult) this.setState({ expanded: false }) + if (!nextProps.version.validationResult) { + this.setState({ expanded: false }) + } } - render () { - - const result = this.props.validationResult + const result = this.props.version.validationResult + if (!result) { + return + } let errors = {} result && result.errors.map(error => { if (!errors[error.file]) { @@ -23,14 +38,6 @@ export default class GtfsValidationSummary extends React.Component { } errors[error.file].push(error) }) - const header = ( -

{ - if(!result) this.props.fetchValidationResult() - this.setState({ expanded: !this.state.expanded }) - }}> - Validation Results -

- ) let report = null const tableStyle = { @@ -75,6 +82,16 @@ export default class GtfsValidationSummary extends React.Component { return (
{report} + + +
) } @@ -121,3 +138,5 @@ class ResultTable extends React.Component { ) } } + +// export default connect()(GtfsValidationSummary) diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index 2ba438a81..d6226db18 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -65,18 +65,6 @@ export default class GtfsValidationViewer extends React.Component { } return
-

- {isModuleEnabled('validator') - ? - : null - } -

{report}
} diff --git a/src/main/client/manager/components/validation/TripsChart.js b/src/main/client/manager/components/validation/TripsChart.js index 1b2c8b1bc..ab0334942 100644 --- a/src/main/client/manager/components/validation/TripsChart.js +++ b/src/main/client/manager/components/validation/TripsChart.js @@ -1,25 +1,43 @@ -import React from 'react' +import React, { Component, PropTypes } from 'react' import moment from 'moment' // import rd3 from 'rd3' -export default class TripsChart extends React.Component { +import Loading from '../../../common/components/Loading' +export default class TripsChart extends Component { + static propTypes = { + validationResult: PropTypes.object, + fetchValidationResult: PropTypes.func + } + componentWillMount () { + if (!this.props.validationResult) { + this.props.fetchValidationResult() + } + } render () { - const data = Object.keys(this.props.data).map(key => [key, this.props.data[key]]) - const graphHeight = 400 + if (!this.props.validationResult) { + return + } + const tripsPerDate = this.props.validationResult.tripsPerDate + const data = Object.keys(tripsPerDate).map(key => [key, tripsPerDate[key]]) + const graphHeight = 300 const spacing = 8 - const leftMargin = 50, bottomMargin = 50 - const svgWidth = leftMargin + data.length * spacing, svgHeight = graphHeight + bottomMargin + const leftMargin = 50 + const bottomMargin = 50 + const svgWidth = leftMargin + data.length * spacing + const svgHeight = graphHeight + bottomMargin const maxTrips = Math.max.apply(Math, data.map(d => d[1])) - const yAxisMax = Math.ceil(maxTrips / 1000) * 1000; + const yAxisMax = Math.ceil(maxTrips / 100) * 100 + console.log(maxTrips, yAxisMax) const yAxisPeriod = maxTrips > 1000 ? 1000 : 100 - const yAxisLabels = []; + const yAxisLabels = [] for (var i = yAxisPeriod; i <= yAxisMax; i += yAxisPeriod) { - yAxisLabels.push(i); + yAxisLabels.push(i) } return ( -
- + {l} })} {data.map((d, index) => { - const dow = moment(d[0]).day() - const x = leftMargin + spacing/2 + index * spacing + const dow = moment(d[0]).day() + const x = leftMargin + spacing / 2 + index * spacing - // generate the bar for this date - return + // generate the bar for this date + return ( + {index % 14 === 0 /* label the date every 14 days */ @@ -66,8 +85,9 @@ export default class TripsChart extends React.Component { : null } - }) + ) } + )}
diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index e616ce760..3fd0cdab6 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -179,8 +179,8 @@ const projects = (state = { projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) let versionSort = (a, b) => { - if(a.version < b.version) return -1 - if(a.version > b.version) return 1 + if (a.version < b.version) return -1 + if (a.version > b.version) return 1 return 0 } return update(state, From 0917f5d22a808ee38cce4632076b9afd54051f96 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 10:37:47 -0400 Subject: [PATCH 010/323] back end updates for reporter ui --- .../datatools/manager/auth/Auth0Users.java | 17 +++++ .../api/FeedVersionController.java | 43 +++++++++-- .../controllers/api/UserController.java | 30 +++++--- .../manager/jobs/ReadTransportNetworkJob.java | 75 ++++++++++++++++++- .../manager/models/FeedValidationResult.java | 35 +++++++++ 5 files changed, 181 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/auth/Auth0Users.java b/src/main/java/com/conveyal/datatools/manager/auth/Auth0Users.java index e39183970..315c5db4e 100644 --- a/src/main/java/com/conveyal/datatools/manager/auth/Auth0Users.java +++ b/src/main/java/com/conveyal/datatools/manager/auth/Auth0Users.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.auth; import com.conveyal.datatools.manager.DataManager; +import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.http.HttpResponse; @@ -15,6 +16,8 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; +import java.util.Collection; +import java.util.HashSet; /** * Created by landon on 4/26/16. @@ -89,6 +92,20 @@ public static String getAuth0Users(String searchQuery, int page) { return doRequest(uri); } + public static Collection getAll () { + Collection users = new HashSet<>(); + + // limited to the first 100 + URI uri = getUrl(null, 0, 100, false); + String response = doRequest(uri); + try { + users = mapper.readValue(response, new TypeReference>(){}); + } catch (IOException e) { + e.printStackTrace(); + } + return users; + } + public static Auth0UserProfile getUserById(String id) { URIBuilder builder = new URIBuilder(); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index cff912389..8e7c38c81 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -14,6 +14,7 @@ import com.conveyal.datatools.manager.jobs.BuildTransportNetworkJob; import com.conveyal.datatools.manager.jobs.CreateFeedVersionFromSnapshotJob; import com.conveyal.datatools.manager.jobs.ProcessSingleFeedJob; +import com.conveyal.datatools.manager.jobs.ReadTransportNetworkJob; import com.conveyal.datatools.manager.models.FeedDownloadToken; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; @@ -38,6 +39,7 @@ import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.*; import com.fasterxml.jackson.databind.JsonNode; @@ -59,6 +61,7 @@ public class FeedVersionController { private static ObjectMapper mapper = new ObjectMapper(); public static JsonManager json = new JsonManager(FeedVersion.class, JsonViews.UserInterface.class); + private static Set readingNetworkVersionList = new HashSet<>(); /** * Grab this feed version. @@ -311,24 +314,44 @@ public static Object getValidationResult(Request req, Response res, boolean chec } public static Object getIsochrones(Request req, Response res) { - LOG.info(req.uri()); Auth0UserProfile userProfile = req.attribute("user"); String id = req.params("id"); FeedVersion version = FeedVersion.get(id); + + // required Double fromLat = Double.valueOf(req.queryParams("fromLat")); Double fromLon = Double.valueOf(req.queryParams("fromLon")); Double toLat = Double.valueOf(req.queryParams("toLat")); Double toLon = Double.valueOf(req.queryParams("toLon")); + LocalDate date = req.queryParams("date") != null ? LocalDate.parse(req.queryParams("date"), DateTimeFormatter.ISO_LOCAL_DATE) : LocalDate.now(); // 2011-12-03 + + // optional with defaults + Integer fromTime = req.queryParams("fromTime") != null ? Integer.valueOf(req.queryParams("fromTime")) : 9 * 3600; + Integer toTime = req.queryParams("toTime") != null ? Integer.valueOf(req.queryParams("toTime")) : 10 * 3600; if (version.transportNetwork == null) { InputStream is = null; try { - is = new FileInputStream(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + version.feedSourceId + "/" + version.id + "_network.dat"); - version.transportNetwork = TransportNetwork.read(is); + if (readingNetworkVersionList.contains(version.id)) { + halt(503, "Try again later. Reading transport network"); + return null; + } + else { + // ReadTransportNetworkJob rtnj = new ReadTransportNetworkJob(version, userProfile.getUser_id()); +// Thread tnThread = new Thread(rtnj); +// tnThread.start(); + is = new FileInputStream(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + version.feedSourceId + "/" + version.id + "_network.dat"); + readingNetworkVersionList.add(version.id); + try { + version.transportNetwork = TransportNetwork.read(is); + } catch (Exception e) { + e.printStackTrace(); + } + } } - // Catch if transport network not built yet - catch (Exception e) { + // Catch FNFE exception transport network not built yet + catch (FileNotFoundException e) { e.printStackTrace(); if (DataManager.config.get("modules").get("validator").get("enabled").asBoolean()) { // new BuildTransportNetworkJob(version).run(); @@ -341,17 +364,21 @@ public static Object getIsochrones(Request req, Response res) { } if (version.transportNetwork != null) { + // remove version from list of reading network + if (readingNetworkVersionList.contains(version.id)) { + readingNetworkVersionList.remove(version.id); + } AnalystClusterRequest clusterRequest = new AnalystClusterRequest(); clusterRequest.profileRequest = new ProfileRequest(); clusterRequest.profileRequest.transitModes = EnumSet.of(TransitModes.TRANSIT); clusterRequest.profileRequest.accessModes = EnumSet.of(LegMode.WALK); - clusterRequest.profileRequest.date = LocalDate.now(); + clusterRequest.profileRequest.date = date; clusterRequest.profileRequest.fromLat = fromLat; clusterRequest.profileRequest.fromLon = fromLon; clusterRequest.profileRequest.toLat = toLat; clusterRequest.profileRequest.toLon = toLon; - clusterRequest.profileRequest.fromTime = 9*3600; - clusterRequest.profileRequest.toTime = 10*3600; + clusterRequest.profileRequest.fromTime = fromTime; + clusterRequest.profileRequest.toTime = toTime; clusterRequest.profileRequest.egressModes = EnumSet.of(LegMode.WALK); clusterRequest.profileRequest.zoneId = ZoneId.of("America/New_York"); PointSet targets = version.transportNetwork.getGridPointSet(); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java index 6c7a5bfcf..b75c03a48 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java @@ -24,6 +24,10 @@ import java.io.*; import java.net.URLEncoder; +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.*; import com.conveyal.datatools.manager.auth.Auth0Users; @@ -182,7 +186,11 @@ public static Object deleteUser(Request req, Response res) throws IOException { public static Object getRecentActivity(Request req, Response res) { Auth0UserProfile userProfile = req.attribute("user"); - + String from = req.queryParams("from"); + String to = req.queryParams("to"); +// if (from == null || to == null) { +// halt(400, "Please provide valid from/to dates"); +// } List activity = new ArrayList<>(); for (Auth0UserProfile.Subscription sub : userProfile.getApp_metadata().getDatatoolsInfo().getSubscriptions()) { @@ -197,15 +205,17 @@ public static Object getRecentActivity(Request req, Response res) { System.out.println(" obj=" + fs); for (Note note : fs.getNotes()) { // TODO: Check if actually recent - Activity act = new Activity(); - act.type = sub.getType(); - act.userId = note.userId; - act.userName = note.userEmail; - act.body = note.body; - act.date = note.date; - act.targetId = targetId; - act.targetName = fs.name; - activity.add(act); +// if (note.date.after(Date.from(Instant.ofEpochSecond(from))) && note.date.before(Date.from(Instant.ofEpochSecond(to)))) { + Activity act = new Activity(); + act.type = sub.getType(); + act.userId = note.userId; + act.userName = note.userEmail; + act.body = note.body; + act.date = note.date; + act.targetId = targetId; + act.targetName = fs.name; + activity.add(act); +// } } } break; diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java index d639a1d11..36a271fe0 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java @@ -1,7 +1,80 @@ package com.conveyal.datatools.manager.jobs; +import com.conveyal.datatools.common.status.MonitorableJob; +import com.conveyal.datatools.manager.DataManager; +import com.conveyal.datatools.manager.models.FeedVersion; +import com.conveyal.r5.transit.TransportNetwork; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.Map; + /** * Created by landon on 10/11/16. */ -public class ReadTransportNetworkJob { +public class ReadTransportNetworkJob extends MonitorableJob { + public FeedVersion feedVersion; + public TransportNetwork result; + public Status status; + + public ReadTransportNetworkJob (FeedVersion feedVersion, String owner) { + super(owner, "Reading in Transport Network for " + feedVersion.getFeedSource().name, JobType.BUILD_TRANSPORT_NETWORK); + this.feedVersion = feedVersion; + this.result = null; + this.status = new Status(); + status.message = "Waiting to begin job..."; + } + + @Override + public void run() { + System.out.println("Reading network"); + InputStream is = null; + try { + is = new FileInputStream(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + feedVersion.feedSourceId + "/" + feedVersion.id + "_network.dat"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + try { + feedVersion.transportNetwork = TransportNetwork.read(is); + } catch (Exception e) { + e.printStackTrace(); + } + synchronized (status) { + status.message = "Transport network read successfully!"; + status.percentComplete = 100; + status.completed = true; + } + jobFinished(); + } + + @Override + public Status getStatus() { + synchronized (status) { + return status.clone(); + } + } + + @Override + public void handleStatusEvent(Map statusMap) { + try { + synchronized (status) { + status.message = (String) statusMap.get("message"); + status.percentComplete = (double) statusMap.get("percentComplete"); + status.error = (boolean) statusMap.get("error"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + +// @Override +// public void handleStatusEvent(StatusEvent statusEvent) { +// synchronized (status) { +// status.message = statusEvent.message; +// status.percentComplete = statusEvent.percentComplete +// status.error = statusEvent.error; +// } +// } + } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java index 85e3b7bf7..57baa89be 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java @@ -1,5 +1,6 @@ package com.conveyal.datatools.manager.models; +import com.conveyal.gtfs.GTFSFeed; import com.conveyal.gtfs.model.ValidationResult; import com.conveyal.gtfs.validator.json.LoadStatus; import com.fasterxml.jackson.annotation.JsonProperty; @@ -8,6 +9,7 @@ import java.io.Serializable; import java.time.LocalDate; import java.util.Collection; +import java.util.stream.Collectors; /** * Created by landon on 5/10/16. @@ -32,5 +34,38 @@ public class FeedValidationResult implements Serializable { public Rectangle2D bounds; public FeedValidationResult() { +// this.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); +// this.agencyCount = stats.getAgencyCount(); +// this.routeCount = stats.getRouteCount(); +// this.bounds = stats.getBounds(); +// LocalDate calDateStart = stats.getCalendarDateStart(); +// LocalDate calSvcStart = stats.getCalendarServiceRangeStart(); +// +// LocalDate calDateEnd = stats.getCalendarDateEnd(); +// LocalDate calSvcEnd = stats.getCalendarServiceRangeEnd(); +// +// if (calDateStart == null && calSvcStart == null) +// // no service . . . this is bad +// this.startDate = null; +// else if (calDateStart == null) +// this.startDate = calSvcStart; +// else if (calSvcStart == null) +// this.startDate = calDateStart; +// else +// this.startDate = calDateStart.isBefore(calSvcStart) ? calDateStart : calSvcStart; +// +// if (calDateEnd == null && calSvcEnd == null) +// // no service . . . this is bad +// this.endDate = null; +// else if (calDateEnd == null) +// this.endDate = calSvcEnd; +// else if (calSvcEnd == null) +// this.endDate = calDateEnd; +// else +// this.endDate = calDateEnd.isAfter(calSvcEnd) ? calDateEnd : calSvcEnd; +// this.loadStatus = LoadStatus.SUCCESS; +// this.tripCount = stats.getTripCount(); +// this.stopTimesCount = stats.getStopTimesCount(); +// this.errorCount = gtfsFeed.errors.size(); } } \ No newline at end of file From 49895cf6300fd7e419b95a9c095fb225286b39a2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 10:40:30 -0400 Subject: [PATCH 011/323] update active sidebar item --- src/main/client/common/components/Sidebar.js | 1 + src/main/client/common/components/SidebarNavItem.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 4219c05c5..75f963c0b 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -58,6 +58,7 @@ export default class Sidebar extends Component { position: 'fixed', top: 10, left: 10, + // left: 0, cursor: 'pointer' } const LOGO_SIZE = 30 diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index c51b75c26..525bc2052 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -26,7 +26,9 @@ export default class SidebarNavItem extends Component { const containerStyle = { marginBottom: 20, cursor: 'pointer', - color: this.props.active ? '#0ff' : this.state.hover ? '#fff' : '#ccc' + color: this.props.active ? '#fff' : this.state.hover ? '#fff' : '#ccc', + // backgroundColor: this.props.active ? '#bbb' : 'rgba(0,0,0,0)', + // padding: 5 } const iconContainerStyle = { From cc6d08ecf6d7721bc0b82341dce7cd525e304755 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 10:41:09 -0400 Subject: [PATCH 012/323] change routes to objects, remove validation explorer routes --- src/main/client/common/containers/App.js | 74 +++++++++++++----------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/src/main/client/common/containers/App.js b/src/main/client/common/containers/App.js index 81549e7c9..676b46b4e 100644 --- a/src/main/client/common/containers/App.js +++ b/src/main/client/common/containers/App.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Component, PropTypes } from 'react' import { connect } from 'react-redux' import { Router, Route, browserHistory } from 'react-router' @@ -26,29 +26,35 @@ import ActiveGtfsPlusEditor from '../../gtfsplus/containers/ActiveGtfsPlusEditor import ActiveGtfsEditor from '../../editor/containers/ActiveGtfsEditor' import ActiveGtfsTableEditor from '../../editor/containers/ActiveGtfsTableEditor' -import ActiveGtfsValidationMap from '../../manager/containers/validation/ActiveGtfsValidationMap' -import ActiveGtfsValidationExplorer from '../../manager/containers/validation/ActiveGtfsValidationExplorer' +// import ActiveGtfsValidationMap from '../../manager/containers/validation/ActiveGtfsValidationMap' +// import ActiveGtfsValidationExplorer from '../../manager/containers/validation/ActiveGtfsValidationExplorer' require('../style.css') // import { UserIsAuthenticated, UserIsAdmin } from '../util/util' -class App extends React.Component { - +class App extends Component { + static propTypes = { + checkExistingLogin: PropTypes.func, + user: PropTypes.object, + login: PropTypes.func, + history: PropTypes.object + } constructor (props) { super(props) } componentDidMount () { // set up status checkLogin - /*setInterval(() => { + /* setInterval(() => { console.log('status check!', this.props.user); this.props.checkJobStatus() - }, 5000)*/ + }, 5000) */ } render () { const requireAuth = (nextState, replace, callback) => { + console.log(nextState) this.props.checkExistingLogin() .then((action) => { if (this.props.user.profile === null) { @@ -74,56 +80,54 @@ class App extends React.Component { console.log('requiring admin') if (this.props.user.profile === null || !this.props.user.permissions.isApplicationAdmin()) { browserHistory.push('/') - // this.props.login({closable: false}, callback) - // .then(() => { - // callback() - // }) } else { callback() } }) } const routes = [ - , - , - , - , - , - , - , - , - // , - , - , - , - - , - , - , - + {path: '/settings(/:subpage)(/:projectId)', component: ActiveUserAccount, onEnter: requireAuth}, + {path: '/admin(/:subpage)', component: ActiveUserAdmin, onEnter: requireAdmin}, + {path: '/signup', component: ActiveSignupPage, onEnter: checkLogin}, + {path: '/home(/:projectId)', component: ActiveUserHomePage, onEnter: requireAuth}, + {path: '/', component: ActivePublicFeedsViewer, onEnter: checkLogin}, + {path: '/public/feed/:feedSourceId(/version/:feedVersionIndex)', component: ActivePublicFeedSourceViewer, onEnter: checkLogin}, + {path: '/project', component: ActiveProjectsList, onEnter: requireAuth}, + {path: '/project/:projectId(/:subpage)(/:subsubpage)', component: ActiveProjectViewer, onEnter: requireAuth}, + { + path: '/feed/:feedSourceId/edit(/:subpage)(/:entity)(/:subsubpage)(/:subentity)(/:subsubcomponent)(/:subsubentity)', + component: ActiveGtfsEditor, + onEnter: requireAuth + }, + {path: '/feed/:feedSourceId(/version/:feedVersionIndex)(/:subpage)(/:subsubpage)', component: ActiveFeedSourceViewer, onEnter: requireAuth}, + + {path: '/deployment/:deploymentId', component: ActiveDeploymentViewer, onEnter: requireAuth}, + {path: '/gtfsplus/:feedSourceId/:feedVersionId', component: ActiveGtfsPlusEditor, onEnter: requireAuth}, + {path: '/feed/:feedSourceId/editTable/:feedVersionId(/:subpage)', component: ActiveGtfsTableEditor, onEnter: requireAuth}, + {path: '*', component: PageNotFound} ] // register routes if alerts module enabled if (isModuleEnabled('alerts')) { routes.unshift( - , - , - + {path: 'alerts', component: MainAlertsViewer, onEnter: requireAuth}, + {path: 'alerts/new', component: ActiveAlertEditor, onEnter: requireAuth}, + {path: 'alerts/alert/:alertId', component: ActiveAlertEditor, onEnter: requireAuth}, ) } // register routes if sign_config module enabled if (isModuleEnabled('sign_config')) { routes.unshift( - , - , - + {path: 'signs', component: MainSignsViewer, onEnter: requireAuth}, + {path: 'signs/new', component: ActiveSignEditor, onEnter: requireAuth}, + {path: 'signs/sign/:signId', component: ActiveSignEditor, onEnter: requireAuth}, ) } return ( - {routes} + {routes.map((r, i) => ())} ) } From 739be905c61cda61abc2f1283339be56672e819c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 10:41:36 -0400 Subject: [PATCH 013/323] code style --- .../client/common/containers/WatchButton.js | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/client/common/containers/WatchButton.js b/src/main/client/common/containers/WatchButton.js index 83b9ccd2a..a6a80bbfb 100644 --- a/src/main/client/common/containers/WatchButton.js +++ b/src/main/client/common/containers/WatchButton.js @@ -1,17 +1,26 @@ -import React from 'react' +import React, { PropTypes, Component } from 'react' import { Button, Glyphicon, MenuItem } from 'react-bootstrap' import { updateTargetForSubscription } from '../../manager/actions/user' import { connect } from 'react-redux' import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' -class WatchButton extends React.Component { +class WatchButton extends Component { + static propTypes = { + dispatch: PropTypes.func, + isWatching: PropTypes.string, + user: PropTypes.object, + target: PropTypes.string, + subscriptionType: PropTypes.string, + componentClass: PropTypes.string + } render () { const {dispatch, isWatching, user, target, subscriptionType} = this.props const messages = getComponentMessages('WatchButton') - if (!getConfigProperty('application.notifications_enabled')) + if (!getConfigProperty('application.notifications_enabled')) { return null + } if (this.props.componentClass === 'menuItem') { return ( ) - } - else { + } else { return (
: null - // const approveRouteForm = ( - // - // Status - // { - // let props = {} - // props.status = evt.target.value - // this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - // }} - // > - // - // - // - // ) const entityForm = this.props.activeComponent === 'scheduleexception' ? null : (
- { - // this.props.activeComponent === 'route' - // ? approveRouteForm - // : null - } - { - table.fields.map((field, colIndex) => { - // get editor field by splitting on first underscore - const editorField = field.name // .split(/_(.+)?/)[1] - const validationIssue = this.props.validation - ? this.props.validation.find(v => - (v.rowIndex === data.origRowIndex && v.fieldName === field.name)) - : null + {table.fields.map((field, colIndex) => { + // get editor field by splitting on first underscore + const editorField = field.name // .split(/_(.+)?/)[1] + // const validationIssue = this.props.validation + // ? this.props.validation.find(v => + // (v.rowIndex === data.origRowIndex && v.fieldName === field.name)) + // : null - const tooltip = validationIssue ? ( - {validationIssue.description} - ) : null + // const tooltip = validationIssue + // ? {validationIssue.description} + // : null return entity - ? getInput(rowIndex, field, entity[editorField], (rowIndex * table.fields.length) + colIndex + 1) + ?
{getInput(rowIndex, field, entity[editorField], (rowIndex * table.fields.length) + colIndex + 1)}
: null - })} + }) + }

* = field is required

- { - // this.props.activeComponent === 'fare' - // ?

Fare rules

- // : null - }
) let entityName = this.props.activeComponent === 'feedinfo' ? 'Feed Info' : getEntityName(this.props.activeComponent, entity) @@ -1253,7 +1248,7 @@ export default class EntityDetails extends Component { : null const header = ( -
+
@@ -1261,13 +1256,15 @@ export default class EntityDetails extends Component { ? Zoom to {this.props.activeComponent}}>
: [ -
@@ -1366,16 +1363,15 @@ export default class EntityDetails extends Component { : null } {validationErrors.length > 0 - ? Fix validation issues before saving + ? Fix validation issues before saving : null } {subNav} -
- , + ,
{this.props.subComponent === 'trippattern' - ? : this.state.editFareRules ? fareRulesForm diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index b638dcc14..eb8cd9440 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -1,11 +1,10 @@ import React, {Component, PropTypes} from 'react' -import { Table, Button, ButtonToolbar, Nav, NavItem, Tooltip, OverlayTrigger } from 'react-bootstrap' +import { Button, ButtonToolbar, Nav, NavItem, Tooltip, OverlayTrigger } from 'react-bootstrap' import {Icon} from 'react-fa' import { FlexTable, FlexColumn } from 'react-virtualized' -import { PureComponent, shallowEqual } from 'react-pure-render' +import { shallowEqual } from 'react-pure-render' import 'react-virtualized/styles.css' -import EntityDetails from './EntityDetails' import VirtualizedEntitySelect from './VirtualizedEntitySelect' import GtfsTable from './GtfsTable' import { getEntityName } from '../util/gtfs' @@ -22,7 +21,7 @@ export default class EntityList extends Component { setActiveEntity: PropTypes.func.isRequired, updateActiveEntity: PropTypes.func.isRequired, deleteEntity: PropTypes.func.isRequired, - newEntityClicked: PropTypes.func.isRequired, + newGtfsEntity: PropTypes.func.isRequired, activeComponent: PropTypes.string.isRequired } @@ -38,7 +37,12 @@ export default class EntityList extends Component { } } shouldComponentUpdate (nextProps) { - return !shallowEqual(nextProps, this.props) + // simply running shallowEqual on all props does not give us the performance we need here + // (especially with many, many stops) + return !shallowEqual(nextProps.entities, this.props.entities) || + !shallowEqual(nextProps.activeEntityId, this.props.activeEntityId) || + !shallowEqual(nextProps.activeComponent, this.props.activeComponent) || + !shallowEqual(nextProps.feedSource, this.props.feedSource) } _getRowStyle (index, list) { const activeColor = '#F2F2F2' @@ -79,6 +83,7 @@ export default class EntityList extends Component { // // } render () { + // console.log(this.props) const sidePadding = '5px' let panelWidth = !this.props.tableView ? `${this.props.width}px` : '100%' let panelStyle = { @@ -91,24 +96,11 @@ export default class EntityList extends Component { paddingRight: '0px', paddingLeft: sidePadding } - const sortedEntities = this.props.entities && this.props.entities.sort((a, b) => { - var aName = getEntityName(this.props.activeComponent, a) - var bName = getEntityName(this.props.activeComponent, b) - if (a.isCreating && !b.isCreating) return -1 - if (!a.isCreating && b.isCreating) return 1 - if (!isNaN(parseInt(aName)) && !isNaN(parseInt(bName)) && !isNaN(+aName) && !isNaN(+bName)) { - if (parseInt(aName) < parseInt(bName)) return -1 - if (parseInt(aName) > parseInt(bName)) return 1 - return 0 - } - if (aName.toLowerCase() < bName.toLowerCase()) return -1 - if (aName.toLowerCase() > bName.toLowerCase()) return 1 - return 0 - }) + const entArray = this.props.entities const activeEntity = this.props.activeEntity let activeIndex - const list = sortedEntities && sortedEntities.length ? sortedEntities.map((entity, index) => - { + const list = entArray && entArray.length + ? entArray.map((entity, index) => { if (activeEntity && entity.id === activeEntity.id) { activeIndex = index } @@ -161,7 +153,7 @@ export default class EntityList extends Component { bsSize='small' disabled={this.props.entities && this.props.entities.findIndex(e => e.id === 'new') !== -1} onClick={() => { - this.props.newEntityClicked(this.props.feedSource.id, this.props.activeComponent) + this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) }} > Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} @@ -176,7 +168,7 @@ export default class EntityList extends Component { ref="activeTable" feedSource={this.props.feedSource} table={activeTable} - tableData={sortedEntities || []} + tableData={entArray || []} newRowClicked={this.props.newRowClicked} saveRowClicked={this.props.saveRowClicked} deleteRowClicked={this.props.deleteRowClicked} @@ -185,7 +177,7 @@ export default class EntityList extends Component { this.props.gtfsEntitySelected(type, entity) }} getGtfsEntity={(type, id) => { - return sortedEntities.find(ent => ent.id === id) + return entArray.find(ent => ent.id === id) // return this.props.gtfsEntityLookup[`${type}_${id}`] }} showHelpClicked={(tableId, fieldName) => { @@ -290,7 +282,7 @@ export default class EntityList extends Component { bsSize='small' disabled={this.props.entities && this.props.entities.findIndex(e => e.id === 'new') !== -1} onClick={() => { - this.props.newEntityClicked(this.props.feedSource.id, this.props.activeComponent) + this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) }} > New {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} @@ -343,7 +335,7 @@ export default class EntityList extends Component { ? { if (!value) { this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent) diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index 7e5212dc4..1341b0c28 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -91,7 +91,10 @@ export default class FeedInfoPanel extends Component { {/* Navigation dropdown */} - Editing {feedName}} id='navigation-dropdown' + Editing {feedName}} + id='navigation-dropdown' onSelect={key => { switch (key) { case '1': @@ -110,7 +113,9 @@ export default class FeedInfoPanel extends Component { {/* Add entity dropdown */} - } + } id='add-entity-dropdown' onSelect={key => { console.log(key) @@ -130,6 +135,7 @@ export default class FeedInfoPanel extends Component { { let snapshot = this.props.feedSource.editorSnapshots.find(s => s.id === key) this.props.showConfirmModal({ diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 7406637a9..72eb1b927 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -42,13 +42,13 @@ export default class GtfsEditor extends Component { resetActiveEntity: PropTypes.func, deleteEntity: PropTypes.func, cloneEntity: PropTypes.func, - newEntityClicked: PropTypes.func, + newGtfsEntity: PropTypes.func, sidebarExpanded: PropTypes.bool, activeEntity: PropTypes.object, activeEntityId: PropTypes.string, - activeSubEntity: PropTypes.string, + subEntityId: PropTypes.string, activeSubSubEntity: PropTypes.string, activeComponent: PropTypes.string, subSubComponent: PropTypes.string @@ -92,9 +92,9 @@ export default class GtfsEditor extends Component { if (nextProps.subSubComponent && nextProps.activeSubSubEntity && !shallowEqual(nextProps.activeSubSubEntity, this.props.activeSubSubEntity)) { switch (nextProps.subSubComponent) { case 'timetable': - console.log(nextProps.activeSubEntity) + console.log(nextProps.subEntityId) console.log(nextProps.activeSubSubEntity) - let pattern = nextProps.activeEntity.tripPatterns.find(p => p.id === nextProps.activeSubEntity) + let pattern = nextProps.activeEntity.tripPatterns.find(p => p.id === nextProps.subEntityId) // fetch trips if they haven't been fetched if (!pattern[nextProps.activeSubSubEntity]) { this.props.fetchTripsForCalendar(nextProps.feedSource.id, pattern, nextProps.activeSubSubEntity) @@ -109,7 +109,7 @@ export default class GtfsEditor extends Component { } render () { - console.log(this.props.currentPattern && this.props.currentPattern.shape) + // console.log(this.props) const feedSource = this.props.feedSource const editingIsDisabled = this.props.feedSource ? !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') : true @@ -154,12 +154,12 @@ export default class GtfsEditor extends Component { right: 0, top: 0 }}> - {this.props.subSubComponent === 'timetable' && this.props.activeEntity + {this.props.subSubComponent === 'timetable' // && this.props.activeEntity ? this.showConfirmModal(props)} - activePatternId={this.props.activeSubEntity} + activePatternId={this.props.subEntityId} activeScheduleId={this.props.activeSubSubEntity} setActiveEntity={this.props.setActiveEntity} tableData={this.props.tableData} @@ -184,9 +184,10 @@ export default class GtfsEditor extends Component { cloneEntity={this.props.cloneEntity} updateActiveEntity={this.props.updateActiveEntity} deleteEntity={this.props.deleteEntity} - newEntityClicked={this.props.newEntityClicked} - entities={this.props.entities} + newGtfsEntity={this.props.newGtfsEntity} showConfirmModal={(props) => this.showConfirmModal(props)} + + entities={this.props.entities} activeEntityId={this.props.activeEntityId} activeComponent={this.props.activeComponent} feedSource={this.props.feedSource} @@ -208,7 +209,7 @@ export default class GtfsEditor extends Component { hidden={this.props.subSubComponent === 'timetable'} stops={this.props.tableData.stop || []} showConfirmModal={(props) => this.showConfirmModal(props)} - drawStops={this.props.mapState.zoom > 14} + drawStops={this.props.mapState.zoom > 14} // && (this.props.activeComponent === 'stop' || this.props.editSettings.addStops)} zoomToTarget={this.props.mapState.target} sidebarExpanded={this.props.sidebarExpanded} {...this.props} diff --git a/src/main/client/editor/components/PatternStopCard.js b/src/main/client/editor/components/PatternStopCard.js index fcd9bd2f2..c38785855 100644 --- a/src/main/client/editor/components/PatternStopCard.js +++ b/src/main/client/editor/components/PatternStopCard.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react' import { DragSource, DropTarget } from 'react-dnd' -import { Row, Col, Button, Collapse, Form, FormGroup, ControlLabel, Checkbox } from 'react-bootstrap' +import { Row, Col, Button, Collapse, FormGroup, ControlLabel, Checkbox } from 'react-bootstrap' import {Icon} from 'react-fa' import { getEntityName } from '../util/gtfs' @@ -84,11 +84,11 @@ export default class PatternStopCard extends Component { else this.props.setActiveStop(null) } render () { - const { isDragging, connectDragSource, connectDropTarget, stop, index, patternStop, cumulativeTravelTime, setActiveStop, updateActiveEntity, saveActiveEntity, activePattern } = this.props + const { isDragging, connectDragSource, connectDropTarget, stop, index, patternStop, cumulativeTravelTime, updateActiveEntity, saveActiveEntity, activePattern } = this.props const opacity = isDragging ? 0 : 1 let stopIsActive = this.props.activeStop === this.props.id let stopName = getEntityName('stop', stop) - let stopNameParts = stopName.split(/(\band\b|&|@|:)+/i) + let stopNameParts = stopName ? stopName.split(/(\band\b|&|@|:)+/i) : null let abbreviatedStopName = stopNameParts && stopNameParts.length === 3 && stop.stop_name.length > 20 ? `${stopNameParts[0].substr(0, 10).trim()}... ${stopNameParts[2].substr(0, 10).trim()}` : stop.stop_name diff --git a/src/main/client/editor/components/PatternStopContainer.js b/src/main/client/editor/components/PatternStopContainer.js index bd874c70c..c560c56a1 100644 --- a/src/main/client/editor/components/PatternStopContainer.js +++ b/src/main/client/editor/components/PatternStopContainer.js @@ -3,21 +3,11 @@ import update from 'react/lib/update' import { shallowEqual } from 'react-pure-render' import PatternStopCard from './PatternStopCard' import { DropTarget, DragDropContext } from 'react-dnd' -import Scrollzone from 'react-dnd-scrollzone' import HTML5Backend from 'react-dnd-html5-backend' -import { getEntityName } from '../util/gtfs' -import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import { polyline as getPolyline, getSegment } from '../../scenario-editor/utils/valhalla' -import ll from 'lonlng' - /* This component and its child components (Card.js) are based on the react-dnd sortable example found here: http://gaearon.github.io/react-dnd/examples-sortable-cancel-on-drop-outside.html */ -const scrollStyle = { - // overflowX: 'scroll', - overflowY: 'scroll', -} const cardTarget = { drop () { } @@ -28,7 +18,12 @@ const cardTarget = { })) export default class PatternStopContainer extends Component { static propTypes = { - connectDropTarget: PropTypes.func.isRequired + connectDropTarget: PropTypes.func.isRequired, + activePattern: PropTypes.object.isRequired, + updateActiveEntity: PropTypes.func, + saveActiveEntity: PropTypes.func, + stops: PropTypes.array, + cardStyle: PropTypes.object } constructor (props) { super(props) @@ -47,7 +42,7 @@ export default class PatternStopContainer extends Component { } } componentWillReceiveProps (nextProps) { - if (nextProps.activePattern.patternStops && !shallowEqual(nextProps.activePattern.patternStops, this.props.activePattern.patternStops)){ + if (nextProps.activePattern.patternStops && !shallowEqual(nextProps.activePattern.patternStops, this.props.activePattern.patternStops)) { this.setState({cards: nextProps.activePattern.patternStops.map(this.addUniqueId)}) } } @@ -59,7 +54,7 @@ export default class PatternStopContainer extends Component { this.props.saveActiveEntity('trippattern') } moveCard (id, atIndex) { - const { card, index } = this.findCard(id); + const { card, index } = this.findCard(id) this.setState(update(this.state, { cards: { $splice: [ @@ -67,7 +62,7 @@ export default class PatternStopContainer extends Component { [atIndex, 0, card] ] } - })); + })) } findCard (id) { @@ -87,9 +82,6 @@ export default class PatternStopContainer extends Component { let cumulativeTravelTime = 0 return connectDropTarget(
- { - /**/ - } {cards.map((card, i) => { if (!card) { return null @@ -117,9 +109,6 @@ export default class PatternStopContainer extends Component { /> ) })} - { - /**/ - }
) } diff --git a/src/main/client/editor/components/TripPatternList.js b/src/main/client/editor/components/TripPatternList.js index f34ee7722..d8c32b47d 100644 --- a/src/main/client/editor/components/TripPatternList.js +++ b/src/main/client/editor/components/TripPatternList.js @@ -1,32 +1,48 @@ import React, {Component, PropTypes} from 'react' import { Table, Button, ButtonGroup, Checkbox, DropdownButton, MenuItem, ButtonToolbar, Collapse, FormGroup, OverlayTrigger, Tooltip, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' import {Icon} from 'react-fa' +import { sentence as toSentenceCase } from 'change-case' +import Rcslider from 'rc-slider' import EditableTextField from '../../common/components/EditableTextField' -import { getConfigProperty } from '../../common/util/config' +import Loading from '../../common/components/Loading' import PatternStopContainer from './PatternStopContainer' import MinuteSecondInput from './MinuteSecondInput' -import { getEntityName } from '../util/gtfs' +import { CLICK_OPTIONS } from '../util' import VirtualizedEntitySelect from './VirtualizedEntitySelect' import { polyline as getPolyline, getSegment } from '../../scenario-editor/utils/valhalla' import ll from 'lonlng' +import 'rc-slider/assets/index.css' + const DEFAULT_SPEED = 20 // km/hr export default class TripPatternList extends Component { static propTypes = { stops: PropTypes.array, + updateActiveEntity: PropTypes.func.isRequired, saveActiveEntity: PropTypes.func.isRequired, - toggleEditSetting: PropTypes.func.isRequired, + updateEditSetting: PropTypes.func.isRequired, resetActiveEntity: PropTypes.func.isRequired, + setActiveEntity: PropTypes.func.isRequired, + cloneEntity: PropTypes.func.isRequired, + deleteEntity: PropTypes.func.isRequired, + newGtfsEntity: PropTypes.func.isRequired, + undoActiveTripPatternEdits: PropTypes.func, + + showConfirmModal: PropTypes.func, + editSettings: PropTypes.object, entity: PropTypes.object, activeEntity: PropTypes.object, feedSource: PropTypes.object, - activeComponent: PropTypes.string.isRequired, - activeSubEntity: PropTypes.string, + mapState: PropTypes.object, + + // activeComponent: PropTypes.string.isRequired, + // subSubComponent: PropTypes.string, + subEntityId: PropTypes.string, currentPattern: PropTypes.object } constructor (props) { @@ -77,10 +93,11 @@ export default class TripPatternList extends Component { this.props.saveActiveEntity('trippattern') } render () { + // console.log(this.props) const { feedSource, activeEntity } = this.props - const activePatternId = this.props.activeSubEntity + const activePatternId = this.props.subEntityId if (!activeEntity.tripPatterns) { - return + return } const activePattern = this.props.currentPattern // activeEntity.tripPatterns.find(p => p.id === activePatternId) const sidePadding = '5px' @@ -128,7 +145,6 @@ export default class TripPatternList extends Component { Use timetables, Use frequencies ] - const showTimetable = isActive && this.props.subSubComponent === 'timetable' const patternName = `${`${pattern.name.length > 35 ? pattern.name.substr(0, 35) + '...' : pattern.name}`} ${pattern.patternStops ? `(${pattern.patternStops.length} stops)` : ''}` return (

{isActive - ?

+ ?
{ @@ -174,110 +189,168 @@ export default class TripPatternList extends Component { {this.props.editSettings.editGeometry ? [ - - , - - , - - ] + , + , + + ] : [ - - , - - , - - ] + , + , + + ] } {this.props.editSettings.editGeometry ? - this.props.toggleEditSetting('followStreets')}> + Edit settings + this.props.updateEditSetting('followStreets', !this.props.editSettings.followStreets)}> Snap to streets - this.props.toggleEditSetting('snapToStops')}> + this.props.updateEditSetting('snapToStops', !this.props.editSettings.snapToStops)}> Snap to stops - this.props.toggleEditSetting('hideStops')}> + this.props.updateEditSetting('hideStops', !this.props.editSettings.hideStops)}> Show stops + Edit mode + this.props.updateEditSetting('onMapClick', evt.target.value)} + > + {CLICK_OPTIONS.map(v => { + return + })} + + {this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERVAL' + ?
+ this.props.updateEditSetting('stopInterval', value)} + step={25} + marks={{ + 100: '100m', + 400: 400m, + 800: '800m', + 1600: '1600m' + }} + tipFormatter={(value) => { + return `${value}m (${Math.round(value * 0.000621371 * 100) / 100}mi)` + }} + /> +
+ : this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERSECTIONS' + ?
+ {/* distance from intersection */} + this.props.updateEditSetting('distanceFromIntersection', evt.target.value)} + style={{width: '60px', marginTop: '10px'}} + /> + meters + {/* before/after intersection */} + this.props.updateEditSetting('afterIntersection', +evt.target.value)} + style={{width: '80px', marginTop: '10px'}} + > + + + + every + {/* every n intersections */} + this.props.updateEditSetting('intersectionStep', evt.target.value)} + style={{width: '55px', marginTop: '10px'}} + /> + intersections + + : null + }
: null } @@ -305,14 +378,10 @@ export default class TripPatternList extends Component {
@@ -322,7 +391,7 @@ export default class TripPatternList extends Component { >
) - const activeTable = getConfigProperty('modules.editor.spec') - .find(t => t.id === 'route') - return (
e.id === 'new') !== -1} onClick={() => { - this.props.newEntityClicked(feedSource.id, 'trippattern', {routeId: activeEntity.id, patternStops: [], name: 'New Pattern', feedId: this.props.feedSource.id, id: 'new'}, true) + this.props.newGtfsEntity(feedSource.id, 'trippattern', {routeId: activeEntity.id, patternStops: [], name: 'New Pattern', feedId: this.props.feedSource.id, id: 'new'}, true) }} > New pattern diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index 71876d7f1..953b867b0 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -1,41 +1,36 @@ import { connect } from 'react-redux' -import GtfsEditor from '../components/GtfsEditor' +import GtfsEditor from '../components/GtfsEditor' import { componentList } from '../util/gtfs' -import { fetchFeedSourceAndProject, fetchFeedVersion } from '../../manager/actions/feeds' +import { fetchFeedSourceAndProject } from '../../manager/actions/feeds' import { fetchFeedInfo } from '../actions/feedInfo' import { fetchStops, - fetchStopsForTripPattern, + fetchStopsForTripPattern } from '../actions/stop' import { - fetchRoutes -} from '../actions/route' -import { - fetchTripPatterns, fetchTripPatternsForRoute, undoActiveTripPatternEdits, updateControlPoint, addControlPoint, - removeControlPoint, + removeControlPoint } from '../actions/tripPattern' import { fetchTripsForCalendar, saveTripsForCalendar, - deleteTripsForCalendar, + deleteTripsForCalendar } from '../actions/trip' import { setActiveGtfsEntity, newGtfsEntity, cloneGtfsEntity, - toggleEditSetting, + updateEditSetting, updateMapSetting, saveActiveGtfsEntity, resetActiveGtfsEntity, deleteGtfsEntity, - settingActiveGtfsEntity, updateActiveGtfsEntity, clearGtfsContent, addGtfsRow, @@ -43,22 +38,21 @@ import { deleteGtfsRow, saveGtfsRow, getGtfsTable, - uploadGtfsFeed, - downloadGtfsFeed, - importGtfsFromGtfs, loadGtfsEntities, receiveGtfsEntities, uploadBrandingAsset } from '../actions/editor' import { updateUserMetadata } from '../../manager/actions/user' +import { findProjectByFeedSource } from '../../manager/util' const mapStateToProps = (state, ownProps) => { const feedSourceId = ownProps.routeParams.feedSourceId // location.pathname.split('/')[2] const activeComponent = ownProps.routeParams.subpage // location.pathname.split('/')[4] + // const { activeComponent, subComponent, subSubComponent, activeEntityId, subEntityId, activeSubSubEntity } = ownProps.routeParams.subpage // location.pathname.split('/')[4] const subComponent = ownProps.routeParams.subsubpage // location.pathname.split('/')[5] const subSubComponent = ownProps.routeParams.subsubcomponent // location.pathname.split('/')[6] const activeEntityId = ownProps.routeParams.entity // location.pathname.split('/')[7] - const activeSubEntity = ownProps.routeParams.subentity // location.pathname.split('/')[8] + const subEntityId = ownProps.routeParams.subentity // location.pathname.split('/')[8] const activeSubSubEntity = ownProps.routeParams.subsubentity // location.pathname.split('/')[9] const activeEntity = state.editor.active && state.editor.active.entity && state.editor.active.entity.id === activeEntityId @@ -67,7 +61,7 @@ const mapStateToProps = (state, ownProps) => { ? state.editor.active.entity : null const currentPattern = state.editor.active && state.editor.active.subEntity - // const activeSubEntity = state.editor.active && state.editor.active.subEntity && state.editor.active.subEntity.id === activeEntityId + // const subEntityId = state.editor.active && state.editor.active.subEntity && state.editor.active.subEntity.id === activeEntityId // ? state.editor.active.subEntity // : state.editor.active && state.editor.active.subEntity && activeComponent === 'feedinfo' // ? state.editor.active.subEntity @@ -90,19 +84,10 @@ const mapStateToProps = (state, ownProps) => { let user = state.user // find the containing project - let project = state.projects.all - ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) - : null - - let feedSource - if (project) { - feedSource = project.feedSources.find(fs => fs.id === feedSourceId) - } + const project = findProjectByFeedSource(state, feedSourceId) + const feedSource = project && project.feedSources.find(fs => fs.id === feedSourceId) - let feedInfo = state.editor.tableData.feedinfo + const feedInfo = state.editor.tableData.feedinfo return { tableData: state.editor.tableData, @@ -123,9 +108,9 @@ const mapStateToProps = (state, ownProps) => { subComponent, activeEntity, activeEntityId, - activeSubEntity, + subEntityId, currentPattern, - // activeSubEntityId, + // subEntityIdId, activeSubSubEntity, editSettings, mapState, @@ -141,7 +126,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { const subComponent = ownProps.routeParams.subsubpage const subSubComponent = ownProps.routeParams.subsubcomponent const activeEntityId = ownProps.routeParams.entity - const activeSubEntity = ownProps.routeParams.subentity + const subEntityId = ownProps.routeParams.subentity const activeSubSubEntity = ownProps.routeParams.subsubentity return { @@ -165,28 +150,26 @@ const mapDispatchToProps = (dispatch, ownProps) => { .then(() => { if (componentList.indexOf(activeComponent) !== -1) { dispatch(getGtfsTable(activeComponent, feedSourceId)) - //// FETCH trip patterns if route selected + // FETCH trip patterns if route selected .then((entities) => { if (activeEntityId === 'new') { dispatch(newGtfsEntity(feedSourceId, activeComponent)) - } - else if (activeEntityId && entities.findIndex(e => e.id === activeEntityId) === -1) { + } else if (activeEntityId && entities.findIndex(e => e.id === activeEntityId) === -1) { console.log('bad entity id, going back to ' + activeComponent) return dispatch(setActiveGtfsEntity(feedSourceId, activeComponent)) } - dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, activeEntityId, subComponent, activeSubEntity, subSubComponent, activeSubSubEntity)) + dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, activeEntityId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) if (activeComponent === 'route' && activeEntityId) { dispatch(fetchTripPatternsForRoute(feedSourceId, activeEntityId)) .then((tripPatterns) => { - let pattern = tripPatterns && tripPatterns.find(p => p.id === activeSubEntity) + let pattern = tripPatterns && tripPatterns.find(p => p.id === subEntityId) if (subSubComponent === 'timetable' && activeSubSubEntity) { dispatch(fetchTripsForCalendar(feedSourceId, pattern, activeSubSubEntity)) } }) } }) - } - else { + } else { dispatch(setActiveGtfsEntity(feedSourceId)) } }) @@ -199,16 +182,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { } } - // Clear gtfs content if no active component - // if (!activeComponent) { - // dispatch(clearGtfsContent()) - // } - - // otherwise, get active table - // else { - // - // } - dispatch(fetchTripPatterns(feedSourceId)) + // TODO: replace fetch trip patterns with map layer + // dispatch(fetchTripPatterns(feedSourceId)) }, onComponentUpdate: (prevProps, newProps) => { // handle back button presses by re-setting active gtfs entity @@ -216,14 +191,16 @@ const mapDispatchToProps = (dispatch, ownProps) => { (prevProps.activeComponent !== newProps.activeComponent || prevProps.activeEntityId !== newProps.activeEntityId || prevProps.subComponent !== newProps.subComponent || - prevProps.activeSubEntity !== newProps.activeSubEntity || + prevProps.subEntityId !== newProps.subEntityId || prevProps.subSubComponent !== newProps.subSubComponent || prevProps.activeSubSubEntity !== newProps.activeSubSubEntity) ) { console.log('handling back button') - dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, activeEntityId, subComponent, activeSubEntity, subSubComponent, activeSubSubEntity)) + dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, activeEntityId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) } }, + + // OLD ROW FUNCTIONS newRowClicked: (tableId) => { dispatch(addGtfsRow(tableId)) }, @@ -240,10 +217,12 @@ const mapDispatchToProps = (dispatch, ownProps) => { dispatch(updateGtfsField(tableId, rowIndex, fieldName, newValue)) }, newRowsDisplayed: (tableId, rows, feedSource) => { - if(feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) + if (feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) }, - toggleEditSetting: (setting) => { - dispatch(toggleEditSetting(setting)) + + // NEW GENERIC GTFS/EDITOR FUNCTIONS + updateEditSetting: (setting, value) => { + dispatch(updateEditSetting(setting, value)) }, updateMapSetting: (props) => { dispatch(updateMapSetting(props)) @@ -251,9 +230,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { gtfsEntitySelected: (type, entity) => { dispatch(receiveGtfsEntities([entity])) }, - uploadBrandingAsset: (feedSourceId, entityId, component, file) => { - dispatch(uploadBrandingAsset(feedSourceId, entityId, component, file)) - }, setActiveEntity: (feedSourceId, component, entity, subComponent, subEntity, subSubComponent, subSubEntity) => { let entityId = entity && entity.id let subEntityId = subEntity && subEntity.id @@ -271,9 +247,17 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, saveActiveEntity: (component) => { return dispatch(saveActiveGtfsEntity(component)) - // .then(entity => { - // // dispatch(setActiveGtfsEntity(feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId)) - // }) + }, + cloneEntity: (feedSourceId, component, entityId, save) => { + dispatch(cloneGtfsEntity(feedSourceId, component, entityId, save)) + }, + newGtfsEntity: (feedSourceId, component, props, save) => { + return dispatch(newGtfsEntity(feedSourceId, component, props, save)) + }, + + // ENTITY-SPECIFIC FUNCTIONS + uploadBrandingAsset: (feedSourceId, entityId, component, file) => { + dispatch(uploadBrandingAsset(feedSourceId, entityId, component, file)) }, saveTripsForCalendar: (feedSourceId, pattern, calendarId, trips) => { return dispatch(saveTripsForCalendar(feedSourceId, pattern, calendarId, trips)) @@ -281,29 +265,18 @@ const mapDispatchToProps = (dispatch, ownProps) => { deleteTripsForCalendar: (feedSourceId, pattern, calendarId, trips) => { return dispatch(deleteTripsForCalendar(feedSourceId, pattern, calendarId, trips)) }, - cloneEntity: (feedSourceId, component, entityId, save) => { - dispatch(cloneGtfsEntity(feedSourceId, component, entityId, save)) - }, - newEntityClicked: (feedSourceId, component, props, save) => { - dispatch(newGtfsEntity(feedSourceId, component, props, save)) - }, + clearGtfsContent: () => { dispatch(clearGtfsContent()) }, + fetchTripPatternsForRoute: (feedSourceId, routeId) => { dispatch(fetchTripPatternsForRoute(feedSourceId, routeId)) }, + fetchStopsForTripPattern: (feedSourceId, tripPatternId) => { dispatch(fetchStopsForTripPattern(feedSourceId, tripPatternId)) }, + fetchStops: (feedSourceId) => { dispatch(fetchStops(feedSourceId)) }, + fetchTripsForCalendar: (feedSourceId, pattern, calendarId) => { dispatch(fetchTripsForCalendar(feedSourceId, pattern, calendarId)) }, + + // TRIP PATTERN EDIT FUNCTIONS undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, addControlPoint: (controlPoint, index) => { dispatch(addControlPoint(controlPoint, index)) }, removeControlPoint: (index) => { dispatch(removeControlPoint(index)) }, - updateControlPoint: (index, point, distance) => { dispatch(updateControlPoint(index, point, distance)) }, - fetchTripPatternsForRoute: (feedSourceId, routeId) => { - dispatch(fetchTripPatternsForRoute(feedSourceId, routeId)) - }, - fetchStopsForTripPattern: (feedSourceId, tripPatternId) => { - dispatch(fetchStopsForTripPattern(feedSourceId, tripPatternId)) - }, - fetchStops: (feedSourceId) => { - dispatch(fetchStops(feedSourceId)) - }, - fetchTripsForCalendar: (feedSourceId, pattern, calendarId) => { - dispatch(fetchTripsForCalendar(feedSourceId, pattern, calendarId)) - }, + updateControlPoint: (index, point, distance) => { dispatch(updateControlPoint(index, point, distance)) } } } diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js new file mode 100644 index 000000000..3d2f43d2b --- /dev/null +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -0,0 +1,86 @@ +import { connect } from 'react-redux' +import { + setActiveGtfsEntity, + newGtfsEntity, + saveActiveGtfsEntity, + deleteGtfsEntity, + updateEditSetting, + updateMapSetting, + updateActiveGtfsEntity, + resetActiveGtfsEntity, + cloneGtfsEntity, + undoActiveTripPatternEdits +} from '../actions/editor' +import { findProjectByFeedSource } from '../../manager/util' + +import TripPatternList from '../components/TripPatternList' + +const mapStateToProps = (state, ownProps) => { + const mapState = state.editor.mapState + const editSettings = state.editor.editSettings + const stops = state.editor.tableData.stop + const currentPattern = state.editor.active.subEntity + + const feedSourceId = state.editor.active.feedSourceId + // find the containing project + const project = findProjectByFeedSource(state, feedSourceId) + const feedSource = project && project.feedSources.find(fs => fs.id === feedSourceId) + + const activeEntity = state.editor.active.entity + // const subSubComponent = state.editor.active.subSubComponent + const subEntityId = state.editor.active.subEntityId + + return { + mapState, + editSettings, + stops, + currentPattern, + feedSource, + activeEntity, + // subSubComponent, + subEntityId + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + + // NEW GENERIC GTFS/EDITOR FUNCTIONS + updateEditSetting: (setting, value) => { + dispatch(updateEditSetting(setting, value)) + }, + updateMapSetting: (props) => { + dispatch(updateMapSetting(props)) + }, + setActiveEntity: (feedSourceId, component, entity, subComponent, subEntity, subSubComponent, subSubEntity) => { + let entityId = entity && entity.id + let subEntityId = subEntity && subEntity.id + let subSubEntityId = subSubEntity && subSubEntity.id + dispatch(setActiveGtfsEntity(feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId)) + }, + updateActiveEntity: (entity, component, props) => { + dispatch(updateActiveGtfsEntity(entity, component, props)) + }, + resetActiveEntity: (entity, component) => { + dispatch(resetActiveGtfsEntity(entity, component)) + }, + deleteEntity: (feedSourceId, component, entityId, routeId) => { + dispatch(deleteGtfsEntity(feedSourceId, component, entityId, routeId)) + }, + saveActiveEntity: (component) => { + return dispatch(saveActiveGtfsEntity(component)) + }, + cloneEntity: (feedSourceId, component, entityId, save) => { + dispatch(cloneGtfsEntity(feedSourceId, component, entityId, save)) + }, + newGtfsEntity: (feedSourceId, component, props, save) => { + return dispatch(newGtfsEntity(feedSourceId, component, props, save)) + }, + + undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, + } +} + +const ActiveTripPatternList = connect(mapStateToProps, mapDispatchToProps)(TripPatternList) + +export default ActiveTripPatternList diff --git a/src/main/client/editor/reducers/editor.js b/src/main/client/editor/reducers/editor.js index cfbabb003..a0402b97b 100644 --- a/src/main/client/editor/reducers/editor.js +++ b/src/main/client/editor/reducers/editor.js @@ -1,40 +1,22 @@ import update from 'react-addons-update' import rbush from 'rbush' +import clone from 'clone' import polyUtil from 'polyline-encoded' -import { getControlPoints, getEntityBounds } from '../util/gtfs' +import { getControlPoints, getEntityBounds, stopToGtfs, agencyToGtfs, gtfsSort } from '../util/gtfs' import { latLngBounds } from 'leaflet' +import { CLICK_OPTIONS } from '../util' -const mapStop = (s) => { - return { - // datatools props - id: s.id, - feedId: s.feedId, - bikeParking: s.bikeParking, - carParking: s.carParking, - pickupType: s.pickupType, - dropOffType: s.dropOffType, - - // gtfs spec props - stop_code: s.stopCode, - stop_name: s.stopName, - stop_desc: s.stopDesc, - stop_lat: s.lat, - stop_lon: s.lon, - zone_id: s.zoneId, - stop_url: s.stopUrl, - location_type: s.locationType, - parent_station: s.parentStation, - stop_timezone: s.stopTimezone, - wheelchair_boarding: s.wheelchairBoarding, - stop_id: s.gtfsStopId - } -} const defaultState = { feedSourceId: null, active: {}, editSettings: { editGeometry: false, followStreets: true, + onMapClick: CLICK_OPTIONS[0], + stopInterval: 400, + distanceFromIntersection: 5, + afterIntersection: true, + intersectionStep: 2, snapToStops: true, addStops: false, hideStops: false, @@ -70,19 +52,19 @@ const editor = (state = defaultState, action) => { return update(state, { mapState: {$set: mapState} }) - case 'TOGGLE_EDIT_SETTING': + case 'UPDATE_EDIT_SETTING': if (action.setting === 'editGeometry' && !state.editSettings.editGeometry) { controlPoints = getControlPoints(state.active.subEntity, state.editSettings.snapToStops) return update(state, { editSettings: { - [action.setting]: {$set: !state.editSettings[action.setting]}, + [action.setting]: {$set: action.value}, controlPoints: {$set: [controlPoints]} } }) } else { return update(state, { editSettings: { - [action.setting]: {$set: !state.editSettings[action.setting]} + [action.setting]: {$set: action.value} } }) } @@ -175,12 +157,11 @@ const editor = (state = defaultState, action) => { return update(newState || state, { tableData: {route: {[routeIndex]: {tripPatterns: {$unshift: [activeEntity]}}}}, active: { - entity: {tripPatterns: {$unshift: [activeEntity]}}, + entity: {tripPatterns: {$unshift: [activeEntity]}} // edited: {$set: typeof action.props !== 'undefined'} } }) - } - else { + } else { activeEntity = { isCreating: true, name: '', @@ -189,13 +170,13 @@ const editor = (state = defaultState, action) => { ...action.props } // if tableData's component array is undefined, add it - if(!state.tableData[action.component]) { + if (!state.tableData[action.component]) { newState = update(state, { tableData: {[action.component]: {$set: []}} }) } return update(newState || state, { - tableData: {[action.component]: {$unshift: [activeEntity]}}, + tableData: {[action.component]: {$unshift: [activeEntity]}} // active: { // entity: {$set: activeEntity}, // edited: {$set: typeof action.props !== 'undefined'} @@ -204,13 +185,13 @@ const editor = (state = defaultState, action) => { } case 'SETTING_ACTIVE_GTFS_ENTITY': activeEntity = action.component === 'feedinfo' - ? Object.assign({}, state.tableData[action.component]) + ? clone(state.tableData[action.component]) : state.tableData[action.component] && action.entityId - ? Object.assign({}, state.tableData[action.component].find(e => e.id === action.entityId)) + ? clone(state.tableData[action.component].find(e => e.id === action.entityId)) : null switch (action.subComponent) { case 'trippattern': - activeSubEntity = activeEntity && activeEntity.tripPatterns ? Object.assign({}, activeEntity.tripPatterns.find(p => p.id === action.subEntityId)) : null + activeSubEntity = activeEntity && activeEntity.tripPatterns ? clone(activeEntity.tripPatterns.find(p => p.id === action.subEntityId)) : null controlPoints = getControlPoints(activeSubEntity, state.editSettings.snapToStops) coordinates = activeSubEntity && activeSubEntity.shape && activeSubEntity.shape.coordinates break @@ -224,13 +205,13 @@ const editor = (state = defaultState, action) => { component: action.component, subComponent: action.subComponent, subSubComponent: action.subSubComponent, - edited: activeEntity && activeEntity.id === 'new', + edited: activeEntity && activeEntity.id === 'new' } stateUpdate = { editSettings: { - controlPoints: {$set: controlPoints}, + controlPoints: {$set: controlPoints} }, - active: {$set: active}, + active: {$set: active} } if (coordinates) { stateUpdate.coordinatesHistory = {$set: [coordinates]} @@ -239,12 +220,10 @@ const editor = (state = defaultState, action) => { case 'RESET_ACTIVE_GTFS_ENTITY': switch (action.component) { case 'trippattern': - routeIndex = state.tableData.route.findIndex(r => r.id === action.entity.routeId) - patternIndex = state.tableData.route[routeIndex].tripPatterns.findIndex(p => p.id === action.entity.id) - activeEntity = Object.assign({}, state.tableData.route[routeIndex].tripPatterns[patternIndex]) + patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) + activeEntity = Object.assign({}, state.active.entity.tripPatterns[patternIndex]) return update(state, { active: { - // entity: {tripPatterns: {[patternIndex]: {$set: activeEntity}}}, subEntity: {$set: activeEntity}, patternEdited: {$set: false} } @@ -263,23 +242,46 @@ const editor = (state = defaultState, action) => { active: { entity: {$set: activeEntity}, edited: {$set: false} - }, + } }) } case 'SAVED_TRIP_PATTERN': - if (action.tripPattern.id == state.active.subEntityId) { + routeIndex = state.tableData.route.findIndex(r => r.id === action.tripPattern.routeId) + patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.tripPattern.id) + stateUpdate = {active: {}} + + // if pattern is active + if (action.tripPattern.id === state.active.subEntityId) { + // set controlPoints initially and then whenever isSnappingToStops changes + controlPoints = getControlPoints(action.tripPattern, state.editSettings.snapToStops) + stateUpdate = { active: { + subEntity: {$set: action.tripPattern}, patternEdited: {$set: false} - } + }, + editSettings: {controlPoints: {$set: [controlPoints]}} } - return update(state, stateUpdate) } + // if not active, but present in active route + if (patternIndex !== -1) { + stateUpdate.tableData = {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}}}} + stateUpdate.active.entity = {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}} + } else if (action.tripPattern.routeId === state.active.entity.id) { // if pattern is entirely new + const patterns = clone(state.active.entity.tripPatterns) + patterns.push(action.tripPattern) + return update(state, { + tableData: {route: {[routeIndex]: {tripPatterns: {$push: [action.tripPattern]}}}}, + // active: {entity: {tripPatterns: {$push: [action.tripPattern]}}} + active: {entity: {tripPatterns: {$set: patterns}}} + }) + } + return update(state, stateUpdate) case 'UPDATE_ACTIVE_GTFS_ENTITY': switch (action.component) { case 'trippattern': patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) - activeEntity = Object.assign({}, state.active.entity.tripPatterns[patternIndex]) + activeEntity = Object.assign({}, state.active.subEntity) for (key in action.props) { activeEntity[key] = action.props[key] } @@ -293,9 +295,9 @@ const editor = (state = defaultState, action) => { if (action.props && 'shape' in action.props) { // add previous coordinates to history // stateUpdate.editSettings = {coordinatesHistory: {$push: [action.props.shape.coordinates]}} - coordinates = state.active.entity.tripPatterns[patternIndex].shape && state.active.entity.tripPatterns[patternIndex].shape.coordinates + coordinates = state.active.subEntity.shape && state.active.subEntity.shape.coordinates if (coordinates) - stateUpdate.editSettings = {coordinatesHistory: {$push: [state.active.entity.tripPatterns[patternIndex].shape.coordinates]}} + stateUpdate.editSettings = {coordinatesHistory: {$push: [coordinates]}} } return update(state, stateUpdate) // case 'feedinfo': @@ -323,25 +325,9 @@ const editor = (state = defaultState, action) => { }) } case 'RECEIVE_AGENCIES': - const agencies = action.agencies.map(ent => { - return { - // datatools props - id: ent.id, - feedId: ent.feedId, - agencyBrandingUrl: ent.agencyBrandingUrl, - - // gtfs spec props - agency_id: ent.agencyId, - agency_name: ent.name, - agency_url: ent.url, - agency_timezone: ent.timezone, - agency_lang: ent.lang, - agency_phone: ent.phone, - agency_fare_url: ent.agencyFareUrl, - agency_email: ent.email, - } - }) - agencyIndex = state.active.entity && action.agencies.findIndex(a => a.id === state.active.entity.id) + const agencies = action.agencies.map(agencyToGtfs) + agencies.sort(gtfsSort) + agencyIndex = state.active.entity && agencies.findIndex(a => a.id === state.active.entity.id) if (agencyIndex !== -1) { return update(state, { tableData: {agency: {$set: agencies}}, @@ -373,7 +359,8 @@ const editor = (state = defaultState, action) => { transfer_duration: fare.transferDuration, } }) - fareIndex = state.active.entity && action.fares.findIndex(f => f.id === state.active.entity.id) + fares.sort(gtfsSort) + fareIndex = state.active.entity && fares.findIndex(f => f.id === state.active.entity.id) if (fareIndex !== -1) { return update(state, { tableData: {fare: {$set: fares}}, @@ -390,21 +377,21 @@ const editor = (state = defaultState, action) => { case 'RECEIVE_FEED_INFO': const feedInfo = action.feedInfo ? { - // datatools props - id: action.feedInfo.id, - color: action.feedInfo.color, - defaultLat: action.feedInfo.defaultLat, - defaultLon: action.feedInfo.defaultLon, - defaultRouteType: action.feedInfo.defaultRouteType, + // datatools props + id: action.feedInfo.id, + color: action.feedInfo.color, + defaultLat: action.feedInfo.defaultLat, + defaultLon: action.feedInfo.defaultLon, + defaultRouteType: action.feedInfo.defaultRouteType, - // gtfs spec props - feed_end_date: action.feedInfo.feedEndDate, - feed_start_date: action.feedInfo.feedStartDate, - feed_lang: action.feedInfo.feedLang, - feed_publisher_name: action.feedInfo.feedPublisherName, - feed_publisher_url: action.feedInfo.feedPublisherUrl, - feed_version: action.feedInfo.feedVersion, - } + // gtfs spec props + feed_end_date: action.feedInfo.feedEndDate, + feed_start_date: action.feedInfo.feedStartDate, + feed_lang: action.feedInfo.feedLang, + feed_publisher_name: action.feedInfo.feedPublisherName, + feed_publisher_url: action.feedInfo.feedPublisherUrl, + feed_version: action.feedInfo.feedVersion + } : null let mapState = {...state.mapState} if (feedInfo && feedInfo.defaultLon && feedInfo.defaultLat) { @@ -448,7 +435,8 @@ const editor = (state = defaultState, action) => { end_date: c.endDate, } }) : null - calendarIndex = state.active.entity && action.calendars.findIndex(c => c.id === state.active.entity.id) + calendars.sort(gtfsSort) + calendarIndex = state.active.entity && calendars.findIndex(c => c.id === state.active.entity.id) if (calendarIndex !== -1) { return update(state, { tableData: {calendar: {$set: calendars}}, @@ -494,9 +482,6 @@ const editor = (state = defaultState, action) => { }) } case 'RECEIVE_ROUTES': - // feedTableData = state.tableData[action.feedId] - // if (!feedTableData) - // feedTableData = {} const routes = action.routes ? action.routes.map(r => { return { // datatools props @@ -518,8 +503,9 @@ const editor = (state = defaultState, action) => { route_id: r.gtfsRouteId } }) : [] + routes.sort(gtfsSort) // feedTableData.route = routes - routeIndex = state.active.entity && action.routes.findIndex(r => r.id === state.active.entity.id) + routeIndex = state.active.entity && routes.findIndex(r => r.id === state.active.entity.id) if (routeIndex !== -1) { let followStreets = routes[routeIndex] ? routes[routeIndex].route_type === 3 || routes[routeIndex].route_type === 0 : true return update(state, { @@ -557,8 +543,7 @@ const editor = (state = defaultState, action) => { // set controlPoints initially and then whenever isSnappingToStops changes if (activePattern) { controlPoints = getControlPoints(activePattern, state.editSettings.snapToStops) - } - else { + } else { controlPoints = [] } if (state.active.entity.id === action.routeId) { @@ -605,10 +590,11 @@ const editor = (state = defaultState, action) => { // }) // } case 'RECEIVE_STOPS': - const stops = action.stops ? action.stops.map(mapStop) : [] - var tree = rbush(9, ['[0]', '[1]', '[0]', '[1]']) + const stops = action.stops ? action.stops.map(stopToGtfs) : [] + stops.sort(gtfsSort) + const tree = rbush(9, ['[0]', '[1]', '[0]', '[1]']) tree.load(stops.map(s => ([s.stop_lon, s.stop_lat, s]))) - stopIndex = state.active.entity && action.stops.findIndex(s => s.id === state.active.entity.id) + stopIndex = state.active.entity && stops.findIndex(s => s.id === state.active.entity.id) if (stopIndex !== -1) { return update(state, { tableData: {stop: {$set: stops}}, @@ -625,28 +611,28 @@ const editor = (state = defaultState, action) => { }) } case 'RECEIVE_STOP': - const stop = mapStop(action.stop) - console.log(stop) + const stop = stopToGtfs(action.stop) let stopIndex = state.tableData.stop.findIndex(s => s.id === stop.id) + // TODO: handle adding to rbush tree + // TODO: updated sort with stops array + // if stop is active, update active entity if (stop.id === state.active.entityId && stopIndex !== -1) { stateUpdate = { - tableData: {stop: {[stopIndex]: {$merge: stop}}}, - active: {entity: {$set: stop}}, - edited: {$set: false}, + tableData: {stop: {[stopIndex]: {$set: stop}}}, + active: { + entity: {$set: stop}, + edited: {$set: false} + } } - } - else if (stopIndex === -1) { + } else if (stopIndex === -1) { stateUpdate = { - tableData: {stop: {$unshift: [stop]}}, - edited: {$set: false}, + tableData: {stop: {$push: [stop]}} } - } - else { + } else { stateUpdate = { - tableData: {stop: {[stopIndex]: {$merge: stop}}}, - edited: {$set: false}, + tableData: {stop: {[stopIndex]: {$set: stop}}} } } return update(state, stateUpdate) @@ -689,7 +675,7 @@ const editor = (state = defaultState, action) => { }) return mappedEntities case 'stop': - return action.entities.map(mapStop) + return action.entities.map(stopToGtfs) case 'calendar': return action.entities.map(ent => { return { diff --git a/src/main/client/editor/util/gtfs.js b/src/main/client/editor/util/gtfs.js index 0801febea..f77dc3d49 100644 --- a/src/main/client/editor/util/gtfs.js +++ b/src/main/client/editor/util/gtfs.js @@ -1,12 +1,15 @@ import along from 'turf-along' import lineDistance from 'turf-line-distance' import { latLngBounds } from 'leaflet' -import { extent } from 'turf-extent' export const componentList = ['route', 'stop', 'fare', 'feedinfo', 'calendar', 'scheduleexception', 'agency'] export const subComponentList = ['trippattern'] export const subSubComponentList = ['timetable'] +export const isNew = (entity) => { + return entity.id === 'new' || typeof entity.id === 'undefined' +} + export const getEntityBounds = (entity, offset = 0.005) => { if (!entity) return null @@ -41,36 +44,38 @@ export const getEntityName = (component, entity) => { if (!entity) { return '[Unnamed]' } - let entName = component === 'agency' - ? 'agency_name' - : component === 'route' + let nameKey = + 'route_id' in entity ? 'route_short_name' - : component === 'stop' + : 'agency_name' in entity + ? 'agency_name' + : 'stop_id' in entity ? 'stop_name' - : component === 'calendar' + : 'service_id' in entity ? 'description' - : component === 'fare' + : 'fare_id' in entity ? 'fare_id' - : component === 'scheduleexception' + : 'exemplar' in entity // schedule exception ? 'name' : null - switch (component) { - case 'stop': + // if (nameKey !== 'stop_name') console.log(nameKey) + switch (nameKey) { + case 'stop_name': return entity.stop_name && entity.stop_code ? `${entity.stop_name} (${entity.stop_code})` : entity.stop_name && entity.stop_id ? `${entity.stop_name} (${entity.stop_id})` - : entity.stop_name - case 'route': + : entity.stop_name || '[no name]' + case 'route_short_name': return entity.route_short_name && entity.route_long_name && entity.route_short_name !== '""' && entity.route_long_name !== '""' ? `${entity.route_short_name} - ${entity.route_long_name}` : entity.route_short_name && entity.route_short_name !== '""' ? entity.route_short_name : entity.route_long_name && entity.route_long_name !== '""' ? entity.route_long_name - : entity.route_id + : entity.route_id || '[no name]' default: - return entity[entName] + return entity[nameKey] || '[no name]' } } @@ -173,3 +178,87 @@ export function getRouteName (route) { return name } + +export const stopToGtfs = (s) => { + return { + // datatools props + id: s.id, + feedId: s.feedId, + bikeParking: s.bikeParking, + carParking: s.carParking, + pickupType: s.pickupType, + dropOffType: s.dropOffType, + + // gtfs spec props + stop_code: s.stopCode, + stop_name: s.stopName, + stop_desc: s.stopDesc, + stop_lat: s.lat, + stop_lon: s.lon, + zone_id: s.zoneId, + stop_url: s.stopUrl, + location_type: s.locationType, + parent_station: s.parentStation, + stop_timezone: s.stopTimezone, + wheelchair_boarding: s.wheelchairBoarding, + stop_id: s.gtfsStopId + } +} + +export const stopFromGtfs = (stop) => { + return { + gtfsStopId: stop.stop_id, + stopCode: stop.stop_code, + stopName: stop.stop_name, + stopDesc: stop.stop_desc, + lat: stop.stop_lat, + lon: stop.stop_lon, + zoneId: stop.zone_id, + stopUrl: stop.stop_url, + locationType: stop.location_type, + parentStation: stop.parent_station, + stopTimezone: stop.stop_timezone, + wheelchairBoarding: stop.wheelchair_boarding, + bikeParking: stop.bikeParking, + carParking: stop.carParking, + pickupType: stop.pickupType, + dropOffType: stop.dropOffType, + feedId: stop.feedId, + id: isNew(stop) ? null : stop.id, + } +} + +export const agencyToGtfs = ent => { + return { + // datatools props + id: ent.id, + feedId: ent.feedId, + agencyBrandingUrl: ent.agencyBrandingUrl, + + // gtfs spec props + agency_id: ent.agencyId, + agency_name: ent.name, + agency_url: ent.url, + agency_timezone: ent.timezone, + agency_lang: ent.lang, + agency_phone: ent.phone, + agency_fare_url: ent.agencyFareUrl, + agency_email: ent.email + } +} + +export const gtfsSort = (a, b) => { + const radix = 10 + var aName = getEntityName(null, a) + var bName = getEntityName(null, b) + if (a.isCreating && !b.isCreating) return -1 + if (!a.isCreating && b.isCreating) return 1 + if (!isNaN(parseInt(aName, radix)) && !isNaN(parseInt(bName, radix)) && !isNaN(+aName) && !isNaN(+bName)) { + if (parseInt(aName, radix) < parseInt(bName, radix)) return -1 + if (parseInt(aName, radix) > parseInt(bName, radix)) return 1 + return 0 + } + if (aName.toLowerCase() < bName.toLowerCase()) return -1 + if (aName.toLowerCase() > bName.toLowerCase()) return 1 + return 0 +} diff --git a/src/main/client/editor/util/index.js b/src/main/client/editor/util/index.js new file mode 100644 index 000000000..a64c29f8a --- /dev/null +++ b/src/main/client/editor/util/index.js @@ -0,0 +1 @@ +export const CLICK_OPTIONS = ['DRAG_HANDLES', 'ADD_STOP_AT_CLICK', 'ADD_STOPS_AT_INTERVAL', 'ADD_STOPS_AT_INTERSECTIONS'] diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java index a101f3091..6ae0455af 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java @@ -140,7 +140,7 @@ public static Object createStop(Request req, Response res) { halt(400); if (!VersionedDataStore.agencyExists(stop.feedId)) { - halt(400); + halt(400, "Stop must reference feed source ID"); } tx = VersionedDataStore.getFeedTx(stop.feedId); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 7f9da407a..4ba6b2cbf 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -114,6 +114,13 @@ public static Object createTripPattern(Request req, Response res) { return null; } + /** + * Update existing trip pattern. NOTE: function assumes only one stop has been + * changed (added, modified, removed) + * @param req + * @param res + * @return + */ public static Object updateTripPattern(Request req, Response res) { TripPattern tripPattern; FeedTx tx = null; From 6db9d6034bee7e59b968f450c95426d000f97419 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 10:47:55 -0400 Subject: [PATCH 018/323] missing files from previous commits --- .../reporter/components/DateTimeFilter.js | 100 ++++++++++++++++++ .../containers/ActiveDateTimeFilter.js | 29 +++++ src/main/client/manager/util/index.js | 10 ++ .../client/scenario-editor/utils/reverse.js | 28 +++++ 4 files changed, 167 insertions(+) create mode 100644 src/main/client/manager/components/reporter/components/DateTimeFilter.js create mode 100644 src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js create mode 100644 src/main/client/manager/util/index.js create mode 100644 src/main/client/scenario-editor/utils/reverse.js diff --git a/src/main/client/manager/components/reporter/components/DateTimeFilter.js b/src/main/client/manager/components/reporter/components/DateTimeFilter.js new file mode 100644 index 000000000..1e8265fa8 --- /dev/null +++ b/src/main/client/manager/components/reporter/components/DateTimeFilter.js @@ -0,0 +1,100 @@ +import React, { Component, PropTypes } from 'react' +import { Row, Col, FormControl, Alert } from 'react-bootstrap' +import moment from 'moment' +import DateTimeField from 'react-bootstrap-datetimepicker' + +const timeOptions = [ + { + label: 'Morning peak (6am - 9am)', + from: 60 * 60 * 6, + to: 60 * 60 * 9 + }, + { + label: 'Midday peak (11am - 2pm)', + from: 60 * 60 * 11, + to: 60 * 60 * 14 + }, + { + label: 'Afternoon peak (4pm - 7pm)', + from: 60 * 60 * 16, + to: 60 * 60 * 19 + }, + { + label: 'Evening service (7pm - 10pm)', + from: 60 * 60 * 19, + to: 60 * 60 * 22 + }, + { + label: '24 hours (12am - 11:59pm)', + from: 0, + to: 60 * 60 * 24 - 1 // 86399 + } +] + +export default class DateTimeFilter extends Component { + static propTypes = { + onChange: PropTypes.func, + dateTime: PropTypes.object, + version: PropTypes.object, + + updateDateTimeFilter: PropTypes.func + } + render () { + const dateTime = this.props.dateTime || {date: null, from: null, to: null} + let dateTimeProps = { + mode: 'date', + dateTime: dateTime.date ? +moment(dateTime.date, 'YYYY-MM-DD') : +moment(), + onChange: (millis) => { + console.log(+millis) + let date = moment(+millis).format('YYYY-MM-DD') + console.log(date) + this.props.updateDateTimeFilter({date}) + this.props.onChange && this.props.onChange({date}) + } + } + if (!dateTime.date) { + dateTimeProps.defaultText = 'Please select a date' + } + const validDate = this.props.version && moment(dateTime.date).isBetween(this.props.version.validationSummary.startDate, this.props.version.validationSummary.endDate) + console.log(validDate, this.props, moment(dateTime.date).isBetween(this.props.version.validationSummary.startDate, this.props.version.validationSummary.endDate)) + return ( +
+ + + + + + { + const fromTo = evt.target.value.split('-') + const from = +fromTo[0] + const to = +fromTo[1] + this.props.updateDateTimeFilter({from, to}) + this.props.onChange && this.props.onChange({from, to}) + }} + > + {timeOptions.map((t, i) => { + return + })} + + + + {!validDate + ? + + + Warning! Chosen date is outside of valid date range for feed version. + + + + : null + } +
+ ) + } +} diff --git a/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js b/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js new file mode 100644 index 000000000..2f2f34a36 --- /dev/null +++ b/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js @@ -0,0 +1,29 @@ +import React from 'react' +import { connect } from 'react-redux' + +import DateTimeFilter from '../components/DateTimeFilter' +import { updateDateTimeFilter } from '../../../../gtfs/actions/filter' + + +const mapStateToProps = (state, ownProps) => { + return { + dateTime: state.gtfs.filter.dateTimeFilter + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + onComponentMount: (initialProps) => { + }, + updateDateTimeFilter: (props) => { + dispatch(updateDateTimeFilter(props)) + } + } +} + +const ActiveDateTimeFilter = connect( + mapStateToProps, + mapDispatchToProps +)(DateTimeFilter) + +export default ActiveDateTimeFilter diff --git a/src/main/client/manager/util/index.js b/src/main/client/manager/util/index.js new file mode 100644 index 000000000..75bad7f16 --- /dev/null +++ b/src/main/client/manager/util/index.js @@ -0,0 +1,10 @@ +export function findProjectByFeedSource (state, feedSourceId) { + return state.projects.all + ? state.projects.all.find(p => { + if (!p.feedSources) { + return false + } + return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) + }) + : null +} diff --git a/src/main/client/scenario-editor/utils/reverse.js b/src/main/client/scenario-editor/utils/reverse.js new file mode 100644 index 000000000..2791f9161 --- /dev/null +++ b/src/main/client/scenario-editor/utils/reverse.js @@ -0,0 +1,28 @@ +import fetch from 'isomorphic-fetch' +import qs from 'qs' + +import { getConfigProperty } from '../../common/util/config' + +export async function reversePelias (point) { + const location = {lon: point.lng, lat: point.lat} + const api_key = getConfigProperty('MAPZEN_TURN_BY_TURN_KEY') + const params = { + api_key, + ...location + } + // api_key=mapzen-xxxxxx&point.lat=48.858268&point.lon=2.294471 + const url = `https://search.mapzen.com/v1/reverse?${qs.stringify(params)}` + const response = await fetch(url) + return await response.json() +} + +export async function reverseEsri (point) { + const params = { + location: `${point.lng},${point.lat}`, + returnIntersection: true, + f: 'pjson' + } + const url = `http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode?${qs.stringify(params)}` + const response = await fetch(url) + return await response.json() +} From 2df1951e748c9669504ef8c76f5f32a9c4cc82d6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 11:18:00 -0400 Subject: [PATCH 019/323] fix page load issues (load feeds on demand) --- .../client/manager/components/UserHomePage.js | 17 ++++++++++++++--- .../manager/containers/ActiveUserHomePage.js | 9 +++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index e18abb3d0..f32f57842 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -4,6 +4,7 @@ import Icon from 'react-fa' import { Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' import moment from 'moment' +import objectPath from 'object-path' import ManagerPage from '../../common/components/ManagerPage' import { getConfigProperty, getComponentMessages, getMessage } from '../../common/util/config' @@ -14,9 +15,11 @@ export default class UserHomePage extends Component { static propTypes = { user: PropTypes.object, projects: PropTypes.array, + project: PropTypes.object, onComponentMount: PropTypes.func, logoutHandler: PropTypes.func, + fetchProjectFeeds: PropTypes.func, visibilityFilter: PropTypes.object, searchTextChanged: PropTypes.func, @@ -29,6 +32,13 @@ export default class UserHomePage extends Component { componentWillMount () { this.props.onComponentMount(this.props) } + componentWillReceiveProps (nextProps) { + const nextId = objectPath.get(nextProps, 'project.id') + const id = objectPath.get(this.props, 'project.id') + if (nextId && nextId !== id) { + this.props.fetchProjectFeeds(nextProps.project.id) + } + } componentWillUnmount () { this.setState({showLoading: true}) } @@ -221,9 +231,10 @@ export default class UserHomePage extends Component { {activeProject && activeProject.feedSources ? activeProject.feedSources.filter(feedVisibilityFilter).map(fs => renderFeedItems(activeProject, fs)) - : this.props.projects && this.props.projects.map(p => { - return p.feedSources && p.feedSources.filter(feedVisibilityFilter).map(fs => renderFeedItems(p, fs)) - }) + :

Choose a project to view feed sources

+ // this.props.projects && this.props.projects.map(p => { + // return p.feedSources && p.feedSources.filter(feedVisibilityFilter).map(fs => renderFeedItems(p, fs)) + // }) } diff --git a/src/main/client/manager/containers/ActiveUserHomePage.js b/src/main/client/manager/containers/ActiveUserHomePage.js index 9f2f51db5..e7a329738 100644 --- a/src/main/client/manager/containers/ActiveUserHomePage.js +++ b/src/main/client/manager/containers/ActiveUserHomePage.js @@ -16,16 +16,21 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { + const activeProjectId = ownProps.routeParams.projectId return { onComponentMount: (props) => { dispatch(getRecentActivity(props.user)) dispatch(fetchProjects()) .then(projects => { - for (var i = 0; i < projects.length; i++) { - dispatch(fetchProjectFeeds(projects[i].id)) + if (activeProjectId) { + dispatch(fetchProjectFeeds(activeProjectId)) } + // for (var i = 0; i < projects.length; i++) { + // dispatch(fetchProjectFeeds(projects[i].id)) + // } }) }, + fetchProjectFeeds: (projectId) => { dispatch(fetchProjectFeeds(projectId)) }, logoutHandler: () => { dispatch(logout()) }, searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text)) }, visibilityFilterChanged: (filter) => dispatch(setVisibilityFilter(filter)) From b3f5a5ad383158ab8a43fbb6c71a135021d5b778 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 11:23:18 -0400 Subject: [PATCH 020/323] only fetch feeds if not fetched --- src/main/client/manager/components/UserHomePage.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index f32f57842..d90d480b6 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -35,7 +35,7 @@ export default class UserHomePage extends Component { componentWillReceiveProps (nextProps) { const nextId = objectPath.get(nextProps, 'project.id') const id = objectPath.get(this.props, 'project.id') - if (nextId && nextId !== id) { + if (nextId && nextId !== id && !nextProps.project.feedSources) { this.props.fetchProjectFeeds(nextProps.project.id) } } @@ -231,7 +231,7 @@ export default class UserHomePage extends Component { {activeProject && activeProject.feedSources ? activeProject.feedSources.filter(feedVisibilityFilter).map(fs => renderFeedItems(activeProject, fs)) - :

Choose a project to view feed sources

+ :

Choose a project to view feeds

// this.props.projects && this.props.projects.map(p => { // return p.feedSources && p.feedSources.filter(feedVisibilityFilter).map(fs => renderFeedItems(p, fs)) // }) From 67231c16f714c41f4f312abc021eaa36de2d94bf Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 11:28:50 -0400 Subject: [PATCH 021/323] added missing gtfs map for reporter --- .../client/gtfs/containers/ActiveGtfsMap.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/main/client/gtfs/containers/ActiveGtfsMap.js diff --git a/src/main/client/gtfs/containers/ActiveGtfsMap.js b/src/main/client/gtfs/containers/ActiveGtfsMap.js new file mode 100644 index 000000000..52d48eacb --- /dev/null +++ b/src/main/client/gtfs/containers/ActiveGtfsMap.js @@ -0,0 +1,56 @@ +import React from 'react' +import { connect } from 'react-redux' + +import GtfsMap from '../components/gtfsmap' +import { fetchPatterns } from '../actions/patterns' +import { fetchStops, stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../actions/stops' +import { fetchRoutes } from '../actions/routes' +import { fetchFeedVersionIsochrones } from '../../manager/actions/feeds' + + +const mapStateToProps = (state, ownProps) => { + return { + stops: state.gtfs.stops.data, + routes: state.gtfs.routes.data, + patterns: state.gtfs.patterns.data, + routing: state.routing.locationBeforeTransitions && state.routing.locationBeforeTransitions.pathname, + dateTime: state.gtfs.filter.dateTimeFilter + } +} + +const mapDispatchToProps = (dispatch, ownProps) => { + const feedId = ownProps.version.feedSource.id + return { + onComponentMount: (initialProps) => { + // if(!initialProps.routes.fetchStatus.fetched) { + // dispatch(fetchRoutes(feedId)) + // } + // if(!initialProps.patterns.fetchStatus.fetched) { + // dispatch(fetchPatterns(feedId, null)) + // } + }, + stopRouteFilterChange: (newValue) => { + dispatch(stopRouteFilterChange(feedId, newValue)) + }, + stopPatternFilterChange: (newValue) => { + dispatch(stopPatternFilterChange(feedId, newValue)) + }, + stopDateTimeFilterChange: (props) => { + dispatch(stopDateTimeFilterChange(feedId, props)) + }, + fetchIsochrones: (feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) => { + dispatch(fetchFeedVersionIsochrones(feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime)) + }, + // viewStops: (row) => { + // dispatch(stopPatternFilterChange(feedId, row)) + // dispatch(ownProps.selectTab('stops')) + // } + } +} + +const ActiveGtfsMap = connect( + mapStateToProps, + mapDispatchToProps +)(GtfsMap) + +export default ActiveGtfsMap From 8c36bda3435f037e845fd234a6be8dfd2c95c2de Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 14 Oct 2016 11:56:25 -0400 Subject: [PATCH 022/323] prevent building network if bounds are null --- .../com/conveyal/datatools/manager/models/FeedVersion.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index d4b2b8449..1b9616687 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -445,6 +445,9 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { eventBus.post(statusMap); Rectangle2D bounds = this.validationResult.bounds; + + if (bounds == null) return null; + String osmFileName = String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf",feedSourceDir, bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY()); File osmExtract = new File(osmFileName); if (!osmExtract.exists()) { From e9e71f9a9a52c1502db1d01b695acb9936701c39 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 17 Oct 2016 18:19:43 -0400 Subject: [PATCH 023/323] remove unused components --- .../components/CircleMarkerWithLabel.js | 30 ------------------- .../editor/components/MarkerWithLabel.js | 30 ------------------- 2 files changed, 60 deletions(-) delete mode 100644 src/main/client/editor/components/CircleMarkerWithLabel.js delete mode 100644 src/main/client/editor/components/MarkerWithLabel.js diff --git a/src/main/client/editor/components/CircleMarkerWithLabel.js b/src/main/client/editor/components/CircleMarkerWithLabel.js deleted file mode 100644 index 275a8334b..000000000 --- a/src/main/client/editor/components/CircleMarkerWithLabel.js +++ /dev/null @@ -1,30 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { CircleMarker } from 'react-leaflet' -import { circleMarker } from 'leaflet' -require('leaflet.label') - -// Use the same code as CircleMarker, just customise componentWillMount() -export default class CircleMarkerWithLabel extends CircleMarker { - // Add the relevant prop types for Leaflet.label - static propTypes = { - label: PropTypes.string.isRequired, - labelOptions: PropTypes.object, - center: PropTypes.array, - }; - - componentWillMount() { - super.componentWillMount(); - // Extract label and labelOptions from props - const { label, labelOptions, map, center, ...props } = this.props; - // Call bindLabel() as documented in Leaflet.label - this.leafletElement = circleMarker(center, props).bindLabel(label, labelOptions); - } - - componentDidUpdate(prevProps) { - if (this.props.center !== prevProps.center) { - this.leafletElement.setLatLng(this.props.center); - } - this.setStyleIfChanged(prevProps, this.props); - // Eventually handle specific logic if props change - } -} diff --git a/src/main/client/editor/components/MarkerWithLabel.js b/src/main/client/editor/components/MarkerWithLabel.js deleted file mode 100644 index b10510667..000000000 --- a/src/main/client/editor/components/MarkerWithLabel.js +++ /dev/null @@ -1,30 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Marker } from 'react-leaflet' -import { marker } from 'leaflet' -require('leaflet.label') - -// Use the same code as Marker, just customise componentWillMount() -export default class MarkerWithLabel extends Marker { - // Add the relevant prop types for Leaflet.label - static propTypes = { - label: PropTypes.string.isRequired, - labelOptions: PropTypes.object, - position: PropTypes.array, - }; - - componentWillMount() { - super.componentWillMount() - // Extract label and labelOptions from props - const { label, labelOptions, map, position, ...props } = this.props - // Call bindLabel() as documented in Leaflet.label - this.leafletElement = marker(position, props).bindLabel(label, labelOptions) - } - - componentDidUpdate(prevProps) { - if (this.props.position !== prevProps.position) { - this.leafletElement.setLatLng(this.props.position) - } - // this.setStyleIfChanged(prevProps, this.props) - // Eventually handle specific logic if props change - } -} From 785a2cb68b137068b35ac76d190d0dccabbd1607 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:12:16 -0400 Subject: [PATCH 024/323] update deprecated jackson functions --- .../manager/utils/ResponseError.java | 20 ------------------- .../manager/utils/json/JsonManager.java | 6 +++--- 2 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 src/main/java/com/conveyal/datatools/manager/utils/ResponseError.java diff --git a/src/main/java/com/conveyal/datatools/manager/utils/ResponseError.java b/src/main/java/com/conveyal/datatools/manager/utils/ResponseError.java deleted file mode 100644 index 6367e675c..000000000 --- a/src/main/java/com/conveyal/datatools/manager/utils/ResponseError.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.conveyal.datatools.manager.utils; - -/** - * Created by landon on 5/24/16. - */ -public class ResponseError { - private String message; - - public ResponseError(String message, String... args) { - this.message = String.format(message, args); - } - - public ResponseError(Exception e) { - this.message = e.getMessage(); - } - - public String getMessage() { - return this.message; - } -} diff --git a/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java b/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java index 3d09e7db7..69b5e3c68 100644 --- a/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java +++ b/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java @@ -39,8 +39,8 @@ public class JsonManager { public JsonManager (Class theClass, Class view) { this.theClass = theClass; this.om = new ObjectMapper(); - om.addMixInAnnotations(InvalidValue.class, InvalidValueMixIn.class); - om.addMixInAnnotations(Rectangle2D.class, Rectangle2DMixIn.class); + om.addMixIn(InvalidValue.class, InvalidValueMixIn.class); + om.addMixIn(Rectangle2D.class, Rectangle2DMixIn.class); om.registerModule(new GeoJsonModule()); SimpleModule deser = new SimpleModule(); @@ -64,7 +64,7 @@ public JsonManager (Class theClass, Class view) { * Add an additional mixin for serialization with this object mapper. */ public void addMixin(Class target, Class mixin) { - om.addMixInAnnotations(target, mixin); + om.addMixIn(target, mixin); } public String write(Object o) throws JsonProcessingException{ From 458dadfd7f69a86f3300ce92dd1176b51712d8e9 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:36:00 -0400 Subject: [PATCH 025/323] major refactoring for gtfs-api GTFSFeed handling --- .../datatools/manager/models/FeedSource.java | 104 ++--- .../manager/models/FeedValidationResult.java | 75 ++-- .../models/FeedValidationResultSummary.java | 7 - .../datatools/manager/models/FeedVersion.java | 334 +++++++++------ .../datatools/manager/models/Model.java | 7 + .../manager/models/OtpRouterConfig.java | 2 +- .../datatools/manager/models/OtpServer.java | 1 + .../manager/persistence/FeedStore.java | 404 ++++++++++++------ 8 files changed, 534 insertions(+), 400 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index f4b576a45..ed4855ab2 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -4,6 +4,7 @@ import com.conveyal.datatools.editor.datastore.GlobalTx; import com.conveyal.datatools.editor.datastore.VersionedDataStore; import com.conveyal.datatools.manager.DataManager; +import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.NotifyUsersForSubscriptionJob; import com.conveyal.datatools.manager.persistence.DataStore; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -17,6 +18,7 @@ import java.io.File; import java.io.IOException; +import java.io.InvalidClassException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -120,75 +122,23 @@ public FeedSource () { /** * Fetch the latest version of the feed. */ - public FeedVersion fetch (EventBus eventBus) { + public FeedVersion fetch (EventBus eventBus, String fetchUser) { Map statusMap = new HashMap<>(); statusMap.put("message", "Downloading file"); statusMap.put("percentComplete", 20.0); statusMap.put("error", false); eventBus.post(statusMap); -// if (this.retrievalMethod.equals(FeedRetrievalMethod.MANUALLY_UPLOADED)) { -// String message = String.format("not fetching feed %s, not a fetchable feed", this.name); -// LOG.info(message); -// statusMap.put("message", message); -// statusMap.put("percentComplete", 0.0); -// statusMap.put("error", true); -// eventBus.post(statusMap); -// return null; -// } - - // fetchable feed, continue FeedVersion latest = getLatest(); // We create a new FeedVersion now, so that the fetched date is (milliseconds) before // fetch occurs. That way, in the highly unlikely event that a feed is updated while we're // fetching it, we will not miss a new feed. - FeedVersion newFeed = new FeedVersion(this); + FeedVersion version = new FeedVersion(this); // build the URL from which to fetch - URL url; -// if (this.retrievalMethod.equals(FeedRetrievalMethod.FETCHED_AUTOMATICALLY)) - url = this.url; -// else if (this.retrievalMethod.equals(FeedRetrievalMethod.PRODUCED_IN_HOUSE)) { -// if (this.snapshotVersion == null) { -// String message = String.format("Feed %s has no editor id; cannot fetch", this.name); -// LOG.error(message); -// statusMap.put("message", message); -// statusMap.put("percentComplete", 0.0); -// statusMap.put("error", true); -// eventBus.post(statusMap); -// return null; -// } -// -// String baseUrl = DataManager.getConfigPropertyAsText("modules.editor.url"); -// -// if (!baseUrl.endsWith("/")) -// baseUrl += "/"; -// -// // build the URL -// try { -// url = new URL(baseUrl + "api/mgrsnapshot/" + this.snapshotVersion + ".zip"); -// } catch (MalformedURLException e) { -// String message = "Invalid URL for editor, check your config."; -// LOG.error(message); -// statusMap.put("message", message); -// statusMap.put("percentComplete", 0.0); -// statusMap.put("error", true); -// eventBus.post(statusMap); -// return null; -// } -// } -// else { -// String message = "Unknown retrieval method: " + this.retrievalMethod; -// LOG.error(message); -// statusMap.put("message", message); -// statusMap.put("percentComplete", 0.0); -// statusMap.put("error", true); -// eventBus.post(statusMap); -// return null; -// } - - LOG.info(url.toString()); + URL url = this.url; + LOG.info("Fetching from {}", url.toString()); // make the request, using the proper HTTP caching headers to prevent refetch, if applicable HttpURLConnection conn; @@ -225,9 +175,6 @@ public FeedVersion fetch (EventBus eventBus) { conn.setDefaultUseCaches(true); - /*if (oauthToken != null) - conn.addRequestProperty("Authorization", "Bearer " + oauthToken);*/ - // lastFetched is set to null when the URL changes and when latest feed version is deleted if (latest != null && this.lastFetched != null) conn.setIfModifiedSince(Math.min(latest.updated.getTime(), this.lastFetched.getTime())); @@ -248,13 +195,13 @@ public FeedVersion fetch (EventBus eventBus) { // TODO: redirects else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - String message = String.format("Saving %s feed", this.name); + String message = String.format("Saving %s feed. (This may take a while with large feeds.)", this.name); LOG.info(message); statusMap.put("message", message); - statusMap.put("percentComplete", 100.0); + statusMap.put("percentComplete", 75.0); statusMap.put("error", false); eventBus.post(statusMap); - File out = newFeed.newGtfsFile(conn.getInputStream()); + File out = version.newGtfsFile(conn.getInputStream()); } else { @@ -273,17 +220,19 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { statusMap.put("percentComplete", 100.0); statusMap.put("error", true); eventBus.post(statusMap); + e.printStackTrace(); return null; + } catch (Exception e) { + throw e; } - // validate the fetched file // note that anything other than a new feed fetched successfully will have already returned from the function - newFeed.hash(); + version.hash(); - if (latest != null && newFeed.hash.equals(latest.hash)) { + if (latest != null && version.hash.equals(latest.hash)) { String message = String.format("Feed %s was fetched but has not changed; server operators should add If-Modified-Since support to avoid wasting bandwidth", this.name); LOG.warn(message); - newFeed.getGtfsFile().delete(); + version.getGtfsFile().delete(); statusMap.put("message", message); statusMap.put("percentComplete", 100.0); statusMap.put("error", false); @@ -291,16 +240,22 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { return null; } else { - newFeed.userId = this.userId; + version.userId = this.userId; - this.lastFetched = newFeed.updated; + this.lastFetched = version.updated; this.save(); NotifyUsersForSubscriptionJob notifyFeedJob = new NotifyUsersForSubscriptionJob("feed-updated", this.id, "New feed version created for " + this.name); Thread notifyThread = new Thread(notifyFeedJob); notifyThread.start(); - - return newFeed; + String message = String.format("Fetch complete for %s", this.name); + LOG.info(message); + statusMap.put("message", message); + statusMap.put("percentComplete", 100.0); + statusMap.put("error", false); + eventBus.post(statusMap); + version.setUserById(fetchUser); + return version; } } @@ -336,7 +291,14 @@ public FeedVersion getLatest () { // if (vs == null){ // return null; // } - FeedVersion v = FeedVersion.versionStore.findFloor("version", new Fun.Tuple2(this.id, Fun.HI)); + FeedVersion v = null; + try { + Class localClass = Class.forName(FeedVersion.class.getName()); + v = FeedVersion.versionStore.findFloor("version", new Fun.Tuple2(this.id, Fun.HI)); + } catch (ClassNotFoundException e) { + LOG.info("Error getting feed version", e); + } + // the ID doesn't necessarily match, because it will fall back to the previous source in the store if there are no versions for this source if (v == null || !v.feedSourceId.equals(this.id)) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java index 57baa89be..de0fb8836 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java @@ -2,6 +2,7 @@ import com.conveyal.gtfs.GTFSFeed; import com.conveyal.gtfs.model.ValidationResult; +import com.conveyal.gtfs.stats.FeedStats; import com.conveyal.gtfs.validator.json.LoadStatus; import com.fasterxml.jackson.annotation.JsonProperty; @@ -15,15 +16,11 @@ * Created by landon on 5/10/16. */ public class FeedValidationResult implements Serializable { + private static final long serialVersionUID = 1L; @JsonProperty public LoadStatus loadStatus; public String loadFailureReason; - public String feedFileName; public Collection agencies; - public ValidationResult routes; - public ValidationResult stops; - public ValidationResult trips; - public ValidationResult shapes; public int agencyCount; public int routeCount; public int tripCount; @@ -33,39 +30,39 @@ public class FeedValidationResult implements Serializable { public LocalDate endDate; public Rectangle2D bounds; - public FeedValidationResult() { -// this.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); -// this.agencyCount = stats.getAgencyCount(); -// this.routeCount = stats.getRouteCount(); -// this.bounds = stats.getBounds(); -// LocalDate calDateStart = stats.getCalendarDateStart(); -// LocalDate calSvcStart = stats.getCalendarServiceRangeStart(); -// -// LocalDate calDateEnd = stats.getCalendarDateEnd(); -// LocalDate calSvcEnd = stats.getCalendarServiceRangeEnd(); -// -// if (calDateStart == null && calSvcStart == null) -// // no service . . . this is bad -// this.startDate = null; -// else if (calDateStart == null) -// this.startDate = calSvcStart; -// else if (calSvcStart == null) -// this.startDate = calDateStart; -// else -// this.startDate = calDateStart.isBefore(calSvcStart) ? calDateStart : calSvcStart; -// -// if (calDateEnd == null && calSvcEnd == null) -// // no service . . . this is bad -// this.endDate = null; -// else if (calDateEnd == null) -// this.endDate = calSvcEnd; -// else if (calSvcEnd == null) -// this.endDate = calDateEnd; -// else -// this.endDate = calDateEnd.isAfter(calSvcEnd) ? calDateEnd : calSvcEnd; -// this.loadStatus = LoadStatus.SUCCESS; -// this.tripCount = stats.getTripCount(); -// this.stopTimesCount = stats.getStopTimesCount(); -// this.errorCount = gtfsFeed.errors.size(); + public FeedValidationResult(GTFSFeed feed, FeedStats stats) { + this.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); + this.agencyCount = stats.getAgencyCount(); + this.routeCount = stats.getRouteCount(); + this.bounds = stats.getBounds(); + LocalDate calDateStart = stats.getCalendarDateStart(); + LocalDate calSvcStart = stats.getCalendarServiceRangeStart(); + + LocalDate calDateEnd = stats.getCalendarDateEnd(); + LocalDate calSvcEnd = stats.getCalendarServiceRangeEnd(); + + if (calDateStart == null && calSvcStart == null) + // no service . . . this is bad + this.startDate = null; + else if (calDateStart == null) + this.startDate = calSvcStart; + else if (calSvcStart == null) + this.startDate = calDateStart; + else + this.startDate = calDateStart.isBefore(calSvcStart) ? calDateStart : calSvcStart; + + if (calDateEnd == null && calSvcEnd == null) + // no service . . . this is bad + this.endDate = null; + else if (calDateEnd == null) + this.endDate = calSvcEnd; + else if (calSvcEnd == null) + this.endDate = calDateEnd; + else + this.endDate = calDateEnd.isAfter(calSvcEnd) ? calDateEnd : calSvcEnd; + this.loadStatus = LoadStatus.SUCCESS; + this.tripCount = stats.getTripCount(); + this.stopTimesCount = stats.getStopTimesCount(); + this.errorCount = feed.errors.size(); } } \ No newline at end of file diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResultSummary.java b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResultSummary.java index 975d4064d..39083a1a1 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResultSummary.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResultSummary.java @@ -53,13 +53,6 @@ public FeedValidationResultSummary (FeedValidationResult result) { this.agencies = result.agencies; if (loadStatus == LoadStatus.SUCCESS) { -// if (result.routes != null) { -// this.errorCount = -// result.routes.invalidValues.size() + -// result.stops.invalidValues.size() + -// result.trips.invalidValues.size() + -// result.shapes.invalidValues.size(); -// } this.errorCount = result.errorCount; this.agencyCount = result.agencyCount; this.routeCount = result.routeCount; diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index 1b9616687..cbae46bbf 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -3,6 +3,8 @@ import java.awt.geom.Rectangle2D; import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -25,14 +27,19 @@ import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.AmazonS3Exception; +import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.S3Object; import com.conveyal.datatools.common.status.StatusEvent; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.controllers.api.GtfsApiController; import com.conveyal.datatools.manager.persistence.DataStore; import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.HashUtils; +import com.conveyal.gtfs.GTFSCache; import com.conveyal.gtfs.GTFSFeed; +import com.conveyal.gtfs.api.ApiMain; import com.conveyal.gtfs.validator.json.LoadStatus; import com.conveyal.gtfs.stats.FeedStats; import com.conveyal.r5.point_to_point.builder.TNBuilderConfig; @@ -40,11 +47,13 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.eventbus.EventBus; import com.vividsolutions.jts.geom.Geometry; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.filefilter.WildcardFileFilter; import org.geotools.geojson.geom.GeometryJSON; import org.mapdb.Fun.Function2; import org.mapdb.Fun.Tuple2; @@ -71,13 +80,13 @@ public class FeedVersion extends Model implements Serializable { private static final long serialVersionUID = 1L; private static ObjectMapper mapper = new ObjectMapper(); public static final Logger LOG = LoggerFactory.getLogger(FeedVersion.class); - + public static final String validationSubdir = "validation/"; static DataStore versionStore = new DataStore("feedversions"); private static FeedStore feedStore = new FeedStore(); static { // set up indexing on feed versions by feed source, indexed by - versionStore.secondaryKey("version", new Function2, String, FeedVersion> () { + versionStore.secondaryKey("version", new Function2, String, FeedVersion>() { @Override public Tuple2 run(String key, FeedVersion fv) { return new Tuple2(fv.feedSourceId, fv.version); @@ -88,7 +97,7 @@ public Tuple2 run(String key, FeedVersion fv) { /** * We generate IDs manually, but we need a bit of information to do so */ - public FeedVersion (FeedSource source) { + public FeedVersion(FeedSource source) { this.updated = new Date(); this.feedSourceId = source.id; @@ -96,7 +105,7 @@ public FeedVersion (FeedSource source) { DateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmssX"); // since we store directly on the file system, this lets users look at the DB directly - this.id = getCleanName(source.name) + "_" + df.format(this.updated) + "_" + source.id + ".zip"; + this.id = getCleanName(source.name) + "-" + df.format(this.updated) + "-" + source.id + ".zip"; // infer the version // FeedVersion prev = source.getLatest(); @@ -113,11 +122,13 @@ public FeedVersion (FeedSource source) { /** * Create an uninitialized feed version. This should only be used for dump/restore. */ - public FeedVersion () { + public FeedVersion() { // do nothing } - /** The feed source this is associated with */ + /** + * The feed source this is associated with + */ @JsonView(JsonViews.DataDump.class) public String feedSourceId; @@ -125,33 +136,35 @@ public FeedVersion () { public transient TransportNetwork transportNetwork; @JsonView(JsonViews.UserInterface.class) - public FeedSource getFeedSource () { + public FeedSource getFeedSource() { return FeedSource.get(feedSourceId); } @JsonIgnore - public FeedVersion getPreviousVersion () { + public FeedVersion getPreviousVersion() { return versionStore.find("version", new Tuple2(this.feedSourceId, this.version - 1)); } @JsonView(JsonViews.UserInterface.class) - public String getPreviousVersionId () { + public String getPreviousVersionId() { FeedVersion p = getPreviousVersion(); return p != null ? p.id : null; } @JsonIgnore - public FeedVersion getNextVersion () { + public FeedVersion getNextVersion() { return versionStore.find("version", new Tuple2(this.feedSourceId, this.version + 1)); } @JsonView(JsonViews.UserInterface.class) - public String getNextVersionId () { + public String getNextVersionId() { FeedVersion p = getNextVersion(); return p != null ? p.id : null; } - /** The hash of the feed file, for quick checking if the file has been updated */ + /** + * The hash of the feed file, for quick checking if the file has been updated + */ @JsonView(JsonViews.DataDump.class) public String hash; @@ -161,41 +174,26 @@ public File getGtfsFile() { } public File newGtfsFile(InputStream inputStream) { - return feedStore.newFeed(id, inputStream, getFeedSource()); + + File file = feedStore.newFeed(id, inputStream, getFeedSource()); + this.fileSize = file.length(); + this.fileTimestamp = file.lastModified(); + this.save(); + LOG.info("New GTFS file saved: {}", id); + return file; } @JsonIgnore public GTFSFeed getGtfsFeed() { -// return GTFSFeed.fromFile(getGtfsFile().getAbsolutePath()); - // TODO: fix broken GTFS cache - try { - LOG.info("Checking for feed in cache.."); - if(!DataManager.gtfsCache.containsId(this.id)) { - File f = getGtfsFile(); - LOG.info("Did not find, putting file in cache: " + f); - DataManager.gtfsCache.put(id, f); - } - return DataManager.gtfsCache.get(id); - } catch (Exception e) { - System.out.println("Exception in getGtfsFeed: " + e.getMessage()); - e.printStackTrace(); - try { - FileUtils.cleanDirectory(new File(DataManager.cacheDirectory)); -// getGtfsFeed(); - } catch (IOException e1) { - e1.printStackTrace(); - } - } - return null; + String apiId = id.replace(".zip", ""); +// return DataManager.gtfsCache.get(apiId); + return GtfsApiController.gtfsApi.getFeedSource(apiId).feed; } /** The results of validating this feed */ @JsonView(JsonViews.DataDump.class) public FeedValidationResult validationResult; -// @JsonIgnore -// public List errors; - @JsonView(JsonViews.UserInterface.class) public FeedValidationResultSummary getValidationSummary() { return new FeedValidationResultSummary(validationResult); @@ -239,10 +237,16 @@ public void validate(EventBus eventBus) { eventBus = new EventBus(); } Map statusMap = new HashMap<>(); - File gtfsFile = null; + GTFSFeed gtfsFeed; try { - eventBus.post(new StatusEvent("Loading feed...", 5, false)); - gtfsFile = getGtfsFile(); + statusMap.put("message", "Unpacking feed..."); + statusMap.put("percentComplete", 15.0); + statusMap.put("error", false); + eventBus.post(statusMap); + + /* First getGtfsFeed() call triggers feed load from zip file into gtfsCache + This may take a while for large feeds */ + gtfsFeed = getGtfsFeed(); } catch (Exception e) { String errorString = String.format("No GTFS feed exists for version: %s", this.id); LOG.warn(errorString); @@ -253,9 +257,8 @@ public void validate(EventBus eventBus) { return; } - GTFSFeed gtfsFeed = getGtfsFeed(); if(gtfsFeed == null) { - String errorString = String.format("Could not get GTFSFeed object for FeedVersion {}", id); + String errorString = String.format("Could not get GTFSFeed object for FeedVersion %s", id); LOG.warn(errorString); // eventBus.post(new StatusEvent(errorString, 0, true)); statusMap.put("message", errorString); @@ -265,25 +268,10 @@ public void validate(EventBus eventBus) { return; } - Map tripsPerDate; - // load feed into GTFS api - // TODO: pass GTFSFeed to GTFSApi? - if (DataManager.getConfigProperty("modules.gtfsapi.enabled").asBoolean() && DataManager.getConfigProperty("modules.gtfsapi.load_on_fetch").asBoolean()) { -// LOG.info("Loading feed into GTFS api"); -// String checksum = this.hash; -// try { -// GtfsApiController.gtfsApi.registerFeedSource(this.feedSourceId, gtfsFile); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// if (GtfsApiController.feedUpdater != null) { -// GtfsApiController.feedUpdater.addFeedETag(checksum); -// } - } + Map tripsPerDate = null; try { // eventBus.post(new StatusEvent("Validating feed...", 30, false)); - statusMap.put("message", "Validating feed..."); statusMap.put("percentComplete", 30.0); statusMap.put("error", false); @@ -292,41 +280,15 @@ public void validate(EventBus eventBus) { gtfsFeed.validate(); LOG.info("Calculating stats..."); FeedStats stats = gtfsFeed.calculateStats(); - validationResult = new FeedValidationResult(); - validationResult.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); - validationResult.agencyCount = stats.getAgencyCount(); - validationResult.routeCount = stats.getRouteCount(); - validationResult.bounds = stats.getBounds(); - LocalDate calDateStart = stats.getCalendarDateStart(); - LocalDate calSvcStart = stats.getCalendarServiceRangeStart(); - - LocalDate calDateEnd = stats.getCalendarDateEnd(); - LocalDate calSvcEnd = stats.getCalendarServiceRangeEnd(); - - if (calDateStart == null && calSvcStart == null) - // no service . . . this is bad - validationResult.startDate = null; - else if (calDateStart == null) - validationResult.startDate = calSvcStart; - else if (calSvcStart == null) - validationResult.startDate = calDateStart; - else - validationResult.startDate = calDateStart.isBefore(calSvcStart) ? calDateStart : calSvcStart; - - if (calDateEnd == null && calSvcEnd == null) - // no service . . . this is bad - validationResult.endDate = null; - else if (calDateEnd == null) - validationResult.endDate = calSvcEnd; - else if (calSvcEnd == null) - validationResult.endDate = calDateEnd; - else - validationResult.endDate = calDateEnd.isAfter(calSvcEnd) ? calDateEnd : calSvcEnd; - validationResult.loadStatus = LoadStatus.SUCCESS; - validationResult.tripCount = stats.getTripCount(); - validationResult.stopTimesCount = stats.getStopTimesCount(); - validationResult.errorCount = gtfsFeed.errors.size(); - tripsPerDate = stats.getTripCountPerDateOfService(); + validationResult = new FeedValidationResult(gtfsFeed, stats); + LOG.info("Total errors after validation: {}", validationResult.errorCount); + try { + // This may take a while for very large feeds. + LOG.info("Calculating # of trips per date of service"); + tripsPerDate = stats.getTripCountPerDateOfService(); + }catch (Exception e) { + e.printStackTrace(); + } } catch (Exception e) { LOG.error("Unable to validate feed {}", this); // eventBus.post(new StatusEvent("Unable to validate feed.", 0, true)); @@ -336,12 +298,11 @@ else if (calSvcEnd == null) eventBus.post(statusMap); e.printStackTrace(); this.validationResult = null; -// validationResult.loadStatus = LoadStatus.OTHER_FAILURE; -// halt(400, "Error validating feed..."); + validationResult.loadStatus = LoadStatus.OTHER_FAILURE; + halt(400, "Error validating feed..."); return; } - String s3Bucket = DataManager.config.get("application").get("data").get("gtfs_s3_bucket").asText(); File tempFile = null; try { // eventBus.post(new StatusEvent("Saving validation results...", 80, false)); @@ -354,12 +315,7 @@ else if (calSvcEnd == null) tempFile.deleteOnExit(); Map validation = new HashMap<>(); validation.put("errors", gtfsFeed.errors); - validation.put("tripsPerDate", tripsPerDate -// .entrySet() -// .stream() -// .map(entry -> entry.getKey().format(DateTimeFormatter.BASIC_ISO_DATE)) -// .collect(Collectors.toList()) - ); + validation.put("tripsPerDate", tripsPerDate); GeometryJSON g = new GeometryJSON(); Geometry buffers = gtfsFeed.getMergedBuffers(); validation.put("mergedBuffers", buffers != null ? g.toString(buffers) : null); @@ -371,34 +327,94 @@ else if (calSvcEnd == null) } catch (IOException e) { e.printStackTrace(); } - // upload to S3, if we have bucket name and use s3 storage - if(s3Bucket != null && DataManager.getConfigProperty("application.data.use_s3_storage").asBoolean() == true) { - AWSCredentials creds; + saveValidationResult(tempFile); + } + + @JsonIgnore + public JsonNode getValidationResult() { + String keyName = validationSubdir + this.id + ".json"; + InputStream objectData = null; + if (DataManager.feedBucket != null && DataManager.useS3) { + try { + LOG.info("Getting validation results from s3"); + S3Object object = FeedStore.s3Client.getObject(new GetObjectRequest(DataManager.feedBucket, keyName)); + objectData = object.getObjectContent(); + } catch (AmazonS3Exception e) { + // if json file does not exist, validate feed. + this.validate(); + this.save(); + halt(503, "Try again later. Validating feed"); + } catch (AmazonServiceException ase) { + LOG.error("Error downloading from s3"); + ase.printStackTrace(); + } + + } + // if s3 upload set to false + else { + File file = new File(FeedStore.basePath + "/" + keyName); + try { + objectData = new FileInputStream(file); + } catch (Exception e) { + LOG.warn("Validation does not exist. Validating feed."); + this.validate(); + this.save(); + halt(503, "Try again later. Validating feed"); + } + } + return ensureValidationIsCurrent(objectData); + } - // default credentials providers, e.g. IAM role - creds = new DefaultAWSCredentialsProviderChain().getCredentials(); + private JsonNode ensureValidationIsCurrent(InputStream objectData) { + JsonNode n; + // Process the objectData stream. + try { + n = mapper.readTree(objectData); + if (!n.has("errors") || !n.has("tripsPerDate")) { + throw new Exception("Validation for feed version not up to date"); + } + return n; + } catch (IOException e) { + // if json file does not exist, validate feed. + this.validate(); + this.save(); + halt(503, "Try again later. Validating feed"); + } catch (Exception e) { + e.printStackTrace(); + this.validate(); + this.save(); + halt(503, "Try again later. Validating feed"); + } + return null; + } - String keyName = "validation/" + this.id + ".json"; + private void saveValidationResult(File file) { + String keyName = validationSubdir + this.id + ".json"; + + // upload to S3, if we have bucket name and use s3 storage + if(DataManager.feedBucket != null && DataManager.useS3) { + AWSCredentials creds; try { LOG.info("Uploading validation json to S3"); - AmazonS3 s3client = new AmazonS3Client(creds); - s3client.putObject(new PutObjectRequest( - s3Bucket, keyName, tempFile)); + FeedStore.s3Client.putObject(new PutObjectRequest( + DataManager.feedBucket, keyName, file)); } catch (AmazonServiceException ase) { LOG.error("Error uploading validation json to S3"); } } // save to validation directory in gtfs folder else { - File validationDir = new File(DataManager.getConfigPropertyAsText("application.data.gtfs") + "/validation"); + File validationDir = new File(FeedStore.basePath + "/" + validationSubdir); + // ensure directory exists validationDir.mkdir(); try { - FileUtils.copyFile(tempFile, new File(validationDir.toPath() + "/" + this.id + ".json")); + FileUtils.copyFile(file, new File(FeedStore.basePath + "/" + keyName)); } catch (IOException e) { LOG.error("Error saving validation json to local disk"); } } } + public void validate() { validate(null); } @@ -431,12 +447,6 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { eventBus = new EventBus(); } - String gtfsDir = DataManager.config.get("application").get("data").get("gtfs").asText() + "/"; - String feedSourceDir = gtfsDir + feedSourceId + "/"; - File fsPath = new File(feedSourceDir); - if (!fsPath.isDirectory()) { - fsPath.mkdir(); - } // Fetch OSM extract Map statusMap = new HashMap<>(); statusMap.put("message", "Fetching OSM extract..."); @@ -446,10 +456,17 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { Rectangle2D bounds = this.validationResult.bounds; - if (bounds == null) return null; + if (bounds == null) { + String message = String.format("Could not build network for %s because feed bounds are unknown.", this.id); + LOG.warn(message); + statusMap.put("message", message); + statusMap.put("percentComplete", 10.0); + statusMap.put("error", true); + eventBus.post(statusMap); + return null; + } - String osmFileName = String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf",feedSourceDir, bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY()); - File osmExtract = new File(osmFileName); + File osmExtract = getOSMFile(bounds); if (!osmExtract.exists()) { InputStream is = getOsmExtract(this.validationResult.bounds); OutputStream out = null; @@ -471,13 +488,22 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { eventBus.post(statusMap); List feedList = new ArrayList<>(); - feedList.add( -// DataManager.gtfsCache.get(id) - getGtfsFeed() - ); - TransportNetwork tn = TransportNetwork.fromFeeds(osmExtract.getAbsolutePath(), feedList, TNBuilderConfig.defaultConfig()); + feedList.add(getGtfsFeed()); + TransportNetwork tn = null; + try { + tn = TransportNetwork.fromFeeds(osmExtract.getAbsolutePath(), feedList, TNBuilderConfig.defaultConfig()); + } catch (Exception e) { + String message = String.format("Unknown error encountered while building network for %s.", this.id); + LOG.warn(message); + statusMap.put("message", message); + statusMap.put("percentComplete", 100.0); + statusMap.put("error", true); + eventBus.post(statusMap); + e.printStackTrace(); + return null; + } this.transportNetwork = tn; - File tnFile = new File(feedSourceDir + this.id + "_network.dat"); + File tnFile = getTransportNetworkPath(); OutputStream tnOut = null; try { tnOut = new FileOutputStream(tnFile); @@ -490,6 +516,11 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { } return null; } + + public static File getOSMFile(Rectangle2D bounds) { + return new File(String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf", getOSMPath(), bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY())); + } + public TransportNetwork buildTransportNetwork() { return buildTransportNetwork(null); } @@ -535,10 +566,10 @@ public Long getFileTimestamp() { return fileTimestamp; } - File file = getGtfsFile(); - if(file == null) return null; - this.fileTimestamp = file.lastModified(); - return file.lastModified(); + this.fileTimestamp = feedStore.getFeedLastModified(id); + this.save(); + + return this.fileTimestamp; } @JsonInclude(Include.NON_NULL) @@ -548,10 +579,10 @@ public Long getFileSize() { return fileSize; } - File file = getGtfsFile(); - if(file == null) return null; - this.fileSize = file.length(); - return file.length(); + this.fileSize = feedStore.getFeedSize(id); + this.save(); + + return fileSize; } /** @@ -560,12 +591,12 @@ public Long getFileSize() { public void delete() { // reset lastModified if feed is latest version FeedSource fs = getFeedSource(); - if (fs.getLatest().id == this.id) { + FeedVersion latest = fs.getLatest(); + if (latest != null && latest.id == this.id) { fs.lastFetched = null; + fs.save(); } - File feed = getGtfsFile(); - if (feed != null && feed.exists()) - feed.delete(); + feedStore.deleteFeed(id); /*for (Deployment d : Deployment.getAll()) { d.feedVersionIds.remove(this.id); @@ -573,4 +604,27 @@ public void delete() { versionStore.delete(this.id); } + @JsonIgnore + public String getR5Path () { + // r5 networks MUST be stored in separate directories (in this case under feed source ID + // because of shared osm.mapdb used by r5 networks placed in same dir + File r5 = new File(String.join(File.separator, FeedStore.basePath.getAbsolutePath(), this.feedSourceId)); + if (!r5.exists()) { + r5.mkdirs(); + } + return r5.getAbsolutePath(); + } + @JsonIgnore + public File getTransportNetworkPath () { + return new File(String.join(File.separator, getR5Path(), id + "_network.dat")); + } + + @JsonIgnore + public static String getOSMPath () { + File osm = new File(FeedStore.basePath.getAbsolutePath() + File.separator + "osm"); + if (!osm.exists()) { + osm.mkdirs(); + } + return osm.getAbsolutePath(); + } } diff --git a/src/main/java/com/conveyal/datatools/manager/models/Model.java b/src/main/java/com/conveyal/datatools/manager/models/Model.java index 5c8c63c8c..618958d26 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/Model.java +++ b/src/main/java/com/conveyal/datatools/manager/models/Model.java @@ -74,6 +74,13 @@ public void setUser (Auth0UserProfile profile) { userId = profile.getUser_id(); } + /** + * Set the owner of this object by Id + */ + public void setUserById (String id) { + userId = id; + } + public void addNote(Note n) { if (noteIds == null) { noteIds = new ArrayList(); diff --git a/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java b/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java index 77d9b4241..234f80083 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java +++ b/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java @@ -8,7 +8,7 @@ */ public class OtpRouterConfig implements Serializable { - + private static final long serialVersionUID = 1L; public Integer numItineraries; public Double walkSpeed; diff --git a/src/main/java/com/conveyal/datatools/manager/models/OtpServer.java b/src/main/java/com/conveyal/datatools/manager/models/OtpServer.java index 2f56c811d..490f76344 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/OtpServer.java +++ b/src/main/java/com/conveyal/datatools/manager/models/OtpServer.java @@ -7,6 +7,7 @@ * Created by landon on 5/20/16. */ public class OtpServer implements Serializable { + private static final long serialVersionUID = 1L; public String name; public List internalUrl; public String publicUrl; diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index 9ad2e89fa..d615454be 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -1,35 +1,35 @@ package com.conveyal.datatools.manager.persistence; import java.io.*; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.UUID; +import com.amazonaws.AmazonClientException; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.model.*; +import com.amazonaws.services.s3.transfer.TransferManager; +import com.amazonaws.services.s3.transfer.Upload; import com.conveyal.datatools.manager.DataManager; +import com.conveyal.datatools.manager.controllers.api.GtfsApiController; import com.conveyal.datatools.manager.models.FeedSource; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.digest.DigestUtils; +import com.conveyal.gtfs.GTFSFeed; +import gnu.trove.list.TLongList; +import gnu.trove.list.array.TLongArrayList; import org.apache.commons.io.IOUtils; -import com.amazonaws.AmazonClientException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.amazonaws.auth.profile.ProfileCredentialsProvider; -import com.amazonaws.event.ProgressEvent; -import com.amazonaws.event.ProgressListener; -import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.transfer.TransferManager; -import com.amazonaws.services.s3.transfer.Upload; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Store a feed on the file system + * Store a feed on the file system or s3 * @author mattwigway * */ @@ -37,54 +37,110 @@ public class FeedStore { public static final Logger LOG = LoggerFactory.getLogger(FeedStore.class); - /** Local file storage path if working offline */ - private File path; - + public static final File basePath = new File(DataManager.getConfigPropertyAsText("application.data.gtfs")); + private final File path; /** An optional AWS S3 bucket to store the feeds */ private String s3Bucket; public static final String s3Prefix = "gtfs/"; + public static AmazonS3Client s3Client; /** An AWS credentials file to use when uploading to S3 */ - private String s3CredentialsFilename; + private static final String s3CredentialsFilename = DataManager.getConfigPropertyAsText("application.data.s3_credentials_file"); public FeedStore() { this(null); } + /** + * Allow construction of feed store in a subdirectory (e.g., "gtfsplus") + * @param subdir + */ public FeedStore(String subdir) { + + // even with s3 storage, we keep a local copy, so we'll still set path. + String pathString = basePath.getAbsolutePath(); + if (subdir != null) pathString += File.separator + subdir; + path = getPath(pathString); + // s3 storage - if (DataManager.getConfigPropertyAsText("application.data.use_s3_storage").equals("true")){ + if (DataManager.useS3){ this.s3Bucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); + s3Client = new AmazonS3Client(getAWSCreds()); } - // local storage - else { - String pathString = DataManager.getConfigPropertyAsText("application.data.gtfs"); - if(subdir != null) pathString += File.separator + subdir; - File path = new File(pathString); - if (!path.exists() || !path.isDirectory()) { - throw new IllegalArgumentException("Not a directory or not found: " + path.getAbsolutePath()); - } - this.path = path; - } + } + private static File getPath (String pathString) { + File path = new File(pathString); + if (!path.exists() || !path.isDirectory()) { + path = null; + throw new IllegalArgumentException("Not a directory or not found: " + path.getAbsolutePath()); + } + return path; } public List getAllFeeds () { ArrayList ret = new ArrayList(); - + // s3 storage + if (DataManager.useS3) { + // TODO: add method for retrieval of all s3 feeds + } // local storage - if (path != null) { + else { for (File file : path.listFiles()) { ret.add(file.getName()); } } + + return ret; + } + + public Long getFeedLastModified (String id) { + // s3 storage + if (DataManager.useS3){ + return s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getLastModified().getTime(); + } + else { + File feed = getFeed(id); + return feed != null ? feed.lastModified() : null; + } + } + + public void deleteFeed (String id) { // s3 storage + if (DataManager.useS3){ + s3Client.deleteObject(s3Bucket, getS3Key(id)); + } else { + File feed = getFeed(id); + if (feed != null && feed.exists()) + feed.delete(); + } + } + public Long getFeedSize (String id) { + // s3 storage + if (DataManager.useS3) { + return s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getContentLength(); } - return ret; + else { + File feed = getFeed(id); + return feed != null ? feed.length() : null; + } + } + + private AWSCredentials getAWSCreds () { + if (this.s3CredentialsFilename != null) { + return new ProfileCredentialsProvider(this.s3CredentialsFilename, "default").getCredentials(); + } else { + // default credentials providers, e.g. IAM role + return new DefaultAWSCredentialsProviderChain().getCredentials(); + } + } + + private String getS3Key (String id) { + return s3Prefix + id; } /** @@ -94,149 +150,213 @@ public File getFeed (String id) { // local storage if (path != null) { File feed = new File(path, id); - if (!feed.exists()) return null; // don't let folks get feeds outside of the directory - if (!feed.getParentFile().equals(path)) return null; - else return feed; + if (feed.getParentFile().equals(path) && feed.exists()) return feed; } // s3 storage - else { - - if (this.s3Bucket != null) { - String keyName = s3Prefix + id; - AWSCredentials creds; - if (this.s3CredentialsFilename != null) { - creds = new ProfileCredentialsProvider(this.s3CredentialsFilename, "default").getCredentials(); - } else { - // default credentials providers, e.g. IAM role - creds = new DefaultAWSCredentialsProviderChain().getCredentials(); - } - try { - LOG.info("Downloading feed from s3"); - AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider()); - S3Object object = s3Client.getObject( - new GetObjectRequest(s3Bucket, keyName)); - InputStream objectData = object.getObjectContent(); - - // Process the objectData stream. - File tempFile = File.createTempFile("test", ".zip"); - try (FileOutputStream out = new FileOutputStream(tempFile)) { - IOUtils.copy(objectData, out); - out.close(); - objectData.close(); - } - tempFile.deleteOnExit(); - return tempFile; - } catch (IOException e) { - e.printStackTrace(); - } catch (AmazonServiceException ase) { - LOG.error("Error downloading from s3"); - ase.printStackTrace(); - } - + else if (s3Bucket != null) { + AWSCredentials creds = getAWSCreds(); + try { + LOG.info("Downloading feed from s3"); + S3Object object = s3Client.getObject( + new GetObjectRequest(s3Bucket, getS3Key(id))); + InputStream objectData = object.getObjectContent(); + + return createTempFile(objectData); + } catch (AmazonServiceException ase) { + LOG.error("Error downloading from s3"); + ase.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); } - return null; } - + return null; } /** * Create a new feed with the given ID. */ public File newFeed (String id, InputStream inputStream, FeedSource feedSource) { - // local storage - if (path != null) { - File out = new File(path, id); - FileOutputStream outStream; + // s3 storage + if (DataManager.useS3) { +// return uploadToS3(inputStream, id, feedSource); + try { + // create temp file +// File file = createTempFile(inputStream); - // store latest as feed-source-id.zip - if (feedSource != null) { - File copy = new File(path, feedSource.id + ".zip"); - FileOutputStream copyStream; - try { - copyStream = new FileOutputStream(copy); + File file = storeFeedLocally(id, inputStream, feedSource); + // let gtfsCache handle loading feed to s3 + if (file != null) { + String apiId = id.replace(".zip", ""); + System.out.println(file.length()); +// GtfsApiController.gtfsApi.getFeedSource(apiId); - } catch (FileNotFoundException e) { - LOG.error("Unable to save latest at {}", copy); - return null; } + return file; + } catch (Exception e) { + e.printStackTrace(); } + } + // local storage + else if (path != null) { + return storeFeedLocally(id, inputStream, feedSource); + } + return null; + } - try { - outStream = new FileOutputStream(out); - - } catch (FileNotFoundException e) { - LOG.error("Unable to open {}", out); - return null; - } + private File storeFeedLocally(String id, InputStream inputStream, FeedSource feedSource) { +// File out = new File(path, id); +// FileOutputStream outStream; - // copy the file - ReadableByteChannel rbc = Channels.newChannel(inputStream); + // store latest as feed-source-id.zip + if (feedSource != null) { try { - outStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - outStream.close(); - return out; + return copyFileUsingInputStream(id, inputStream, feedSource); } catch (IOException e) { - LOG.error("Unable to transfer from upload to saved file."); - return null; + e.printStackTrace(); } +// return copyFileUsingFilesCopy(id, inputStream, feedSource); +// File copy = new File(path, feedSource.id + ".zip"); +// FileOutputStream copyStream; +// try { +// copyStream = new FileOutputStream(copy); +// } catch (FileNotFoundException e) { +// LOG.error("Unable to save latest at {}", copy); +// return null; +// } } - // s3 storage - else { - // upload to S3, if applicable - if(this.s3Bucket != null) { - String keyName = s3Prefix + id; - AWSCredentials creds; - - if (this.s3CredentialsFilename != null) { - creds = new ProfileCredentialsProvider(this.s3CredentialsFilename, "default").getCredentials(); - } - else { - // default credentials providers, e.g. IAM role - creds = new DefaultAWSCredentialsProviderChain().getCredentials(); - } - - try { - // Use tempfile - File tempFile = File.createTempFile("test", ".zip"); - tempFile.deleteOnExit(); - try (FileOutputStream out = new FileOutputStream(tempFile)) { - IOUtils.copy(inputStream, out); - out.close(); - inputStream.close(); - } - - LOG.info("Uploading feed to S3 from inputstream"); - AmazonS3 s3client = new AmazonS3Client(creds); - s3client.putObject(new PutObjectRequest( - s3Bucket, keyName, tempFile)); - - if (feedSource != null){ - LOG.info("Copying feed on s3 to latest version"); - // copy to [name]-latest.zip - String copyKey = s3Prefix + feedSource.id + ".zip"; - CopyObjectRequest copyObjRequest = new CopyObjectRequest( - this.s3Bucket, keyName, this.s3Bucket, copyKey); - s3client.copyObject(copyObjRequest); - } +// try { +// outStream = new FileOutputStream(out); +// } catch (FileNotFoundException e) { +// LOG.error("Unable to open {}", out); +// return null; +// } +// +// // copy the file +// ReadableByteChannel rbc = Channels.newChannel(inputStream); +// try { +// outStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); +// outStream.close(); +// return out; +// } catch (IOException e) { +// LOG.error("Unable to transfer from upload to saved file."); +// return null; +// } + return null; + } + private File copyFileUsingFilesCopy(String id, InputStream inputStream, FeedSource feedSource) { + final Path latest = Paths.get(String.valueOf(path), feedSource.id + ".zip"); + final Path version = Paths.get(String.valueOf(path), id); + try { + Files.copy(inputStream, latest, StandardCopyOption.REPLACE_EXISTING); + LOG.info("Storing feed locally {}", id); + Files.copy(inputStream, version, StandardCopyOption.REPLACE_EXISTING); + return version.toFile(); + } catch (IOException e) { + e.printStackTrace(); + LOG.error("Unable to save latest at {}", version); + } + return null; + } - return tempFile; + private File copyFileUsingInputStream(String id, InputStream inputStream, FeedSource feedSource) throws IOException { + OutputStream output = null; + File out = new File(path, id); + try { + output = new FileOutputStream(out); + byte[] buf = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buf)) > 0) { + output.write(buf, 0, bytesRead); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + inputStream.close(); + output.close(); + try { + System.out.println(out.length()); +// GtfsApiController.gtfsApi.getFeedSource(id.replace(".zip", "")); +// GTFSFeed feed = DataManager.gtfsCache.put(id.replace(".zip", ""), out); + } catch (Exception e) { + e.printStackTrace(); + } + return out; + } + } + private File createTempFile (InputStream in) throws IOException { + final File tempFile = File.createTempFile("test", ".zip"); + tempFile.deleteOnExit(); + try (FileOutputStream out = new FileOutputStream(tempFile)) { + IOUtils.copy(in, out); + } catch (Exception e) { + e.printStackTrace(); + } + return tempFile; + } - } + private File uploadToS3 (InputStream inputStream, String id, FeedSource feedSource) { + if(this.s3Bucket != null) { + try { + // Use tempfile + LOG.info("Creating temp file for {}", id); + File tempFile = createTempFile(inputStream); + + LOG.info("Uploading feed {} to S3 from tempfile", id); + TransferManager tm = new TransferManager(getAWSCreds()); + PutObjectRequest request = new PutObjectRequest(s3Bucket, getS3Key(id), tempFile); + // Subscribe to the event and provide event handler. + TLongList transferredBytes = new TLongArrayList(); + long totalBytes = tempFile.length(); + LOG.info("Total kilobytes: {}", totalBytes / 1000); + request.setGeneralProgressListener(progressEvent -> { + if (transferredBytes.size() == 75) { + LOG.info("Each dot is {} kilobytes",transferredBytes.sum() / 1000); + } + if (transferredBytes.size() % 75 == 0) { + System.out.print("."); + } +// LOG.info("Uploaded {}/{}", transferredBytes.sum(), totalBytes); + transferredBytes.add(progressEvent.getBytesTransferred()); + }); + // TransferManager processes all transfers asynchronously, + // so this call will return immediately. + Upload upload = tm.upload(request); - catch (AmazonServiceException ase) { - LOG.error("Error uploading feed to S3"); - ase.printStackTrace(); - return null; - } catch (IOException e) { + try { + // You can block and wait for the upload to finish + upload.waitForCompletion(); + } catch (AmazonClientException amazonClientException) { + System.out.println("Unable to upload file, upload aborted."); + amazonClientException.printStackTrace(); + } catch (InterruptedException e) { e.printStackTrace(); } +// s3Client.putObject(); + + if (feedSource != null){ + LOG.info("Copying feed on s3 to latest version"); + // copy to [feedSourceId].zip + String copyKey = s3Prefix + feedSource.id + ".zip"; + CopyObjectRequest copyObjRequest = new CopyObjectRequest( + this.s3Bucket, getS3Key(id), this.s3Bucket, copyKey); + s3Client.copyObject(copyObjRequest); + } + return tempFile; + } catch (AmazonServiceException ase) { + LOG.error("Error uploading feed to S3"); + ase.printStackTrace(); + return null; + } catch (IOException e) { + e.printStackTrace(); } - return null; } + return null; } } \ No newline at end of file From 41c3b90e77cd839260fdbbc50e3267284d3623ad Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:36:30 -0400 Subject: [PATCH 026/323] more refactoring for gtfs-api GTFSFeed handling --- .../datatools/manager/DataManager.java | 128 +++--- .../controllers/api/FeedSourceController.java | 129 +++--- .../api/FeedVersionController.java | 355 ++++++--------- .../controllers/api/GtfsApiController.java | 15 +- .../controllers/api/ProjectController.java | 404 ++++++++++-------- .../jobs/BuildTransportNetworkJob.java | 13 +- .../manager/jobs/FetchProjectFeedsJob.java | 7 +- .../manager/jobs/FetchSingleFeedJob.java | 2 +- .../manager/jobs/ProcessSingleFeedJob.java | 5 + .../manager/persistence/FeedStore.java | 30 +- 10 files changed, 503 insertions(+), 585 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index 547ff9a97..7c88c85e4 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -1,5 +1,6 @@ package com.conveyal.datatools.manager; +import com.amazonaws.services.s3.AmazonS3Client; import com.conveyal.datatools.manager.auth.Auth0Connection; import com.conveyal.datatools.manager.controllers.DumpController; @@ -18,12 +19,12 @@ import com.conveyal.datatools.manager.models.Project; import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.CorsFilter; -import com.conveyal.datatools.manager.utils.ResponseError; import com.conveyal.gtfs.GTFSCache; import com.conveyal.gtfs.api.ApiMain; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.gson.Gson; import org.apache.http.concurrent.Cancellable; import org.slf4j.Logger; @@ -32,6 +33,7 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -63,37 +65,31 @@ public class DataManager { public static Map> userJobsMap = new HashMap<>(); public static Map autoFetchMap = new HashMap<>(); - public final static ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1); - + public final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); public static GTFSCache gtfsCache; public static String feedBucket; public static String cacheDirectory; public static String bucketFolder; +// public final AmazonS3Client s3Client; + public static boolean useS3; + public static final String apiPrefix = "/api/manager/"; + private static List apiFeedSources = new ArrayList<>(); public static void main(String[] args) throws IOException { // load config - FileInputStream in; - - if (args.length == 0) - in = new FileInputStream(new File("config.yml")); - else - in = new FileInputStream(new File(args[0])); - - ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - config = mapper.readTree(in); - - ObjectMapper serverMapper = new ObjectMapper(new YAMLFactory()); - serverConfig = serverMapper.readTree(new File("config_server.yml")); + loadConfig(args); // set port - if(config.get("application").has("port")) { - port(Integer.parseInt(config.get("application").get("port").asText())); + if (getConfigProperty("application.port") != null) { + port(Integer.parseInt(getConfigPropertyAsText("application.port"))); } + useS3 = getConfigPropertyAsText("application.data.use_s3_storage").equals("true"); + cacheDirectory = getConfigPropertyAsText("application.data.gtfs") + "/cache"; // initialize map of auto fetched projects for (Project p : Project.getAll()) { @@ -104,19 +100,20 @@ public static void main(String[] args) throws IOException { } } } - cacheDirectory = getConfigPropertyAsText("application.data.gtfs") + "/cache"; - File cacheDir = new File(cacheDirectory); - if (!cacheDir.isDirectory()) { - cacheDir.mkdir(); - } + feedBucket = getConfigPropertyAsText("application.data.gtfs_s3_bucket"); bucketFolder = FeedStore.s3Prefix; - gtfsCache = new GTFSCache(feedBucket, bucketFolder, cacheDir); + if (useS3) { + LOG.info("Initializing gtfs-api for bucket {}/{} and cache dir {}", feedBucket, bucketFolder, cacheDirectory); + gtfsCache = new GTFSCache(feedBucket, bucketFolder, FeedStore.basePath); + } + else { + LOG.info("Initializing gtfs cache locally (no s3 bucket) {}", FeedStore.basePath); + gtfsCache = new GTFSCache(null, FeedStore.basePath); + } CorsFilter.apply(); - String apiPrefix = "/api/manager/"; - // core controllers ProjectController.register(apiPrefix); FeedSourceController.register(apiPrefix); @@ -127,7 +124,7 @@ public static void main(String[] args) throws IOException { // Editor routes if ("true".equals(getConfigPropertyAsText("modules.editor.enabled"))) { - gtfsConfig = mapper.readTree(new File("gtfs.yml")); + gtfsConfig = yamlMapper.readTree(new File("gtfs.yml")); AgencyController.register(apiPrefix); CalendarController.register(apiPrefix); RouteController.register(apiPrefix); @@ -150,7 +147,7 @@ public static void main(String[] args) throws IOException { } if (isModuleEnabled("gtfsplus")) { GtfsPlusController.register(apiPrefix); - gtfsPlusConfig = mapper.readTree(new File("gtfsplus.yml")); + gtfsPlusConfig = yamlMapper.readTree(new File("gtfsplus.yml")); } if (isModuleEnabled("user_admin")) { UserController.register(apiPrefix); @@ -164,42 +161,39 @@ public static void main(String[] args) throws IOException { Auth0Connection.checkUser(request); }); - // lazy load feeds if new one is requested - if ("true".equals(getConfigPropertyAsText("modules.gtfsapi.load_on_fetch"))) { - before(apiPrefix + "*", (request, response) -> { - String feeds = request.queryParams("feed"); - if (feeds != null) { - String[] feedIds = feeds.split(","); - for (String feedId : feedIds) { - FeedSource fs = FeedSource.get(feedId); - if (fs == null) { - continue; - } - else if (!GtfsApiController.gtfsApi.registeredFeedSources.contains(fs.id) && !apiFeedSources.contains(fs.id)) { - apiFeedSources.add(fs.id); - - LoadGtfsApiFeedJob loadJob = new LoadGtfsApiFeedJob(fs); - new Thread(loadJob).start(); - halt(202, "Initializing feed load..."); - } - else if (apiFeedSources.contains(fs.id) && !GtfsApiController.gtfsApi.registeredFeedSources.contains(fs.id)) { - halt(202, "Loading feed, please try again later"); - } - } - - } - }); - } - + // lazy load by feed source id if new one is requested +// if ("true".equals(getConfigPropertyAsText("modules.gtfsapi.load_on_fetch"))) { +// before(apiPrefix + "*", (request, response) -> { +// String feeds = request.queryParams("feed"); +// if (feeds != null) { +// String[] feedIds = feeds.split(","); +// for (String feedId : feedIds) { +// FeedSource fs = FeedSource.get(feedId); +// if (fs == null) { +// continue; +// } +// else if (!GtfsApiController.gtfsApi.registeredFeedSources.contains(fs.id) && !apiFeedSources.contains(fs.id)) { +// apiFeedSources.add(fs.id); +// +// LoadGtfsApiFeedJob loadJob = new LoadGtfsApiFeedJob(fs); +// new Thread(loadJob).start(); +// halt(202, "Initializing feed load..."); +// } +// else if (apiFeedSources.contains(fs.id) && !GtfsApiController.gtfsApi.registeredFeedSources.contains(fs.id)) { +// halt(202, "Loading feed, please try again later"); +// } +// } +// +// } +// }); +// } + // return "application/json" for all API routes after(apiPrefix + "*", (request, response) -> { - - // only set content type if successful response -// if (response.status() < 300) { - response.type("application/json"); -// } + response.type("application/json"); response.header("Content-Encoding", "gzip"); }); + // return js for any other request get("/main.js", (request, response) -> { try (InputStream stream = DataManager.class.getResourceAsStream("/public/main.js")) { return IOUtils.toString(stream); @@ -233,10 +227,6 @@ else if (apiFeedSources.contains(fs.id) && !GtfsApiController.gtfsApi.registered // if the resource doesn't exist we just carry on. } }); -// exception(IllegalArgumentException.class,(e,req,res) -> { -// res.status(400); -// res.body(new Gson().toJson(new ResponseError(e))); -// }); registerExternalResources(); } @@ -285,7 +275,17 @@ private static void registerExternalResources() { registerExternalResource(new TransitFeedsFeedResource()); } } + private static void loadConfig (String[] args) throws IOException { + FileInputStream in; + if (args.length == 0) + in = new FileInputStream(new File("config.yml")); + else + in = new FileInputStream(new File(args[0])); + + config = yamlMapper.readTree(in); + serverConfig = yamlMapper.readTree(new File("config_server.yml")); + } private static void registerExternalResource(ExternalFeedResource resource) { feedResources.put(resource.getResourceType(), resource); } diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index d7ba6db0f..edf165d1b 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -10,6 +10,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.Request; import spark.Response; @@ -26,21 +28,13 @@ */ public class FeedSourceController { - + public static final Logger LOG = LoggerFactory.getLogger(FeedSourceController.class); public static JsonManager json = new JsonManager<>(FeedSource.class, JsonViews.UserInterface.class); private static ObjectMapper mapper = new ObjectMapper(); + public static FeedSource getFeedSource(Request req, Response res) { - String id = req.params("id"); - Boolean publicFilter = req.pathInfo().contains("public"); - FeedSource fs = FeedSource.get(id); - if (fs == null) { - halt(404, null); - } - if (publicFilter && !fs.isPublic) { - halt(503); - } - return fs; + return requestFeedSourceById(req, "view"); } public static Collection getAllFeedSources(Request req, Response res) { @@ -124,17 +118,13 @@ public static FeedSource createFeedSource(Request req, Response res) throws IOEx } public static FeedSource updateFeedSource(Request req, Response res) throws IOException { - String id = req.params("id"); - FeedSource source = FeedSource.get(id); - if (source == null) { - halt(404); - } + FeedSource source = requestFeedSourceById(req, "manage"); applyJsonToFeedSource(source, req.body()); source.save(); // notify users after successful save - NotifyUsersForSubscriptionJob notifyFeedJob = new NotifyUsersForSubscriptionJob("feed-updated", id, "Feed property updated for " + source.name); + NotifyUsersForSubscriptionJob notifyFeedJob = new NotifyUsersForSubscriptionJob("feed-updated", source.id, "Feed property updated for " + source.name); Thread notifyThread = new Thread(notifyFeedJob); notifyThread.start(); @@ -146,7 +136,6 @@ public static FeedSource updateFeedSource(Request req, Response res) throws IOEx } public static void applyJsonToFeedSource(FeedSource source, String json) throws IOException { - ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree(json); Iterator> fieldsIter = node.fields(); while (fieldsIter.hasNext()) { @@ -195,10 +184,8 @@ public static void applyJsonToFeedSource(FeedSource source, String json) throws } public static FeedSource updateExternalFeedResource(Request req, Response res) throws IOException { - String id = req.params("id"); - FeedSource source = FeedSource.get(id); + FeedSource source = requestFeedSourceById(req, "manage"); String resourceType = req.queryParams("resourceType"); - ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree(req.body()); Iterator> fieldsIter = node.fields(); @@ -226,15 +213,14 @@ public static FeedSource updateExternalFeedResource(Request req, Response res) t } public static FeedSource deleteFeedSource(Request req, Response res) { - String id = req.params("id"); - FeedSource source = FeedSource.get(id); + FeedSource source = requestFeedSourceById(req, "manage"); - if (source != null) { + try { source.delete(); return source; - } - else { - halt(404); + } catch (Exception e) { + e.printStackTrace(); + halt(400, "Unknown error deleting feed source."); return null; } } @@ -244,47 +230,68 @@ public static FeedSource deleteFeedSource(Request req, Response res) { * @throws JsonProcessingException */ public static FeedVersion fetch (Request req, Response res) throws JsonProcessingException { - Auth0UserProfile userProfile = req.attribute("user"); - FeedSource s = FeedSource.get(req.params("id")); - - System.out.println("fetching feed for source "+ s.name); - // ways to have permission to do this: - // 1) be an admin - // 2) have access to this feed through project permissions - // if all fail, the user cannot do this. + FeedSource s = requestFeedSourceById(req, "manage"); - if (!userProfile.canAdministerProject(s.projectId) && !userProfile.canManageFeed(s.projectId, s.id)) - halt(401); + LOG.info("Fetching feed for source {}", s.name); + Auth0UserProfile userProfile = req.attribute("user"); FetchSingleFeedJob job = new FetchSingleFeedJob(s, userProfile.getUser_id()); - job.run(); -// if (job.getStatus().error) { -// halt(304); -// } + new Thread(job).start(); return job.result; -// return true; } -// /** -// * The current status of a deployment, polled to update the progress dialog. -// * @throws JsonProcessingException -// */ -// public static Object fetchFeedStatus (Request req, Response res) throws JsonProcessingException { -// // this is not access-controlled beyond requiring auth, which is fine -// // there's no good way to know who should be able to see this. -// String id = req.params("id"); -// fetchJobsByFeed.forEach((s, deployJob) -> System.out.println(s)); -// if (!fetchJobsByFeed.containsKey(id)) -// halt(404, "Feed source id '"+id+"' not found"); -// -// FetchSingleFeedJob j = fetchJobsByFeed.get(id); -// -// if (j == null) -// halt(404, "No active job for " + id + " found"); -// -// return j.getStatus(); -// } + /** + * Helper function returns feed source if user has permission for specified action. + * @param req spark Request object from API request + * @param action action type (either "view" or "manage") + * @return + */ + private static FeedSource requestFeedSourceById(Request req, String action) { + String id = req.params("id"); + if (id == null) { + halt("Please specify id param"); + } + return requestFeedSource(req, FeedSource.get(id), action); + } + public static FeedSource requestFeedSource(Request req, FeedSource s, String action) { + Auth0UserProfile userProfile = req.attribute("user"); + Boolean publicFilter = Boolean.valueOf(req.queryParams("public")); + + // check for null feedsource + if (s == null) + halt(400, "Feed source ID does not exist"); + + boolean authorized; + switch (action) { + case "manage": + authorized = userProfile.canManageFeed(s.projectId, s.id); + break; + case "view": + authorized = userProfile.canViewFeed(s.projectId, s.id); + break; + default: + authorized = false; + break; + } + + // if requesting public sources + if (publicFilter){ + // if feed not public and user not authorized, halt + if (!s.isPublic && !authorized) + halt(403, "User not authorized to perform action on feed source"); + // if feed is public, but action is managerial, halt (we shouldn't ever get here, but just in case) + else if (s.isPublic && action.equals("manage")) + halt(403, "User not authorized to perform action on feed source"); + } + else { + if (!authorized) + halt(403, "User not authorized to perform action on feed source"); + } + + // if we make it here, user has permission and it's a valid feedsource + return s; + } public static void register (String apiPrefix) { get(apiPrefix + "secure/feedsource/:id", FeedSourceController::getFeedSource, json::write); options(apiPrefix + "secure/feedsource", (q, s) -> ""); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index 8e7c38c81..e2f0ae4f2 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -1,20 +1,10 @@ package com.conveyal.datatools.manager.controllers.api; -import com.amazonaws.AmazonServiceException; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.auth.profile.ProfileCredentialsProvider; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.AmazonS3Exception; -import com.amazonaws.services.s3.model.GetObjectRequest; -import com.amazonaws.services.s3.model.S3Object; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.BuildTransportNetworkJob; import com.conveyal.datatools.manager.jobs.CreateFeedVersionFromSnapshotJob; import com.conveyal.datatools.manager.jobs.ProcessSingleFeedJob; -import com.conveyal.datatools.manager.jobs.ReadTransportNetworkJob; import com.conveyal.datatools.manager.models.FeedDownloadToken; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; @@ -41,6 +31,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; +import java.util.stream.Collectors; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -53,10 +44,13 @@ import javax.servlet.ServletException; import javax.servlet.http.Part; +import static com.conveyal.datatools.manager.controllers.api.FeedSourceController.requestFeedSource; import static spark.Spark.*; public class FeedVersionController { - + enum Permission { + VIEW, MANAGE + } public static final Logger LOG = LoggerFactory.getLogger(FeedVersionController.class); private static ObjectMapper mapper = new ObjectMapper(); public static JsonManager json = @@ -68,77 +62,26 @@ public class FeedVersionController { * If you pass in ?summarized=true, don't include the full tree of validation results, only the counts. */ public static FeedVersion getFeedVersion (Request req, Response res) throws JsonProcessingException { - String id = req.params("id"); - if (id == null) { - halt(404, "Must specify feed version ID"); - } - FeedVersion v = FeedVersion.get(id); - - if (v == null) { - halt(404, "Version ID does not exist"); - } - FeedSource s = v.getFeedSource(); + FeedVersion v = requestFeedVersion(req, "view"); return v; - // ways to have permission to do this: - // 1) be an admin - // 2) have access to this feed through project permissions - /*if (userProfile.canAdministerProject(s.feedCollectionId) || userProfile.canViewFeed(s.feedCollectionId, s.id)) { - if ("true".equals(request().getQueryString("summarized"))) { - return ok(jsonSummarized.write(new SummarizedFeedVersion(v))).as("application/json"); - } - else { - return ok(json.write(v)).as("application/json"); - } - } - else { - return unauthorized(); - }*/ } - /** - * Grab this feed version's GTFS. - */ - /*public static Result getGtfs (String id) throws JsonProcessingException { - Auth0UserProfile userProfile = getSessionProfile(); - if(userProfile == null) return unauthorized(); - - FeedVersion v = FeedVersion.get(id); - FeedSource s = v.getFeedSource(); - - if (userProfile.canAdministerProject(s.feedCollectionId) || userProfile.canViewFeed(s.feedCollectionId, s.id)) { - return ok(v.getGtfsFile()); - } - else { - return unauthorized(); - } - }*/ - - public static Collection getAllFeedVersions (Request req, Response res) throws JsonProcessingException { + Auth0UserProfile userProfile = req.attribute("user"); + FeedSource s = requestFeedSourceById(req, "view"); - // parse the query parameters - String sourceId = req.queryParams("feedSourceId"); - if (sourceId == null) { - halt("Please specify a feedsource"); - } - Boolean publicFilter = Boolean.valueOf(req.queryParams("public")); - - FeedSource s = FeedSource.get(sourceId); - - Collection versions = new ArrayList<>(); - - for (FeedVersion v : s.getFeedVersions()){ - // if requesting public sources and source is not public; skip source - if (publicFilter && !s.isPublic) - continue; - versions.add(v); + return s.getFeedVersions().stream() + .collect(Collectors.toCollection(ArrayList::new)); + } + private static FeedSource requestFeedSourceById(Request req, String action) { + String id = req.queryParams("feedSourceId"); + if (id == null) { + halt("Please specify feedsourceId param"); } - - return versions; + return requestFeedSource(req, FeedSource.get(id), action); } - /** * Upload a feed version directly. This is done behind Backbone's back, and as such uses * x-multipart-formdata rather than a json blob. This is done because uploading files in a JSON @@ -150,13 +93,14 @@ public static Collection getAllFeedVersions (Request req, Response public static Boolean createFeedVersion (Request req, Response res) throws IOException, ServletException { Auth0UserProfile userProfile = req.attribute("user"); - FeedSource s = FeedSource.get(req.queryParams("feedSourceId")); + FeedSource s = requestFeedSourceById(req, "manage"); - if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) + if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { halt(400, "Feed is autofetched! Cannot upload."); + } FeedVersion v = new FeedVersion(s); - //v.setUser(req.attribute("auth0User")); + v.setUser(userProfile); if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) { MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); @@ -165,7 +109,7 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc Part part = req.raw().getPart("file"); - LOG.info("Saving feed {} from upload", s); + LOG.info("Saving feed from upload {}", s); InputStream uploadStream; @@ -173,6 +117,7 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc uploadStream = part.getInputStream(); v.newGtfsFile(uploadStream); } catch (Exception e) { + e.printStackTrace(); LOG.error("Unable to open input stream from upload"); halt(400, "Unable to read uploaded feed"); } @@ -180,7 +125,9 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc v.hash(); FeedVersion latest = s.getLatest(); - if (latest != null && latest.hash.equals(v.hash)) { + + // Check that hashes don't match (as long as v and latest are not the same entry) + if (latest != null && latest.hash.equals(v.hash) && latest.id != v.id) { v.getGtfsFile().delete(); // Uploaded feed is same as latest version halt(304); @@ -203,7 +150,8 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc public static Boolean createFeedVersionFromSnapshot (Request req, Response res) throws IOException, ServletException { Auth0UserProfile userProfile = req.attribute("user"); - FeedSource s = FeedSource.get(req.queryParams("feedSourceId")); + // TODO: should this be edit privelege? + FeedSource s = requestFeedSourceById(req, "manage"); CreateFeedVersionFromSnapshotJob createFromSnapshotJob = new CreateFeedVersionFromSnapshotJob(s, req.queryParams("snapshotId"), userProfile.getUser_id()); @@ -214,8 +162,8 @@ public static Boolean createFeedVersionFromSnapshot (Request req, Response res) } public static FeedVersion deleteFeedVersion(Request req, Response res) { - String id = req.params("id"); - FeedVersion version = FeedVersion.get(id); + FeedVersion version = requestFeedVersion(req, "manage"); + version.delete(); // renumber the versions @@ -231,6 +179,18 @@ public static FeedVersion deleteFeedVersion(Request req, Response res) { return version; } + private static FeedVersion requestFeedVersion(Request req, String action) { + String id = req.params("id"); + + FeedVersion version = FeedVersion.get(id); + if (version == null) { + halt(404, "Version ID does not exist"); + } + // performs permissions checks for at feed source level and halts if any issues + FeedSource s = requestFeedSource(req, version.getFeedSource(), "manage"); + return version; + } + public static Object getValidationResult(Request req, Response res) { return getValidationResult(req, res, false); } @@ -239,87 +199,84 @@ public static Object getPublicValidationResult(Request req, Response res) { return getValidationResult(req, res, true); } - public static Object getValidationResult(Request req, Response res, boolean checkPublic) { - String id = req.params("id"); - FeedVersion version = FeedVersion.get(id); - - if(checkPublic && !version.getFeedSource().isPublic) { - halt(401, "Not a public feed"); - return null; - } + public static JsonNode getValidationResult(Request req, Response res, boolean checkPublic) { + FeedVersion version = requestFeedVersion(req, "view"); + return version.getValidationResult(); + } - // TODO: separate this out if non s3 bucket - String s3Bucket = DataManager.config.get("application").get("data").get("gtfs_s3_bucket").asText(); - String keyName = "validation/" + version.id + ".json"; - JsonNode n = null; - InputStream objectData = null; - if (s3Bucket != null && DataManager.getConfigProperty("application.data.use_s3_storage").asBoolean() == true) { - AWSCredentials creds; - // default credentials providers, e.g. IAM role - creds = new DefaultAWSCredentialsProviderChain().getCredentials(); - try { - LOG.info("Getting validation results from s3"); - AmazonS3 s3Client = new AmazonS3Client(new ProfileCredentialsProvider()); - S3Object object = s3Client.getObject( - new GetObjectRequest(s3Bucket, keyName)); - objectData = object.getObjectContent(); - } catch (AmazonS3Exception e) { - // if json file does not exist, validate feed. - version.validate(); - version.save(); - halt(503, "Try again later. Validating feed"); - } catch (AmazonServiceException ase) { - LOG.error("Error downloading from s3"); - ase.printStackTrace(); - } + public static JsonNode getIsochrones(Request req, Response res) { + FeedVersion version = requestFeedVersion(req, "view"); + Auth0UserProfile userProfile = req.attribute("user"); + // if tn is null, check first if it's being built, else try reading in tn + if (version.transportNetwork == null) { + buildOrReadTransportNetwork(version, userProfile); + return null; } - // if s3 upload set to false else { - File file = new File(DataManager.getConfigPropertyAsText("application.data.gtfs") + "/validation/" + version.id + ".json"); - try { - objectData = new FileInputStream(file); -// } catch (IOException e) { -// e.printStackTrace(); - } catch (Exception e) { - LOG.warn("Validation does not exist. Validating feed."); - version.validate(); - version.save(); - halt(503, "Try again later. Validating feed"); + // remove version from list of reading network + if (readingNetworkVersionList.contains(version.id)) { + readingNetworkVersionList.remove(version.id); } + AnalystClusterRequest clusterRequest = buildProfileRequest(req); + return getRouterResult(version.transportNetwork, clusterRequest); } + } - // Process the objectData stream. + private static void buildOrReadTransportNetwork(FeedVersion version, Auth0UserProfile userProfile) { + InputStream is = null; try { - n = mapper.readTree(objectData); - if (!n.has("errors") || !n.has("tripsPerDate")) { - throw new Exception("Validation for feed version not up to date"); + if (readingNetworkVersionList.contains(version.id)) { + halt(503, "Try again later. Reading transport network"); + } + else { + is = new FileInputStream(version.getTransportNetworkPath()); + readingNetworkVersionList.add(version.id); + try { + version.transportNetwork = TransportNetwork.read(is); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + // Catch exception if transport network not built yet + catch (Exception e) { + if (DataManager.isModuleEnabled("validator")) { + LOG.warn("Transport network not found. Beginning build."); + BuildTransportNetworkJob btnj = new BuildTransportNetworkJob(version, userProfile.getUser_id()); + Thread tnThread = new Thread(btnj); + tnThread.start(); } + halt(503, "Try again later. Building transport network"); + } + } + + private static JsonNode getRouterResult(TransportNetwork transportNetwork, AnalystClusterRequest clusterRequest) { + PointSet targets = transportNetwork.getGridPointSet(); + StreetMode mode = StreetMode.WALK; + final LinkedPointSet linkedTargets = targets.link(transportNetwork.streetLayer, mode); + RepeatedRaptorProfileRouter router = new RepeatedRaptorProfileRouter(transportNetwork, clusterRequest, linkedTargets, new TaskStatistics()); + ResultEnvelope result = router.route(); - return n; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + JsonGenerator jgen = new JsonFactory().createGenerator(out); + jgen.writeStartObject(); + result.avgCase.writeIsochrones(jgen); + jgen.writeEndObject(); + jgen.close(); + out.close(); + String outString = new String( out.toByteArray(), StandardCharsets.UTF_8 ); + return mapper.readTree(outString); } catch (IOException e) { - // if json file does not exist, validate feed. - version.validate(); - version.save(); - halt(503, "Try again later. Validating feed"); - } catch (Exception e) { e.printStackTrace(); - version.validate(); - version.save(); - halt(503, "Try again later. Validating feed"); } - return null; } - public static Object getIsochrones(Request req, Response res) { - Auth0UserProfile userProfile = req.attribute("user"); - - String id = req.params("id"); - FeedVersion version = FeedVersion.get(id); - - // required + private static AnalystClusterRequest buildProfileRequest(Request req) { + // required fields? Double fromLat = Double.valueOf(req.queryParams("fromLat")); Double fromLon = Double.valueOf(req.queryParams("fromLon")); Double toLat = Double.valueOf(req.queryParams("toLat")); @@ -330,81 +287,22 @@ public static Object getIsochrones(Request req, Response res) { Integer fromTime = req.queryParams("fromTime") != null ? Integer.valueOf(req.queryParams("fromTime")) : 9 * 3600; Integer toTime = req.queryParams("toTime") != null ? Integer.valueOf(req.queryParams("toTime")) : 10 * 3600; - if (version.transportNetwork == null) { - InputStream is = null; - try { - if (readingNetworkVersionList.contains(version.id)) { - halt(503, "Try again later. Reading transport network"); - return null; - } - else { - // ReadTransportNetworkJob rtnj = new ReadTransportNetworkJob(version, userProfile.getUser_id()); -// Thread tnThread = new Thread(rtnj); -// tnThread.start(); - is = new FileInputStream(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + version.feedSourceId + "/" + version.id + "_network.dat"); - readingNetworkVersionList.add(version.id); - try { - version.transportNetwork = TransportNetwork.read(is); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - // Catch FNFE exception transport network not built yet - catch (FileNotFoundException e) { - e.printStackTrace(); - if (DataManager.config.get("modules").get("validator").get("enabled").asBoolean()) { -// new BuildTransportNetworkJob(version).run(); - BuildTransportNetworkJob btnj = new BuildTransportNetworkJob(version, userProfile.getUser_id()); - Thread tnThread = new Thread(btnj); - tnThread.start(); - } - halt(503, "Try again later. Building transport network"); - } - } - - if (version.transportNetwork != null) { - // remove version from list of reading network - if (readingNetworkVersionList.contains(version.id)) { - readingNetworkVersionList.remove(version.id); - } - AnalystClusterRequest clusterRequest = new AnalystClusterRequest(); - clusterRequest.profileRequest = new ProfileRequest(); - clusterRequest.profileRequest.transitModes = EnumSet.of(TransitModes.TRANSIT); - clusterRequest.profileRequest.accessModes = EnumSet.of(LegMode.WALK); - clusterRequest.profileRequest.date = date; - clusterRequest.profileRequest.fromLat = fromLat; - clusterRequest.profileRequest.fromLon = fromLon; - clusterRequest.profileRequest.toLat = toLat; - clusterRequest.profileRequest.toLon = toLon; - clusterRequest.profileRequest.fromTime = fromTime; - clusterRequest.profileRequest.toTime = toTime; - clusterRequest.profileRequest.egressModes = EnumSet.of(LegMode.WALK); - clusterRequest.profileRequest.zoneId = ZoneId.of("America/New_York"); - PointSet targets = version.transportNetwork.getGridPointSet(); - StreetMode mode = StreetMode.WALK; - final LinkedPointSet linkedTargets = targets.link(version.transportNetwork.streetLayer, mode); - RepeatedRaptorProfileRouter router = new RepeatedRaptorProfileRouter(version.transportNetwork, clusterRequest, linkedTargets, new TaskStatistics()); - ResultEnvelope result = router.route(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - try { - JsonGenerator jgen = new JsonFactory().createGenerator(out); - jgen.writeStartObject(); - result.avgCase.writeIsochrones(jgen); - jgen.writeEndObject(); - jgen.close(); - out.close(); - String outString = new String( out.toByteArray(), StandardCharsets.UTF_8 ); - //System.out.println(outString); - ObjectMapper mapper = new ObjectMapper(); - return mapper.readTree(outString); - } catch (IOException e) { - e.printStackTrace(); - } - - - } - return null; + // build request with transit as default mode + AnalystClusterRequest clusterRequest = new AnalystClusterRequest(); + clusterRequest.profileRequest = new ProfileRequest(); + clusterRequest.profileRequest.transitModes = EnumSet.of(TransitModes.TRANSIT); + clusterRequest.profileRequest.accessModes = EnumSet.of(LegMode.WALK); + clusterRequest.profileRequest.date = date; + clusterRequest.profileRequest.fromLat = fromLat; + clusterRequest.profileRequest.fromLon = fromLon; + clusterRequest.profileRequest.toLat = toLat; + clusterRequest.profileRequest.toLon = toLon; + clusterRequest.profileRequest.fromTime = fromTime; + clusterRequest.profileRequest.toTime = toTime; + clusterRequest.profileRequest.egressModes = EnumSet.of(LegMode.WALK); + clusterRequest.profileRequest.zoneId = ZoneId.of("America/New_York"); + + return clusterRequest; } private static Object downloadFeedVersion(FeedVersion version, Response res) { @@ -435,15 +333,7 @@ private static Object downloadFeedVersion(FeedVersion version, Response res) { } public static Boolean renameFeedVersion (Request req, Response res) throws JsonProcessingException { - String id = req.params("id"); - if (id == null) { - halt(404, "Must specify feed version ID"); - } - FeedVersion v = FeedVersion.get(id); - - if (v == null) { - halt(404, "Version ID does not exist"); - } + FeedVersion v = requestFeedVersion(req, "manage"); String name = req.queryParams("name"); if (name == null) { @@ -456,19 +346,19 @@ public static Boolean renameFeedVersion (Request req, Response res) throws JsonP } private static Object downloadFeedVersionDirectly(Request req, Response res) { - FeedVersion version = FeedVersion.get(req.params("id")); + FeedVersion version = requestFeedVersion(req, "view"); return downloadFeedVersion(version, res); } private static FeedDownloadToken getDownloadToken (Request req, Response res) { - FeedVersion version = FeedVersion.get(req.params("id")); + FeedVersion version = requestFeedVersion(req, "view"); FeedDownloadToken token = new FeedDownloadToken(version); token.save(); return token; } private static FeedDownloadToken getPublicDownloadToken (Request req, Response res) { - FeedVersion version = FeedVersion.get(req.params("id")); + FeedVersion version = requestFeedVersion(req, "view"); if(!version.getFeedSource().isPublic) { halt(401, "Not a public feed"); return null; @@ -512,5 +402,4 @@ public static void register (String apiPrefix) { get(apiPrefix + "downloadfeed/:token", FeedVersionController::downloadFeedVersionWithToken); } - } diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java index b00dd1783..bb0261534 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java @@ -34,11 +34,7 @@ public static void register (String apiPrefix) throws IOException { // store list of GTFS feed eTags here List eTagList = new ArrayList<>(); - cacheDirectory = DataManager.getConfigPropertyAsText("application.data.gtfs") + "/cache/api/"; - File dir = new File(cacheDirectory); - if (!dir.isDirectory()) { - dir.mkdirs(); - } + gtfsApi.initialize(DataManager.gtfsCache); // check for use extension... String extensionType = DataManager.getConfigPropertyAsText("modules.gtfsapi.use_extension"); @@ -48,15 +44,13 @@ public static void register (String apiPrefix) throws IOException { feedBucket = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_bucket"); bucketFolder = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_download_prefix"); - gtfsApi.initialize(DataManager.gtfsCache); - eTagList.addAll(registerS3Feeds(feedBucket, cacheDirectory)); // set feedUpdater to poll for new feeds at specified frequency (in seconds) feedUpdater = new FeedUpdater(eTagList, 0, DataManager.getConfigProperty("modules.gtfsapi.update_frequency").asInt()); } // if not using MTC extension - else if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.enabled"))) { + else { LOG.warn("No extension provided for GTFS API"); if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { feedBucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); @@ -65,9 +59,6 @@ else if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.enab else { feedBucket = null; } - // TODO: update other gtfs api conditions to use - LOG.info("Initializing gtfs-api for bucket {}/{} and cache dir {}", feedBucket, bucketFolder, cacheDirectory); - gtfsApi.initialize(DataManager.gtfsCache); } // check for load on startup @@ -82,8 +73,6 @@ else if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.enab } // else, use local directory else { - gtfsApi.initialize(null, cacheDirectory); - // iterate over latest feed versions for (FeedSource fs : FeedSource.getAll()) { FeedVersion v = fs.getLatest(); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 4c67dd892..3dd8fdeab 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -29,7 +29,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -109,8 +111,7 @@ public static Project createProject(Request req, Response res) throws IOExceptio } public static Project updateProject(Request req, Response res) throws IOException { - String id = req.params("id"); - Project proj = Project.get(id); + Project proj = requestProjectById(req, "manage"); applyJsonToProject(proj, req.body()); proj.save(); @@ -119,9 +120,7 @@ public static Project updateProject(Request req, Response res) throws IOExceptio } public static Project deleteProject(Request req, Response res) throws IOException { - String id = req.params("id"); - Project proj = Project.get(id); - + Project proj = requestProjectById(req, "manage"); proj.delete(); return proj; @@ -130,9 +129,8 @@ public static Project deleteProject(Request req, Response res) throws IOExceptio public static Boolean fetch(Request req, Response res) { Auth0UserProfile userProfile = req.attribute("user"); - String id = req.params("id"); - System.out.println("project fetch for " + id); - Project proj = Project.get(id); + Project proj = requestProjectById(req, "manage"); + FetchProjectFeedsJob fetchProjectFeedsJob = new FetchProjectFeedsJob(proj, userProfile.getUser_id()); new Thread(fetchProjectFeedsJob).start(); return true; @@ -140,6 +138,7 @@ public static Boolean fetch(Request req, Response res) { public static void applyJsonToProject(Project proj, String json) throws IOException { JsonNode node = mapper.readTree(json); + boolean updateFetchSchedule = false; Iterator> fieldsIter = node.fields(); while (fieldsIter.hasNext()) { Map.Entry entry = fieldsIter.next(); @@ -189,178 +188,211 @@ else if(entry.getKey().equals("defaultTimeZone")) { } else if(entry.getKey().equals("autoFetchHour")) { proj.autoFetchHour = entry.getValue().asInt(); + updateFetchSchedule = true; } else if(entry.getKey().equals("autoFetchMinute")) { proj.autoFetchMinute = entry.getValue().asInt(); - - // If auto fetch flag is turned on - if (proj.autoFetchFeeds){ - cancelAutoFetch(proj.id); - int interval = 1; // once per day interval - DataManager.autoFetchMap.put(proj.id, scheduleAutoFeedFetch(proj.id, proj.autoFetchHour, proj.autoFetchMinute, interval, proj.defaultTimeZone)); - } - - // otherwise, cancel any existing task for this id - else{ - cancelAutoFetch(proj.id); - } - } - else if(entry.getKey().equals("autoFetchMinute")) { - proj.autoFetchMinute = entry.getValue().asInt(); - - // If auto fetch flag is turned on - if (proj.autoFetchFeeds){ - cancelAutoFetch(proj.id); - int interval = 1; // once per day interval - DataManager.autoFetchMap.put(proj.id, scheduleAutoFeedFetch(proj.id, proj.autoFetchHour, proj.autoFetchMinute, interval, proj.defaultTimeZone)); - } - - // otherwise, cancel any existing task for this id - else{ - cancelAutoFetch(proj.id); - } + updateFetchSchedule = true; } else if(entry.getKey().equals("autoFetchFeeds")) { proj.autoFetchFeeds = entry.getValue().asBoolean(); - - // If auto fetch flag is turned on - if (proj.autoFetchFeeds){ - cancelAutoFetch(proj.id); - int interval = 1; // once per day interval - DataManager.autoFetchMap.put(proj.id, scheduleAutoFeedFetch(proj.id, proj.autoFetchHour, proj.autoFetchMinute, interval, proj.defaultTimeZone)); - } - - // otherwise, cancel any existing task for this id - else{ - cancelAutoFetch(proj.id); - } + updateFetchSchedule = true; } else if(entry.getKey().equals("otpServers")) { - JsonNode otpServers = entry.getValue(); - if (otpServers.isArray()) { - proj.otpServers = new ArrayList<>(); - for (int i = 0; i < otpServers.size(); i++) { - JsonNode otpServer = otpServers.get(i); - - OtpServer otpServerObj = new OtpServer(); - if (otpServer.has("name")) { - JsonNode name = otpServer.get("name"); - otpServerObj.name = name.isNull() ? null : name.asText(); - } + updateOtpServers(proj, entry.getValue()); + } + else if (entry.getKey().equals("buildConfig")) { + updateBuildConfig(proj, entry.getValue()); + } + else if (entry.getKey().equals("routerConfig")) { + updateRouterConfig(proj, entry.getValue()); + } + } + if (updateFetchSchedule) { + // If auto fetch flag is turned on + if (proj.autoFetchFeeds){ + int interval = 1; // once per day interval + DataManager.autoFetchMap.put(proj.id, scheduleAutoFeedFetch(proj.id, proj.autoFetchHour, proj.autoFetchMinute, interval, proj.defaultTimeZone)); + } + // otherwise, cancel any existing task for this id + else{ + cancelAutoFetch(proj.id); + } + } + } - if (otpServer.has("admin")) { - JsonNode admin = otpServer.get("admin"); - otpServerObj.admin = admin.isNull() ? false : admin.asBoolean(); - } + private static void updateOtpServers(Project proj, JsonNode otpServers) { + if (otpServers.isArray()) { + proj.otpServers = new ArrayList<>(); + for (int i = 0; i < otpServers.size(); i++) { + JsonNode otpServer = otpServers.get(i); - if (otpServer.has("publicUrl")) { - JsonNode publicUrl = otpServer.get("publicUrl"); - otpServerObj.publicUrl = publicUrl.isNull() ? null : publicUrl.asText(); + OtpServer otpServerObj = new OtpServer(); + if (otpServer.has("name")) { + JsonNode name = otpServer.get("name"); + otpServerObj.name = name.isNull() ? null : name.asText(); + } + if (otpServer.has("admin")) { + JsonNode admin = otpServer.get("admin"); + otpServerObj.admin = admin.isNull() ? false : admin.asBoolean(); + } + if (otpServer.has("publicUrl")) { + JsonNode publicUrl = otpServer.get("publicUrl"); + otpServerObj.publicUrl = publicUrl.isNull() ? null : publicUrl.asText(); + } + if (otpServer.has("internalUrl") && otpServer.get("internalUrl").isArray()) { + JsonNode internalUrl = otpServer.get("internalUrl"); + for (int j = 0; j < internalUrl.size(); j++) { + if (internalUrl.get(j).isNull()) { + continue; } - if (otpServer.has("internalUrl") && otpServer.get("internalUrl").isArray()) { - JsonNode internalUrl = otpServer.get("internalUrl"); - for (int j = 0; j < internalUrl.size(); j++) { - if (internalUrl.get(j).isNull()) { - continue; - } - String url = internalUrl.get(j).asText(); - if (otpServerObj.internalUrl == null) { - otpServerObj.internalUrl = new ArrayList<>(); - } - otpServerObj.internalUrl.add(url); - } + String url = internalUrl.get(j).asText(); + if (otpServerObj.internalUrl == null) { + otpServerObj.internalUrl = new ArrayList<>(); } - - proj.otpServers.add(otpServerObj); + otpServerObj.internalUrl.add(url); } } + proj.otpServers.add(otpServerObj); } - else if (entry.getKey().equals("buildConfig")) { - JsonNode buildConfig = entry.getValue(); - if(proj.buildConfig == null) proj.buildConfig = new OtpBuildConfig(); + } + } - if(buildConfig.has("subwayAccessTime")) { - JsonNode subwayAccessTime = buildConfig.get("subwayAccessTime"); - // allow client to un-set option via 'null' value - proj.buildConfig.subwayAccessTime = subwayAccessTime.isNull() ? null : subwayAccessTime.asDouble(); - } + private static void updateBuildConfig(Project proj, JsonNode buildConfig) { + if(proj.buildConfig == null) proj.buildConfig = new OtpBuildConfig(); + if(buildConfig.has("subwayAccessTime")) { + JsonNode subwayAccessTime = buildConfig.get("subwayAccessTime"); + // allow client to un-set option via 'null' value + proj.buildConfig.subwayAccessTime = subwayAccessTime.isNull() ? null : subwayAccessTime.asDouble(); + } + if(buildConfig.has("fetchElevationUS")) { + JsonNode fetchElevationUS = buildConfig.get("fetchElevationUS"); + proj.buildConfig.fetchElevationUS = fetchElevationUS.isNull() ? null : fetchElevationUS.asBoolean(); + } + if(buildConfig.has("stationTransfers")) { + JsonNode stationTransfers = buildConfig.get("stationTransfers"); + proj.buildConfig.stationTransfers = stationTransfers.isNull() ? null : stationTransfers.asBoolean(); + } + if (buildConfig.has("fares")) { + JsonNode fares = buildConfig.get("fares"); + proj.buildConfig.fares = fares.isNull() ? null : fares.asText(); + } + } - if(buildConfig.has("fetchElevationUS")) { - JsonNode fetchElevationUS = buildConfig.get("fetchElevationUS"); - proj.buildConfig.fetchElevationUS = fetchElevationUS.isNull() ? null : fetchElevationUS.asBoolean(); - } + /** + * Helper function returns feed source if user has permission for specified action. + * @param req spark Request object from API request + * @param action action type (either "view" or "manage") + * @return + */ + private static Project requestProjectById(Request req, String action) { + String id = req.params("id"); + if (id == null) { + halt("Please specify id param"); + } + return requestProject(req, Project.get(id), action); + } + public static Project requestProject(Request req, Project p, String action) { + Auth0UserProfile userProfile = req.attribute("user"); + Boolean publicFilter = Boolean.valueOf(req.queryParams("public")); - if(buildConfig.has("stationTransfers")) { - JsonNode stationTransfers = buildConfig.get("stationTransfers"); - proj.buildConfig.stationTransfers = stationTransfers.isNull() ? null : stationTransfers.asBoolean(); - } + // check for null feedsource + if (p == null) + halt(400, "Feed source ID does not exist"); + + boolean authorized; + switch (action) { + case "manage": + authorized = userProfile.canAdministerProject(p.id); + break; + case "view": + authorized = false; // userProfile.canViewProject(p.id, p.id); + break; + default: + authorized = false; + break; + } - if (buildConfig.has("fares")) { - JsonNode fares = buildConfig.get("fares"); - proj.buildConfig.fares = fares.isNull() ? null : fares.asText(); - } - } - else if (entry.getKey().equals("routerConfig")) { - JsonNode routerConfig = entry.getValue(); - if (proj.routerConfig == null) proj.routerConfig = new OtpRouterConfig(); + // if requesting public sources +// if (publicFilter){ +// // if feed not public and user not authorized, halt +// if (!p.isPublic && !authorized) +// halt(403, "User not authorized to perform action on feed source"); +// // if feed is public, but action is managerial, halt (we shouldn't ever get here, but just in case) +// else if (p.isPublic && action.equals("manage")) +// halt(403, "User not authorized to perform action on feed source"); +// +// } +// else { +// if (!authorized) +// halt(403, "User not authorized to perform action on feed source"); +// } - if (routerConfig.has("numItineraries")) { - JsonNode numItineraries = routerConfig.get("numItineraries"); - proj.routerConfig.numItineraries = numItineraries.isNull() ? null : numItineraries.asInt(); - } + // if we make it here, user has permission and it's a valid feedsource + return p; + } - if (routerConfig.has("walkSpeed")) { - JsonNode walkSpeed = routerConfig.get("walkSpeed"); - proj.routerConfig.walkSpeed = walkSpeed.isNull() ? null : walkSpeed.asDouble(); - } + private static void updateRouterConfig(Project proj, JsonNode routerConfig) { + if (proj.routerConfig == null) proj.routerConfig = new OtpRouterConfig(); - if (routerConfig.has("carDropoffTime")) { - JsonNode carDropoffTime = routerConfig.get("carDropoffTime"); - proj.routerConfig.carDropoffTime = carDropoffTime.isNull() ? null : carDropoffTime.asDouble(); - } + if (routerConfig.has("numItineraries")) { + JsonNode numItineraries = routerConfig.get("numItineraries"); + proj.routerConfig.numItineraries = numItineraries.isNull() ? null : numItineraries.asInt(); + } - if (routerConfig.has("stairsReluctance")) { - JsonNode stairsReluctance = routerConfig.get("stairsReluctance"); - proj.routerConfig.stairsReluctance = stairsReluctance.isNull() ? null : stairsReluctance.asDouble(); - } + if (routerConfig.has("walkSpeed")) { + JsonNode walkSpeed = routerConfig.get("walkSpeed"); + proj.routerConfig.walkSpeed = walkSpeed.isNull() ? null : walkSpeed.asDouble(); + } - if (routerConfig.has("updaters")) { - JsonNode updaters = routerConfig.get("updaters"); - if (updaters.isArray()) { - proj.routerConfig.updaters = new ArrayList<>(); - for (int i = 0; i < updaters.size(); i++) { - JsonNode updater = updaters.get(i); - - OtpRouterConfig.Updater updaterObj = new OtpRouterConfig.Updater(); - if(updater.has("type")) { - JsonNode type = updater.get("type"); - updaterObj.type = type.isNull() ? null : type.asText(); - } + if (routerConfig.has("carDropoffTime")) { + JsonNode carDropoffTime = routerConfig.get("carDropoffTime"); + proj.routerConfig.carDropoffTime = carDropoffTime.isNull() ? null : carDropoffTime.asDouble(); + } - if(updater.has("sourceType")) { - JsonNode sourceType = updater.get("sourceType"); - updaterObj.sourceType = sourceType.isNull() ? null : sourceType.asText(); - } + if (routerConfig.has("stairsReluctance")) { + JsonNode stairsReluctance = routerConfig.get("stairsReluctance"); + proj.routerConfig.stairsReluctance = stairsReluctance.isNull() ? null : stairsReluctance.asDouble(); + } - if(updater.has("defaultAgencyId")) { - JsonNode defaultAgencyId = updater.get("defaultAgencyId"); - updaterObj.defaultAgencyId = defaultAgencyId.isNull() ? null : defaultAgencyId.asText(); - } + if (routerConfig.has("updaters")) { + updateProjectUpdaters(proj, routerConfig.get("updaters")); + } + } - if(updater.has("url")) { - JsonNode url = updater.get("url"); - updaterObj.url = url.isNull() ? null : url.asText(); - } + private static void updateProjectUpdaters(Project proj, JsonNode updaters) { + if (updaters.isArray()) { + proj.routerConfig.updaters = new ArrayList<>(); + for (int i = 0; i < updaters.size(); i++) { + JsonNode updater = updaters.get(i); - if(updater.has("frequencySec")) { - JsonNode frequencySec = updater.get("frequencySec"); - updaterObj.frequencySec = frequencySec.isNull() ? null : frequencySec.asInt(); - } + OtpRouterConfig.Updater updaterObj = new OtpRouterConfig.Updater(); + if(updater.has("type")) { + JsonNode type = updater.get("type"); + updaterObj.type = type.isNull() ? null : type.asText(); + } - proj.routerConfig.updaters.add(updaterObj); - } - } + if(updater.has("sourceType")) { + JsonNode sourceType = updater.get("sourceType"); + updaterObj.sourceType = sourceType.isNull() ? null : sourceType.asText(); + } + + if(updater.has("defaultAgencyId")) { + JsonNode defaultAgencyId = updater.get("defaultAgencyId"); + updaterObj.defaultAgencyId = defaultAgencyId.isNull() ? null : defaultAgencyId.asText(); + } + + if(updater.has("url")) { + JsonNode url = updater.get("url"); + updaterObj.url = url.isNull() ? null : url.asText(); } + + if(updater.has("frequencySec")) { + JsonNode frequencySec = updater.get("frequencySec"); + updaterObj.frequencySec = frequencySec.isNull() ? null : frequencySec.asInt(); + } + + proj.routerConfig.updaters.add(updaterObj); } } } @@ -594,56 +626,58 @@ public static Project thirdPartySync(Request req, Response res) throws Exception return null; } public static ScheduledFuture scheduleAutoFeedFetch (String id, int hour, int minute, int interval, String timezoneId){ + TimeUnit unit = TimeUnit.DAYS; + try { + // First cancel any already scheduled auto fetch task for this project id. + cancelAutoFetch(id); - // First cancel any already scheduled auto fetch task for this project id. - cancelAutoFetch(id); - Project p = Project.get(id); - if (p == null) - return null; + Project p = Project.get(id); + if (p == null) + return null; - Cancellable task = null; + Cancellable task = null; - ZoneId timezone; - try { - timezone = ZoneId.of(timezoneId); - }catch(Exception e){ - timezone = ZoneId.of("America/New_York"); - } -// ZoneId.systemDefault(); - System.out.println("Using timezone: " + timezone.getId()); + ZoneId timezone; + try { + timezone = ZoneId.of(timezoneId); + }catch(Exception e){ + timezone = ZoneId.of("America/New_York"); + } + LOG.info("Scheduling autofetch for projectID: {}", p.id); - long initialDelay = 0; + long initialDelay = 0; - // NOW in UTC - ZonedDateTime now = LocalDateTime.now().atZone(timezone); - System.out.println("Current time:" + now.toString()); + // NOW in default timezone + ZonedDateTime now = LocalDateTime.now().atZone(timezone); - // Format and parse datetime string - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); - String dtString = String.valueOf(now.getDayOfMonth()) + "/" + String.valueOf(now.getMonth()) + "/" + String.valueOf(now.getYear()) + " " + String.valueOf(hour) + ":" + String.valueOf(minute); - ZonedDateTime startTime = ZonedDateTime.parse(dtString, formatter); - System.out.println("Start time:" + startTime.toString()); + // SCHEDULED START TIME + ZonedDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(hour, minute)).atZone(timezone); + LOG.info("Scheduled start time: {}", startTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); - // Get diff between start time and current time - long diffInMinutes = (startTime.toEpochSecond() - now.toEpochSecond()) / 60; - if ( diffInMinutes >= 0 ){ - initialDelay = diffInMinutes; // delay in minutes - } - else{ - initialDelay = 24 * 60 + diffInMinutes; // wait for one day plus difference (which is negative) - } + // Get diff between start time and current time + long diffInMinutes = (startTime.toEpochSecond() - now.toEpochSecond()) / 60; + if ( diffInMinutes >= 0 ){ + initialDelay = diffInMinutes; // delay in minutes + } + else{ + initialDelay = 24 * 60 + diffInMinutes; // wait for one day plus difference (which is negative) + } - System.out.println("Scheduling the feed auto fetch daemon to kick off in " + String.valueOf(initialDelay / 60) + " hours." ); + LOG.info("Auto fetch begins in {} hours and runs every {} hours", String.valueOf(initialDelay / 60.0), unit.toHours(interval)); - FetchProjectFeedsJob fetchProjectFeedsJob = new FetchProjectFeedsJob(p, null); + FetchProjectFeedsJob fetchProjectFeedsJob = new FetchProjectFeedsJob(p, null); - return DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, initialDelay, interval, TimeUnit.MINUTES); + return DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, initialDelay, interval, unit); + } catch (Exception e) { + e.printStackTrace(); + return null; + } } public static void cancelAutoFetch(String id){ Project p = Project.get(id); if ( p != null && DataManager.autoFetchMap.get(p.id) != null) { - System.out.println("Cancelling the feed auto fetch daemon for projectID: " + p.id); + LOG.info("Cancelling autofetch for projectID: {}", p.id); DataManager.autoFetchMap.get(p.id).cancel(true); } } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java index 14988c633..f026385ff 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java @@ -38,7 +38,18 @@ public BuildTransportNetworkJob (FeedVersion feedVersion, String owner) { @Override public void run() { System.out.println("Building network"); - feedVersion.buildTransportNetwork(eventBus); + try { + feedVersion.buildTransportNetwork(eventBus); + } catch (Exception e) { + e.printStackTrace(); + synchronized (status) { + status.message = "Transport network failed!"; + status.percentComplete = 100; + status.error = true; + status.completed = true; + } + + } synchronized (status) { status.message = "Transport network built successfully!"; status.percentComplete = 100; diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/FetchProjectFeedsJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/FetchProjectFeedsJob.java index a06bbd46b..6512ae335 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/FetchProjectFeedsJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FetchProjectFeedsJob.java @@ -7,6 +7,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.HashMap; import java.util.Map; @@ -27,14 +30,14 @@ public FetchProjectFeedsJob (Project proj, String owner) { @Override public void run() { - LOG.info("fetch job running for proj: " + proj.name); + LOG.info("Fetch job running for {} project at {}", proj.name, ZonedDateTime.now(ZoneId.of("America/New_York"))); result = new HashMap<>(); for(FeedSource feedSource : proj.getProjectFeedSources()) { if (!FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(feedSource.retrievalMethod)) continue; - +// LOG.info(); FetchSingleFeedJob fetchSingleFeedJob = new FetchSingleFeedJob(feedSource, owner); new Thread(fetchSingleFeedJob).start(); diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java index d0aab1da5..a6bf0c648 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java @@ -26,7 +26,7 @@ public FetchSingleFeedJob (FeedSource feedSource, String owner) { @Override public void run() { // TODO: fetch automatically vs. manually vs. in-house - result = feedSource.fetch(eventBus); + result = feedSource.fetch(eventBus, owner); jobFinished(); if (result != null) { new ProcessSingleFeedJob(result, this.owner).run(); diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java index 84f8c22a4..45cb41b42 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java @@ -1,5 +1,6 @@ package com.conveyal.datatools.manager.jobs; +import com.conveyal.datatools.common.status.MonitorableJob; import com.conveyal.datatools.editor.jobs.ProcessGtfsSnapshotMerge; import com.conveyal.datatools.editor.models.Snapshot; import com.conveyal.datatools.manager.DataManager; @@ -24,6 +25,10 @@ public class ProcessSingleFeedJob implements Runnable { public ProcessSingleFeedJob (FeedVersion feedVersion, String owner) { this.feedVersion = feedVersion; this.owner = owner; +// this.status = new MonitorableJob.Status(); +// status.message = "Fetching..."; +// status.percentComplete = 0.0; +// status.uploading = true; } public void run() { diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index d615454be..6b01bb2d1 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -177,25 +177,9 @@ else if (s3Bucket != null) { * Create a new feed with the given ID. */ public File newFeed (String id, InputStream inputStream, FeedSource feedSource) { - // s3 storage + // s3 storage (store locally and let gtfsCache handle loading feed to s3) if (DataManager.useS3) { -// return uploadToS3(inputStream, id, feedSource); - try { - // create temp file -// File file = createTempFile(inputStream); - - File file = storeFeedLocally(id, inputStream, feedSource); - // let gtfsCache handle loading feed to s3 - if (file != null) { - String apiId = id.replace(".zip", ""); - System.out.println(file.length()); -// GtfsApiController.gtfsApi.getFeedSource(apiId); - - } - return file; - } catch (Exception e) { - e.printStackTrace(); - } + return storeFeedLocally(id, inputStream, feedSource); } // local storage else if (path != null) { @@ -205,14 +189,12 @@ else if (path != null) { } private File storeFeedLocally(String id, InputStream inputStream, FeedSource feedSource) { -// File out = new File(path, id); -// FileOutputStream outStream; - // store latest as feed-source-id.zip if (feedSource != null) { try { - return copyFileUsingInputStream(id, inputStream, feedSource); - } catch (IOException e) { +// return copyFileUsingInputStream(id, inputStream, feedSource); + return copyFileUsingFilesCopy(id, inputStream, feedSource); + } catch (Exception e) { e.printStackTrace(); } // return copyFileUsingFilesCopy(id, inputStream, feedSource); @@ -280,8 +262,6 @@ private File copyFileUsingInputStream(String id, InputStream inputStream, FeedSo output.close(); try { System.out.println(out.length()); -// GtfsApiController.gtfsApi.getFeedSource(id.replace(".zip", "")); -// GTFSFeed feed = DataManager.gtfsCache.put(id.replace(".zip", ""), out); } catch (Exception e) { e.printStackTrace(); } From 5f7c722a0bd08bcafaff9187f2df00707ddfcefd Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:37:47 -0400 Subject: [PATCH 027/323] add missing serialVersionUID and update trip pattern to handle multiple stop additions --- .../datatools/editor/controllers/Base.java | 4 ++-- .../controllers/api/StopController.java | 8 ++++++- .../api/TripPatternController.java | 5 ++++- .../editor/models/transit/EditorFeed.java | 1 + .../editor/models/transit/TripPattern.java | 22 +++++++++++++++++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/Base.java b/src/main/java/com/conveyal/datatools/editor/controllers/Base.java index b2f7df0ad..c49c429dd 100755 --- a/src/main/java/com/conveyal/datatools/editor/controllers/Base.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/Base.java @@ -32,9 +32,9 @@ public class Base { } public static String toJson(Object pojo, boolean prettyPrint) - throws JsonMappingException, JsonGenerationException, IOException { + throws IOException { StringWriter sw = new StringWriter(); - JsonGenerator jg = jf.createJsonGenerator(sw); + JsonGenerator jg = jf.createGenerator(sw); if (prettyPrint) { jg.useDefaultPrettyPrinter(); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java index 6ae0455af..7ba2490fe 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java @@ -3,6 +3,9 @@ import com.conveyal.datatools.editor.datastore.FeedTx; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import com.fasterxml.jackson.core.JsonGenerationException; +import com.fasterxml.jackson.core.JsonParseException; +import com.fasterxml.jackson.databind.JsonMappingException; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.conveyal.datatools.editor.controllers.Base; @@ -152,9 +155,12 @@ public static Object createStop(Request req, Response res) { tx.stops.put(stop.id, stop); tx.commit(); json = Base.toJson(stop, false); - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); halt(400); + } catch (Exception e) { + e.printStackTrace(); + throw e; } finally { if (tx != null) tx.rollbackIfOpen(); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 4ba6b2cbf..42f713830 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -15,6 +15,8 @@ import java.util.Collection; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.Request; import spark.Response; @@ -22,7 +24,7 @@ public class TripPatternController { - + private static final Logger LOG = LoggerFactory.getLogger(TripPatternController.class); public static JsonManager json = new JsonManager<>(TripPattern.class, JsonViews.UserInterface.class); @@ -152,6 +154,7 @@ public static Object updateTripPattern(Request req, Response res) { TripPattern.reconcilePatternStops(originalTripPattern, tripPattern, tx); } catch (IllegalStateException e) { tx.rollback(); + LOG.info("Could not save trip pattern", e); halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/EditorFeed.java b/src/main/java/com/conveyal/datatools/editor/models/transit/EditorFeed.java index 58fa23ba5..c4cc533d7 100644 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/EditorFeed.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/EditorFeed.java @@ -17,6 +17,7 @@ * Created by demory on 6/8/16. */ public class EditorFeed extends Model implements Cloneable, Serializable { + private static final long serialVersionUID = 1L; // GTFS Editor defaults public String color; diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java b/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java index 5a4726682..44eec88ce 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java @@ -132,7 +132,7 @@ public static void reconcilePatternStops(TripPattern originalTripPattern, TripPa if (originalStops.size() == 0) return; - // ADDITIONS + // ADDITIONS (IF DIFF == 1) if (originalStops.size() == newStops.size() - 1) { // we have an addition; find it @@ -262,8 +262,26 @@ else if (newStops.get(firstDifferentIndex).stopId.equals(originalStops.get(lastD tx.trips.put(trip.id, trip); } } + // CHECK IF SET OF STOPS ADDED TO END OF LIST + else if (originalStops.size() < newStops.size()) { + // find the left bound of the changed region to check that no stops have changed in between + int firstDifferentIndex = 0; + while (firstDifferentIndex < originalStops.size() && originalStops.get(firstDifferentIndex).stopId.equals(newStops.get(firstDifferentIndex).stopId)) { + firstDifferentIndex++; + } + if (firstDifferentIndex != originalStops.size()) + throw new IllegalStateException("When adding multiple stops to patterns, new stops must all be at the end"); - + for (Trip trip : tx.getTripsByPattern(originalTripPattern.id)) { + + // insert a skipped stop for each new element in newStops + for (int i = firstDifferentIndex; i < newStops.size(); i++) { + trip.stopTimes.add(i, null); + } + // TODO: safe? + tx.trips.put(trip.id, trip); + } + } // OTHER STUFF IS NOT SUPPORTED else { throw new IllegalStateException("Changes to trip pattern stops must be made one at a time"); From 05a517de8dfd3f2a20abc668f5b3f20b2c648bdb Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:39:17 -0400 Subject: [PATCH 028/323] clean up imports, improve pattern creation strategies, hide tutorial --- src/main/client/editor/actions/editor.js | 25 +++-- .../editor/components/EditorHelpModal.js | 15 ++- .../client/editor/components/EditorMap.js | 94 +++++++++++++------ .../client/editor/components/EntityDetails.js | 6 +- .../client/editor/components/FeedInfoPanel.js | 5 +- .../client/editor/components/GtfsEditor.js | 4 +- .../editor/components/PatternStopCard.js | 10 +- .../editor/components/TripPatternList.js | 23 +++-- .../editor/containers/ActiveGtfsEditor.js | 13 ++- src/main/client/editor/reducers/editor.js | 16 ++-- src/main/client/editor/util/gtfs.js | 2 + 11 files changed, 145 insertions(+), 68 deletions(-) diff --git a/src/main/client/editor/actions/editor.js b/src/main/client/editor/actions/editor.js index 35176203d..fad76104b 100644 --- a/src/main/client/editor/actions/editor.js +++ b/src/main/client/editor/actions/editor.js @@ -1,5 +1,4 @@ import JSZip from 'jszip' -import faker from 'faker' import shp from 'shpjs' import { secureFetch, generateUID, generateRandomInt, generateRandomColor, idealTextColor } from '../../common/util/util' @@ -273,6 +272,16 @@ export function cloneGtfsEntity (feedSourceId, component, entityId, save) { } } +export function newGtfsEntities (feedSourceId, component, propsArray, save) { + return function (dispatch, getState) { + Promise.all(propsArray.map(props => { + return dispatch(newGtfsEntity(feedSourceId, component, props, save)) + })).then(results => { + return results + }) + } +} + export function newGtfsEntity (feedSourceId, component, props, save) { return function (dispatch, getState) { if (!props) { @@ -285,7 +294,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { route_id: generateUID(), agency_id: agency ? agency.id : null, route_short_name: generateRandomInt(1, 300), - route_long_name: faker.address.streetName(), + route_long_name: null, route_color: color, route_text_color: idealTextColor(color), route_type: getState().editor.tableData.feedinfo && getState().editor.tableData.feedinfo.defaultRouteType !== null ? getState().editor.tableData.feedinfo.defaultRouteType : 3, @@ -294,7 +303,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { let stopId = generateUID() return { stop_id: stopId, - stop_name: faker.address.streetName(), + stop_name: null, stop_lat: 0, stop_lon: 0, // route_color: color, @@ -315,15 +324,6 @@ export function newGtfsEntity (feedSourceId, component, props, save) { } if (save) { return dispatch(saveActiveGtfsEntity(component, props)) - // .then((entity) => { - // if (props && 'routeId' in props) { - // dispatch(setActiveGtfsEntity(feedSourceId, 'route', props.routeId, component, entity.id)) - // } - // else { - // console.log('setting after save') - // dispatch(setActiveGtfsEntity(feedSourceId, component, entity.id)) - // } - // }) } else { dispatch(createGtfsEntity(feedSourceId, component, props)) if (props && 'routeId' in props) { @@ -335,7 +335,6 @@ export function newGtfsEntity (feedSourceId, component, props, save) { dispatch(setActiveGtfsEntity(feedSourceId, component, 'new')) } } - } } diff --git a/src/main/client/editor/components/EditorHelpModal.js b/src/main/client/editor/components/EditorHelpModal.js index 02232540c..467010abf 100644 --- a/src/main/client/editor/components/EditorHelpModal.js +++ b/src/main/client/editor/components/EditorHelpModal.js @@ -8,10 +8,14 @@ export default class EditorHelpModal extends Component { constructor (props) { super(props) this.state = { - showModal: this.props.show + showModal: this.props.show, + hideTutorial: this.props.hideTutorial } } close () { + if (this.state.hideTutorial !== this.props.hideTutorial) { + this.props.setTutorialHidden(!this.props.hideTutorial) + } this.setState({ showModal: false }) } open () { @@ -55,7 +59,14 @@ export default class EditorHelpModal extends Component { - Do not show when editor opens + + this.setState({hideTutorial: !this.state.hideTutorial})} + > + Do not show when editor opens + + diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 602de73a0..3ac5d62e7 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -17,8 +17,6 @@ import { generateUID } from '../../common/util/util' import { stopToGtfs } from '../util/gtfs' import { getUserMetadataProperty } from '../../common/util/user' import { getConfigProperty } from '../../common/util/config' -// import CircleMarkerWithLabel from './CircleMarkerWithLabel' -// import MarkerWithLabel from './MarkerWithLabel' import MinuteSecondInput from './MinuteSecondInput' // import StopMarkersLayer from './StopMarkersLayer' import StopLayer from '../../scenario-editor/components/StopLayer' @@ -53,6 +51,7 @@ export default class EditorMap extends Component { setActiveEntity: PropTypes.func, updateControlPoint: PropTypes.func, newGtfsEntity: PropTypes.func, + fetchTripPatterns: PropTypes.func, updateMapSetting: PropTypes.func, addControlPoint: PropTypes.func, @@ -147,11 +146,14 @@ export default class EditorMap extends Component { await this.props.saveActiveEntity('trippattern') return updatedShape } + stopToStopTime (stop) { + return {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} + } async addStopToPattern (pattern, stop, index) { let patternStops = [...pattern.patternStops] let coordinates = pattern.shape && pattern.shape.coordinates - let newStop = {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} + let newStop = this.stopToStopTime(stop) // if adding stop to end, also a proxy for extending pattern to point if (typeof index === 'undefined' || index === null) { // if shape coordinates already exist, just extend them @@ -297,6 +299,15 @@ export default class EditorMap extends Component { // remove controlPoint this.props.removeControlPoint(index) } + async addStopsAtPoints (latlngList) { + const stops = [] + for (var i = 0; i < latlngList.length; i++) { + const stop = await this.constructStop(latlngList[i]) + stops.push(stop) + } + const newStops = await this.props.newGtfsEntities(this.props.feedSource.id, 'stop', stops, true) + return newStops.map(s => stopToGtfs(s)) + } async addStopAtPoint (latlng, addToPattern = false, index) { // create stop const stop = await this.constructStop(latlng) @@ -331,6 +342,8 @@ export default class EditorMap extends Component { this.addStopAtPoint(e.latlng, true) break case 'ADD_STOPS_AT_INTERSECTIONS': + // TODO: implement intersection strategy + // extend pattern to click point // get intersections from OSRM @@ -348,28 +361,38 @@ export default class EditorMap extends Component { let initialDistance = lineDistance(this.props.currentPattern.shape, 'meters') // extend pattern to click point - let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) + let endPoint + if (coordinates) { + endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) + } else { + endPoint = {lng: patternStops[0].stop_lon, lat: patternStops[0].stop_lat} + } const updatedShape = await this.extendPatternToPoint(this.props.currentPattern, endPoint, e.latlng) let totalDistance = lineDistance(this.props.currentPattern.shape, 'meters') - let diff = totalDistance - initialDistance - let numIntervals = diff / this.props.editSettings.stopInterval + let distanceAdded = totalDistance - initialDistance + let numIntervals = distanceAdded / this.props.editSettings.stopInterval + const latlngList = [] for (var i = 1; i < numIntervals; i++) { let stopDistance = initialDistance + i * this.props.editSettings.stopInterval // add stops along shape at interval (stopInterval) let position = along(updatedShape, stopDistance, 'meters') let stopLatlng = ll.toLatlng(position.geometry.coordinates) - console.log(stopLatlng) + latlngList.push(stopLatlng) // pass patternStops.length as index to ensure pattern not extended to locaton - const newStop = await this.addStopAtPoint(stopLatlng, true, patternStops.length) - + const newStop = await this.addStopAtPoint(stopLatlng, false, patternStops.length) // add new stop to array - patternStops.push(newStop) + patternStops.push(this.stopToStopTime(newStop)) } + // TODO: switch to adding multiple stops per action (Java controller and action promise need updating) + // const newStops = await this.addStopsAtPoints(latlngList) + // // add new stop to array + // patternStops = [...patternStops, ...newStops.map(s => this.stopToStopTime(s))] + // update and save all new stops to pattern - // this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {patternStops: patternStops}) - // this.props.saveActiveEntity('trippattern') + this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {patternStops: patternStops}) + this.props.saveActiveEntity('trippattern') } break default: @@ -1027,7 +1050,11 @@ export default class EditorMap extends Component { return null } } - + overlayAdded (e) { + if (e.name === 'Route alignments' && !this.props.tripPatterns) { + this.props.fetchTripPatterns(this.props.feedSource.id) + } + } render () { // console.log(this.props) const mapLayers = [ @@ -1048,6 +1075,23 @@ export default class EditorMap extends Component { id: 'mapbox.streets-satellite' } ] + const OVERLAYS = [ + { + name: 'Route alignments', + component: + {this.props.tripPatterns ? this.props.tripPatterns.map((tp) => { + if (!tp.latLngs) return null + return + }) : null} + + }, + { + name: 'Stop locations', + component: + } + ] const { feedSource, activeComponent, activeEntity } = this.props const offset = 0.005 let feedSourceBounds = feedSource && feedSource.latestValidation && feedSource.latestValidation.bounds @@ -1080,7 +1124,8 @@ export default class EditorMap extends Component { onZoomEnd: (e) => this.mapBoundsChanged(e), onMoveEnd: (e) => this.mapBoundsChanged(e), onBaseLayerChange: (e) => this.mapBaseLayerChanged(e, mapLayers), - scrollWheelZoom: true + scrollWheelZoom: true, + onOverlayAdd: (e) => this.overlayAdded(e) } if (this.state.willMount || this.state.zoomToTarget) { mapProps.bounds = bounds @@ -1102,22 +1147,11 @@ export default class EditorMap extends Component { /> ))} - - - {this.props.tripPatterns ? this.props.tripPatterns.map((tp) => { - if (!tp.latLngs) return null - return - }) : null} - - - - - + {OVERLAYS.map((overlay, i) => ( + + {overlay.component} + + ))} { this.getMapComponents(this.props.activeComponent, this.props.activeEntity, this.props.subEntityId) diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 3dae287d2..0d1682c62 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -72,8 +72,9 @@ export default class EntityDetails extends Component { handleClose (field) { this.setState({ [field]: !this.state[field] }) } - shouldComponentUpdate (nextProps) { - return !shallowEqual(nextProps.feedSource, this.props.feedSource) || + shouldComponentUpdate (nextProps, nextState) { + return !shallowEqual(nextState, this.state) || // for color picker + !shallowEqual(nextProps.feedSource, this.props.feedSource) || !shallowEqual(nextProps.subComponent, this.props.subComponent) || !shallowEqual(nextProps.subEntityId, this.props.subEntityId) || !shallowEqual(nextProps.mapState.target, this.props.mapState.target) || @@ -83,7 +84,6 @@ export default class EntityDetails extends Component { !shallowEqual(nextProps.width, this.props.width) } render () { - // console.log(this.props) const approveGtfsDisabled = this.props.project && this.props.feedSource && this.props.user && !this.props.user.permissions.hasFeedPermission(this.props.project.id, this.props.feedSource.id, 'approve-gtfs') const styles = reactCSS({ 'default': { diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index 1341b0c28..fb1992d24 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -2,7 +2,6 @@ import React, {Component, PropTypes} from 'react' import { Button, ButtonGroup, DropdownButton, Dropdown, MenuItem, Tooltip, OverlayTrigger } from 'react-bootstrap' import Icon from 'react-fa' import { browserHistory } from 'react-router' -import ReactCSSTransitionGroup from 'react-addons-css-transition-group' import CreateSnapshotModal from './CreateSnapshotModal' import SelectFileModal from '../../common/components/SelectFileModal.js' @@ -66,7 +65,7 @@ export default class FeedInfoPanel extends Component { : 'Unnamed' return ( - 0 ? 'right' : 'left'}`} transitionEnterTimeout={500} transitionLeaveTimeout={300}> +
- +
) } } diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 72eb1b927..afbfc3de7 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -43,6 +43,7 @@ export default class GtfsEditor extends Component { deleteEntity: PropTypes.func, cloneEntity: PropTypes.func, newGtfsEntity: PropTypes.func, + setTutorialHidden: PropTypes.func, sidebarExpanded: PropTypes.bool, @@ -214,9 +215,10 @@ export default class GtfsEditor extends Component { sidebarExpanded={this.props.sidebarExpanded} {...this.props} /> - {!this.props.activeComponent + {!this.props.activeComponent && !this.props.hideTutorial ? : null } diff --git a/src/main/client/editor/components/PatternStopCard.js b/src/main/client/editor/components/PatternStopCard.js index c38785855..27ade7519 100644 --- a/src/main/client/editor/components/PatternStopCard.js +++ b/src/main/client/editor/components/PatternStopCard.js @@ -128,7 +128,15 @@ export default class PatternStopCard extends Component { {/* Remove from pattern button */} - + { + let patternStops = [...activePattern.patternStops] + patternStops[index].timepoint = !stop.timepoint + updateActiveEntity(activePattern, 'trippattern', {patternStops}) + saveActiveEntity('trippattern') + }} + > Timepoint? diff --git a/src/main/client/editor/components/TripPatternList.js b/src/main/client/editor/components/TripPatternList.js index d8c32b47d..a3b893fb2 100644 --- a/src/main/client/editor/components/TripPatternList.js +++ b/src/main/client/editor/components/TripPatternList.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import { Table, Button, ButtonGroup, Checkbox, DropdownButton, MenuItem, ButtonToolbar, Collapse, FormGroup, OverlayTrigger, Tooltip, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' +import { Table, Button, ButtonGroup, Alert, Checkbox, DropdownButton, MenuItem, ButtonToolbar, Collapse, FormGroup, OverlayTrigger, Tooltip, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' import {Icon} from 'react-fa' import { sentence as toSentenceCase } from 'change-case' import Rcslider from 'rc-slider' @@ -93,7 +93,6 @@ export default class TripPatternList extends Component { this.props.saveActiveEntity('trippattern') } render () { - // console.log(this.props) const { feedSource, activeEntity } = this.props const activePatternId = this.props.subEntityId if (!activeEntity.tripPatterns) { @@ -252,7 +251,7 @@ export default class TripPatternList extends Component { }) }} > - Delete + Delete , + From ec9a3579417811b022e6e4480df38e79e640fb47 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:42:15 -0400 Subject: [PATCH 035/323] clean up imports --- src/main/client/common/components/EditableCell.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/client/common/components/EditableCell.js b/src/main/client/common/components/EditableCell.js index c064c6f44..eba2f5022 100644 --- a/src/main/client/common/components/EditableCell.js +++ b/src/main/client/common/components/EditableCell.js @@ -1,6 +1,5 @@ import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' -import ContentEditable from 'react-contenteditable' import moment from 'moment' export default class EditableCell extends Component { From d60bb4030fbf7ad0c4e158368119dc7eff60ea34 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:42:30 -0400 Subject: [PATCH 036/323] add props --- src/main/client/common/components/SelectFileModal.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/client/common/components/SelectFileModal.js b/src/main/client/common/components/SelectFileModal.js index 8d6e349d4..4ab2ff4f7 100644 --- a/src/main/client/common/components/SelectFileModal.js +++ b/src/main/client/common/components/SelectFileModal.js @@ -12,6 +12,7 @@ export default class SelectFileModal extends React.Component { } close () { + if (this.props.onClose) this.props.onClose() this.setState({ showModal: false }) @@ -24,6 +25,7 @@ export default class SelectFileModal extends React.Component { title: props.title, body: props.body, onConfirm: props.onConfirm, + onClose: props.onClose, errorMessage: props.errorMessage }) } else { From 592dee970b8d2a03e610fb551d6af42ecbda9c9e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:44:03 -0400 Subject: [PATCH 037/323] hide tutorial prop --- src/main/client/common/components/Sidebar.js | 12 ++++++++++-- src/main/client/common/containers/ActiveSidebar.js | 7 +++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 75f963c0b..c1d76cad7 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -150,12 +150,20 @@ export default class Sidebar extends Component { close={() => closePopover()} >
- { this.props.setSidebarExpanded(!this.props.expanded) }} > Show Sidebar Labels - + { this.props.setTutorialHidden(!this.props.hideTutorial) }} + > + Hide editor tutorial +
diff --git a/src/main/client/common/containers/ActiveSidebar.js b/src/main/client/common/containers/ActiveSidebar.js index 2beeb20ef..9cdabbb2e 100644 --- a/src/main/client/common/containers/ActiveSidebar.js +++ b/src/main/client/common/containers/ActiveSidebar.js @@ -6,14 +6,16 @@ import { login, logout, resetPassword } from '../../manager/actions/user' import { setActiveProject } from '../../manager/actions/projects' import { setActiveLanguage } from '../../manager/actions/languages' import { setJobMonitorVisible, removeRetiredJob } from '../../manager/actions/status' -import { setSidebarExpanded } from '../../manager/actions/ui' +import { setSidebarExpanded, setTutorialHidden } from '../../manager/actions/ui' const mapStateToProps = (state, ownProps) => { return { expanded: state.ui.sidebarExpanded, + hideTutorial: state.ui.hideTutorial, username: state.user.profile ? state.user.profile.email : null, // userPicture: state.user.profile ? state.user.profile.picture : null, userIsAdmin: state.user.profile && state.user.permissions.isApplicationAdmin(), + profile: state.user.profile, projects: state.projects ? state.projects : null, languages: state.languages ? state.languages : ['English', 'Español', 'Français'], jobMonitor: state.status.jobMonitor @@ -29,7 +31,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { setActiveLanguage: (language) => { dispatch(setActiveLanguage(language)) }, setJobMonitorVisible: (visible) => { dispatch(setJobMonitorVisible(visible)) }, removeRetiredJob: (job) => { dispatch(removeRetiredJob(job)) }, - setSidebarExpanded: (value) => { dispatch(setSidebarExpanded(value)) } + setSidebarExpanded: (value) => { dispatch(setSidebarExpanded(value)) }, + setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) }, } } From b804a5a25f1f5e03a5b8511131b89350ff01a0a3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:44:31 -0400 Subject: [PATCH 038/323] clean up imports and fix auth0 --- src/main/client/common/user/Auth0Manager.js | 5 ++--- src/main/client/common/util/util.js | 4 +--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index ba414f14d..3ec79fa40 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -14,7 +14,7 @@ export default class Auth0Manager { constructor (props) { this.props = props this.lock = new Auth0Lock(props.client_id, props.domain) - this.auth0 = new Auth0({clientID: props.client_id, domain: props.domain}) + // this.auth0 = new Auth0({clientID: props.client_id, domain: props.domain}) } loggedIn () { @@ -43,10 +43,9 @@ export default class Auth0Manager { // redirect to "targetUrl" if any let newLocation = hash.state || '' browserHistory.push(newLocation) - } else { // check if logged in elsewhere via SSO - this.auth0.getSSOData((err, data) => { + this.lock.$auth0.getSSOData((err, data) => { if (!err && data.sso) { // there is! redirect to Auth0 for SSO this.lock.$auth0.signin({ diff --git a/src/main/client/common/util/util.js b/src/main/client/common/util/util.js index 05b123077..302bdce39 100644 --- a/src/main/client/common/util/util.js +++ b/src/main/client/common/util/util.js @@ -1,10 +1,8 @@ -import { UserAuthWrapper } from 'redux-auth-wrapper' -import { routerActions, push } from 'react-router-redux' import fetch from 'isomorphic-fetch' import { setErrorMessage } from '../../manager/actions/status' import gravatar from 'gravatar' -export function defaultSorter(a, b) { +export function defaultSorter (a, b) { if(a.isCreating && !b.isCreating) return -1 if(!a.isCreating && b.isCreating) return 1 if(a.name.toLowerCase() < b.name.toLowerCase()) return -1 From 809e24b9bfcfcc952a1713b391315c867419a013 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:45:05 -0400 Subject: [PATCH 039/323] set active feed version action --- src/main/client/manager/actions/feeds.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/client/manager/actions/feeds.js b/src/main/client/manager/actions/feeds.js index fcede0370..eba95ed3e 100644 --- a/src/main/client/manager/actions/feeds.js +++ b/src/main/client/manager/actions/feeds.js @@ -28,6 +28,7 @@ export function fetchProjectFeeds (projectId) { .then(response => response.json()) .then(feedSources => { dispatch(receiveFeedSources(projectId, feedSources)) + return feedSources }) } } @@ -269,6 +270,13 @@ export function receiveFeedVersions (feedSource, feedVersions) { } } +export function setActiveVersion (feedVersion) { + return { + type: 'SET_ACTIVE_FEEDVERSION', + feedVersion + } +} + export function fetchFeedVersions (feedSource, unsecured) { return function (dispatch, getState) { dispatch(requestingFeedVersions()) From 59fec83c1f39cce14bd8e117750b63e54cc335db Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:45:42 -0400 Subject: [PATCH 040/323] use version id w/o zip --- src/main/client/manager/components/reporter/containers/Feed.js | 2 +- .../client/manager/components/reporter/containers/Patterns.js | 2 +- .../client/manager/components/reporter/containers/Routes.js | 2 +- src/main/client/manager/components/reporter/containers/Stops.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/client/manager/components/reporter/containers/Feed.js b/src/main/client/manager/components/reporter/containers/Feed.js index 22bf0d681..6e9cd5c8b 100644 --- a/src/main/client/manager/components/reporter/containers/Feed.js +++ b/src/main/client/manager/components/reporter/containers/Feed.js @@ -11,7 +11,7 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedId = ownProps.version.feedSource.id + const feedId = ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { if (!initialProps.feed.fetchStatus.fetched) { diff --git a/src/main/client/manager/components/reporter/containers/Patterns.js b/src/main/client/manager/components/reporter/containers/Patterns.js index 58768017b..63abf1415 100644 --- a/src/main/client/manager/components/reporter/containers/Patterns.js +++ b/src/main/client/manager/components/reporter/containers/Patterns.js @@ -14,7 +14,7 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedId = ownProps.version.feedSource.id + const feedId = ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { if (!initialProps.routes.fetchStatus.fetched) { diff --git a/src/main/client/manager/components/reporter/containers/Routes.js b/src/main/client/manager/components/reporter/containers/Routes.js index 020d22bdb..58396f3c2 100644 --- a/src/main/client/manager/components/reporter/containers/Routes.js +++ b/src/main/client/manager/components/reporter/containers/Routes.js @@ -14,7 +14,7 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedId = ownProps.version.feedSource.id + const feedId = ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { if(!initialProps.routes.fetchStatus.fetched) { diff --git a/src/main/client/manager/components/reporter/containers/Stops.js b/src/main/client/manager/components/reporter/containers/Stops.js index 8ad396bf8..754ec8111 100644 --- a/src/main/client/manager/components/reporter/containers/Stops.js +++ b/src/main/client/manager/components/reporter/containers/Stops.js @@ -17,7 +17,7 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedId = ownProps.version.feedSource.id + const feedId = ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { if (!initialProps.routes.fetchStatus.fetched) { From f424f24a6077f376e17ee663de2fe46094b67248 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:46:08 -0400 Subject: [PATCH 041/323] fix hover issue with modal --- .../manager/components/FeedSourceTable.js | 42 ++++++++++---- .../manager/components/FeedSourceViewer.js | 55 ++++--------------- 2 files changed, 42 insertions(+), 55 deletions(-) diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index 551cd37cc..603a1f370 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -41,6 +41,7 @@ export default class FeedSourceTable extends Component { } render () { + console.log(this.state) const messages = getComponentMessages('ProjectViewer') const hover = this.props.deleteFeedSource(fs)} uploadFeed={(fs, file) => this.props.uploadFeed(fs, file)} fetchFeed={(fs) => this.props.fetchFeed(fs)} + setHold={(fs) => this.setState({holdFeedSource: fs})} /> return ( @@ -67,6 +69,8 @@ export default class FeedSourceTable extends Component { saveFeedSource={this.props.saveFeedSource} hoverComponent={hover} onHover={(fs) => this.setState({activeFeedSource: fs})} + active={this.state.activeFeedSource && this.state.activeFeedSource.id === feedSource.id} + hold={this.state.holdFeedSource && this.state.holdFeedSource.id === feedSource.id} /> }) : @@ -88,7 +92,9 @@ class FeedSourceTableRow extends Component { updateFeedSourceProperty: PropTypes.func, saveFeedSource: PropTypes.func, - onHover: PropTypes.func + onHover: PropTypes.func, + active: PropTypes.bool, + hold: PropTypes.bool } constructor (props) { @@ -100,10 +106,11 @@ class FeedSourceTableRow extends Component { } shouldComponentUpdate (nextProps, nextState) { - return !shallowEqual(nextProps.feedSource, this.props.feedSource) || this.state.hovered !== nextState.hovered + return !shallowEqual(nextProps.feedSource, this.props.feedSource) || this.props.active !== nextProps.active } render () { + console.log('row', this.state) const fs = this.props.feedSource const na = (N/A) const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') @@ -137,19 +144,17 @@ class FeedSourceTableRow extends Component { key={fs.id} // bsStyle={fs.isPublic ? 'default' : 'warning'} onMouseEnter={() => { - if (!this.state.hovered) { - this.setState({ hovered: true }) this.props.onHover(fs) - } }} onMouseLeave={() => { - if (this.state.hovered) this.setState({ hovered: false }) + if (!this.props.hold) + this.props.onHover(false) }} > {this.state.hovered + >{this.props.active ? this.props.hoverComponent : null } @@ -193,7 +198,7 @@ class FeedSourceTableRow extends Component { } }} onMouseLeave={() => { - if (this.state.hovered) this.setState({ hovered: false }) + if (!this.props.active && this.state.hovered) this.setState({ hovered: false }) }} > @@ -259,7 +264,15 @@ class FeedSourceDropdown extends Component { fetchFeed: PropTypes.func, uploadFeed: PropTypes.func } - + deleteFeed () { + this.props.setHold(this.props.feedSource) + this.refs['deleteModal'].open() + // this.setState({keepActive: true}) + } + uploadFeed () { + this.props.setHold(this.props.feedSource) + this.refs['uploadModal'].open() + } render () { const fs = this.props.feedSource const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') @@ -274,6 +287,9 @@ class FeedSourceDropdown extends Component { console.log('OK, deleting') this.props.deleteFeedSource(fs) }} + onClose={() => { + this.props.setHold(false) + }} /> { if (isValidZipFile(files[0])) { this.props.uploadFeed(fs, files[0]) + this.props.setHold(false) return true } else { return false } }} + onClose={() => { + this.props.setHold(false) + }} errorMessage='Uploaded file must be a valid zip file (.zip).' /> @@ -298,11 +318,11 @@ class FeedSourceDropdown extends Component { console.log(key) switch (key) { case 'delete': - return this.refs['deleteModal'].open() + return this.deleteFeed() case 'fetch': return this.props.fetchFeed(fs) case 'upload': - return this.refs['uploadModal'].open() + return this.uploadFeed() case 'deploy': return this.props.createDeploymentFromFeedSource(fs) case 'public': diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index 96275d755..22a836221 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -1,32 +1,23 @@ import React, {Component, PropTypes} from 'react' -import fetch from 'isomorphic-fetch' import Icon from 'react-fa' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' import { LinkContainer } from 'react-router-bootstrap' -import { Grid, Row, Col, ListGroup, ListGroupItem, Well, Button, Table, Image, Badge, Panel, Label, Glyphicon, ButtonToolbar, ButtonGroup, Tabs, Tab, FormControl, InputGroup, ControlLabel, FormGroup, Checkbox, DropdownButton, MenuItem } from 'react-bootstrap' +import { Grid, Row, Col, ListGroup, ListGroupItem, Button, Badge, Panel, Glyphicon, ButtonToolbar, Tabs, Tab, FormControl, InputGroup, ControlLabel, FormGroup, Checkbox } from 'react-bootstrap' import { Link, browserHistory } from 'react-router' import moment from 'moment' import numeral from 'numeral' import ManagerPage from '../../common/components/ManagerPage' import Breadcrumbs from '../../common/components/Breadcrumbs' -import EditableTextField from '../../common/components/EditableTextField' import WatchButton from '../../common/containers/WatchButton' import StarButton from '../../common/containers/StarButton' -import { isValidZipFile, retrievalMethodString } from '../../common/util/util' import ExternalPropertiesTable from './ExternalPropertiesTable' import ActiveFeedVersionNavigator from '../containers/ActiveFeedVersionNavigator' import NotesViewer from './NotesViewer' import ActiveEditorFeedSourcePanel from '../../editor/containers/ActiveEditorFeedSourcePanel' import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' -const retrievalMethods = [ - 'FETCHED_AUTOMATICALLY', - 'MANUALLY_UPLOADED', - 'PRODUCED_IN_HOUSE' -] - export default class FeedSourceViewer extends Component { static propTypes = { @@ -40,7 +31,9 @@ export default class FeedSourceViewer extends Component { activeComponent: PropTypes.string, activeSubComponent: PropTypes.string, + componentDidUpdate: PropTypes.func, createDeployment: PropTypes.func, + deleteFeedSource: PropTypes.func, deleteFeedVersionConfirmed: PropTypes.func, downloadFeedClicked: PropTypes.func, externalPropertyChanged: PropTypes.func, @@ -63,14 +56,12 @@ export default class FeedSourceViewer extends Component { super(props) this.state = {} } - componentWillMount () { this.props.onComponentMount(this.props) // this.setState({willMount: true}) } componentDidMount () { this.setState({didMount: true}) - } componentDidUpdate (prevProps) { this.props.componentDidUpdate(prevProps, this.props) @@ -90,32 +81,17 @@ export default class FeedSourceViewer extends Component { } return numeral(avg || 0).format('0 b') } - deleteFeedVersion (feedSource, feedVersion) { + confirmDeleteFeedSource (feedSource) { this.refs['page'].showConfirmModal({ - title: 'Delete Feed Version?', - body: 'Are you sure you want to delete this version?', + title: 'Delete Feed Source?', + body: 'Are you sure you want to delete this feed source? This action cannot be undone and all feed versions will be deleted.', onConfirm: () => { - this.props.deleteFeedVersionConfirmed(feedSource, feedVersion) + this.props.deleteFeedSource(feedSource) + .then(() => browserHistory.push(`/project/${feedSource.projectId}`)) } }) } - // showUploadFeedModal () { - // this.refs.page.showSelectFileModal({ - // title: 'Upload Feed', - // body: 'Select a GTFS feed (.zip) to upload:', - // onConfirm: (files) => { - // if (isValidZipFile(files[0])) { - // this.props.uploadFeed(this.props.feedSource, files[0]) - // return true - // } else { - // return false - // } - // }, - // errorMessage: 'Uploaded file must be a valid zip file (.zip).' - // }) - // } - render () { const fs = this.props.feedSource if (this.props.isFetching && this.state.didMount) { @@ -158,15 +134,6 @@ export default class FeedSourceViewer extends Component { const editGtfsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'edit-gtfs') const dateFormat = getConfigProperty('application.date_format') const autoFetchFeed = fs.retrievalMethod === 'FETCHED_AUTOMATICALLY' - const fsCenter = fs.latestValidation && fs.latestValidation.bounds - ? `${(fs.latestValidation.bounds.east + fs.latestValidation.bounds.west) / 2},${(fs.latestValidation.bounds.north + fs.latestValidation.bounds.south) / 2}` - : null - const fsOverlay = fsCenter - ? `pin-l-bus(${fsCenter})/` - : '' - const mapUrl = fsCenter - ? `https://api.mapbox.com/v4/mapbox.light/${fsOverlay}${fsCenter},6/1000x400@2x.png?access_token=${getConfigProperty('mapbox.access_token')}` - : '' const resourceType = this.props.activeComponent === 'settings' && this.props.activeSubComponent && this.props.activeSubComponent.toUpperCase() const activeTab = ['settings', 'comments', 'snapshots'].indexOf(this.props.activeComponent) === -1 || typeof this.props.routeParams.feedVersionIndex !== 'undefined' ? '' @@ -188,7 +155,7 @@ export default class FeedSourceViewer extends Component { /> +

Delete this feed source.

Once you delete a feed source, it cannot be recovered.

From 519b7fe2021ffc3df9f8a8dce7f88f9b77ed239c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:46:59 -0400 Subject: [PATCH 042/323] active version update, and improvements to report ui --- .../components/FeedVersionNavigator.js | 19 +++++---- .../manager/components/FeedVersionReport.js | 40 +++++++++++-------- .../manager/components/FeedVersionViewer.js | 2 +- .../containers/ActiveFeedSourceViewer.js | 21 ++-------- .../containers/ActiveFeedVersionNavigator.js | 15 ++++++- 5 files changed, 53 insertions(+), 44 deletions(-) diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index b035393e9..7788aa7aa 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -24,17 +24,18 @@ export default class FeedVersionNavigator extends Component { loadFeedVersionForEditing: PropTypes.func, newNotePostedForVersion: PropTypes.func, notesRequestedForVersion: PropTypes.func, - fetchValidationResult: PropTypes.func + fetchValidationResult: PropTypes.func, + setVersionIndex: PropTypes.func } - constructor (props) { super(props) this.state = {} } - componentWillReceiveProps (nextProps) { + if (nextProps.feedVersionIndex !== this.props.feedVersionIndex && nextProps.feedSource && nextProps.feedSource.feedVersions) { + this.props.setVersionIndex(nextProps.feedSource, nextProps.feedVersionIndex, false) + } } - render () { console.log(this.props) const versionTitleStyle = { @@ -103,7 +104,7 @@ export default class FeedVersionNavigator extends Component { {/* Previous Version Button */} @@ -111,7 +112,11 @@ export default class FeedVersionNavigator extends Component { {/* Version Selector Dropdown */} browserHistory.push(`${publicPrefix}/feed/${fs.id}/version/${key}`)} + onSelect={(key) => { + if (key !== this.props.feedVersionIndex) { + this.props.setVersionIndex(fs, key) + } + }} > {versions.map((version, k) => { k = k + 1 @@ -122,7 +127,7 @@ export default class FeedVersionNavigator extends Component { {/* Next Version Button */} diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index ed2915eda..912bb63c4 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -1,9 +1,9 @@ import React, {Component, PropTypes} from 'react' -import { Row, Col, Image, Button, Panel, Label, Tabs, Tab, Glyphicon, FormControl, ButtonGroup, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Row, Col, Image, Button, Panel, ControlLabel, Label, Tabs, Tab, Glyphicon, FormControl, ButtonGroup, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' import Icon from 'react-fa' import numeral from 'numeral' - +import Rcslider from 'rc-slider' import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' @@ -103,7 +103,8 @@ export default class FeedVersionReport extends Component { // maxHeight: '500px', pagination: true, options: { - paginationShowsTotal: true + paginationShowsTotal: true, + sizePerPageList: [10, 20, 50, 100] } } return
- - this.setState({isochroneBand: +evt.target.value})} - bsSize='large' - value={this.state.isochroneBand} - > - {ISO_BANDS.map(v => { - return ( - - ) - })} - + + Travel time + this.setState({isochroneBand: value * 60})} + step={5} + marks={{ + 15: '¼ hour', + 30: '½ hour', + 60: 1 hour, + 120: '2 hours' + }} + tipFormatter={(value) => { + return `${value} minutes` + }} + /> {getMessage(messages, 'noVersionsExist')}

+ if (!version) return

{getMessage(messages, 'noVersionsExist')}

if (this.props.listView) { // List view of feed versions diff --git a/src/main/client/manager/containers/ActiveFeedSourceViewer.js b/src/main/client/manager/containers/ActiveFeedSourceViewer.js index 647870ce2..287fac9cf 100644 --- a/src/main/client/manager/containers/ActiveFeedSourceViewer.js +++ b/src/main/client/manager/containers/ActiveFeedSourceViewer.js @@ -10,6 +10,7 @@ import { downloadFeedViaToken, fetchFeedSourceAndProject, fetchFeedSource, + deleteFeedSource, fetchFeedVersions, fetchNotesForFeedSource, fetchNotesForFeedVersion, @@ -47,25 +48,10 @@ const mapStateToProps = (state, ownProps) => { feedSource = null } let isFetching = state.projects.isFetching - /*let feedVersionIndex - let routeVersionIndex = +ownProps.routeParams.feedVersionIndex - let hasVersionIndex = typeof ownProps.routeParams.feedVersionIndex !== 'undefined' - if (feedSource && typeof feedSource.feedVersions !== 'undefined') { - if ((hasVersionIndex && isNaN(routeVersionIndex)) || routeVersionIndex > feedSource.feedVersions.length || routeVersionIndex < 0) { - console.log(`version index ${routeVersionIndex} is invalid`) - // cannot use browserHistory.push in middle of state transition - // browserHistory.push(`/feed/${feedSourceId}`) - window.location.href = `/feed/${feedSourceId}` - } else { - feedVersionIndex = hasVersionIndex - ? routeVersionIndex - : feedSource.feedVersions.length - } - }*/ + return { feedSource, feedSourceId, - //feedVersionIndex, activeComponent: ownProps.routeParams.subpage, activeSubComponent: ownProps.routeParams.subsubpage, project, @@ -86,7 +72,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { loadFeedVersionForEditing: (feedVersion) => { dispatch(loadFeedVersionForEditing(feedVersion)) }, - deleteFeedVersionConfirmed: (feedSource, feedVersion) => { + deleteFeedVersion: (feedSource, feedVersion) => { dispatch(deleteFeedVersion(feedSource, feedVersion)) }, downloadFeedClicked: (feedVersion) => { dispatch(downloadFeedViaToken(feedVersion)) }, @@ -151,6 +137,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { } }, fetchFeed: (feedSource) => { dispatch(runFetchFeed(feedSource)) }, + deleteFeedSource: (feedSource) => { return dispatch(deleteFeedSource(feedSource)) }, updateUserSubscription: (profile, target, subscriptionType) => { dispatch(updateTargetForSubscription(profile, target, subscriptionType)) }, uploadFeed: (feedSource, file) => { dispatch(uploadFeed(feedSource, file)) }, fetchValidationResult: (feedSource, feedVersion) => { diff --git a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js index b2e973500..bb41877be 100644 --- a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js +++ b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js @@ -1,4 +1,5 @@ import { connect } from 'react-redux' +import { browserHistory } from 'react-router' import FeedVersionNavigator from '../components/FeedVersionNavigator' @@ -8,7 +9,8 @@ import { fetchNotesForFeedVersion, fetchValidationResult, postNoteForFeedVersion, - renameFeedVersion + renameFeedVersion, + setActiveVersion } from '../actions/feeds' import { loadFeedVersionForEditing } from '../../editor/actions/snapshots' @@ -24,7 +26,8 @@ const mapStateToProps = (state, ownProps) => { routeVersionIndex > ownProps.feedSource.feedVersions.length || routeVersionIndex < 0) { console.log(`version index ${routeVersionIndex} is invalid`) - // cannot use browserHistory.push in middle of state transition + + // CANNOT use browserHistory.push in middle of state transition // browserHistory.push(`/feed/${feedSourceId}`) window.location.href = `/feed/${ownProps.feedSource.id}` } else { @@ -41,7 +44,15 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { + const publicPrefix = ownProps.isPublic ? '/public' : '' return { + setVersionIndex: (fs, index, push = true) => { + dispatch(setActiveVersion(fs.feedVersions[index - 1])) + + if (push) { + browserHistory.push(`${publicPrefix}/feed/${fs.id}/version/${index}`) + } + }, loadFeedVersionForEditing: (feedVersion) => { dispatch(loadFeedVersionForEditing(feedVersion)) }, From c11b288e62f7c812eee51b3f482e2298363b806f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 10:47:31 -0400 Subject: [PATCH 043/323] action/reducer for hideTutorial --- src/main/client/manager/actions/ui.js | 14 ++++++++++++++ src/main/client/manager/reducers/ui.js | 9 +++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/client/manager/actions/ui.js b/src/main/client/manager/actions/ui.js index 6820d716c..5af9ae315 100644 --- a/src/main/client/manager/actions/ui.js +++ b/src/main/client/manager/actions/ui.js @@ -13,3 +13,17 @@ export function setSidebarExpanded (value) { dispatch(updateUserMetadata(getState().user.profile, {sidebarExpanded: value})) } } + +export function settingTutorialHidden (value) { + return { + type: 'SETTING_TUTORIAL_HIDDEN', + value + } +} + +export function setTutorialHidden (value) { + return function (dispatch, getState) { + dispatch(settingTutorialHidden(value)) + dispatch(updateUserMetadata(getState().user.profile, {hideTutorial: value})) + } +} diff --git a/src/main/client/manager/reducers/ui.js b/src/main/client/manager/reducers/ui.js index 0a1c4caee..9360e2d70 100644 --- a/src/main/client/manager/reducers/ui.js +++ b/src/main/client/manager/reducers/ui.js @@ -2,14 +2,19 @@ import update from 'react-addons-update' import { getUserMetadataProperty } from '../../common/util/user' const ui = (state = { - sidebarExpanded: true + sidebarExpanded: true, + hideTutorial: false }, action) => { switch (action.type) { case 'USER_LOGGED_IN': + const hideTutorial = getUserMetadataProperty(action.profile, 'hideTutorial') const sidebarExpanded = getUserMetadataProperty(action.profile, 'sidebarExpanded') return update(state, { - sidebarExpanded: { $set: sidebarExpanded } + sidebarExpanded: { $set: sidebarExpanded }, + hideTutorial: { $set: hideTutorial }, }) + case 'SETTING_TUTORIAL_VISIBILITY': + return update(state, { hideTutorial: { $set: action.value } }) case 'SETTING_SIDEBAR_EXPANDED': return update(state, { sidebarExpanded: { $set: action.value } }) default: From 28e19ecd9eff27823c8ff9fb3ecc0501fca605ba Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 11:01:18 -0400 Subject: [PATCH 044/323] log status when loading legacy dump --- .../conveyal/datatools/manager/controllers/DumpController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index 929c4dc0f..eed2c4f22 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -117,6 +117,7 @@ public static boolean loadLegacy (Request req, Response res) throws Exception { Iterator> fieldsIter = node.fields(); while (fieldsIter.hasNext()) { Map.Entry entry = fieldsIter.next(); + LOG.info("Loading {} {}...", entry.getValue().size(), entry.getKey()); switch(entry.getKey()) { case "feedCollections": for(int i=0; i< entry.getValue().size(); i++) { From 3fe3d530211836135ccdeb0f24657bfc7c084e58 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 11:16:35 -0400 Subject: [PATCH 045/323] add default break --- .../conveyal/datatools/manager/controllers/DumpController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index eed2c4f22..cbb4742d9 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -137,7 +137,8 @@ public static boolean loadLegacy (Request req, Response res) throws Exception { } FeedVersion.commit(); break; - + default: + break; } } return true; From dc811bc25e2c692267eda2ffb8b90957c269d65a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 12:36:41 -0400 Subject: [PATCH 046/323] fix validate force skip --- .../datatools/manager/controllers/DumpController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index cbb4742d9..d85a65fb6 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -10,6 +10,7 @@ import com.conveyal.datatools.manager.models.Project; import com.conveyal.datatools.manager.utils.json.JsonManager; import com.conveyal.datatools.manager.utils.json.JsonUtil; +import com.conveyal.gtfs.validator.json.LoadStatus; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -194,13 +195,14 @@ private static void loadLegacyFeedVersion (JsonNode node) throws Exception { } public static boolean validateAll (Request req, Response res) throws Exception { - System.out.println("validating all feeds..."); + LOG.info("validating all feeds..."); for(FeedVersion version: FeedVersion.getAll()) { - if(!req.queryParams("force").equals("true") && version.validationResult != null) continue; - LOG.info("Validating " + version.id); + if(!req.queryParams("force").equals("true") && version.validationResult != null && !version.validationResult.loadStatus.equals(LoadStatus.SUCCESS)) continue; + LOG.info("Validating {}", version.id); version.validate(); version.save(); } + LOG.info("Finished validation..."); return true; } From be8d3700890408c3b3d27426ddecbcb43cace764 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 12:41:51 -0400 Subject: [PATCH 047/323] skip only if status is SUCCESS --- .../conveyal/datatools/manager/controllers/DumpController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index d85a65fb6..d2b65fd3f 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -197,7 +197,7 @@ private static void loadLegacyFeedVersion (JsonNode node) throws Exception { public static boolean validateAll (Request req, Response res) throws Exception { LOG.info("validating all feeds..."); for(FeedVersion version: FeedVersion.getAll()) { - if(!req.queryParams("force").equals("true") && version.validationResult != null && !version.validationResult.loadStatus.equals(LoadStatus.SUCCESS)) continue; + if(!req.queryParams("force").equals("true") && version.validationResult != null && version.validationResult.loadStatus.equals(LoadStatus.SUCCESS)) continue; LOG.info("Validating {}", version.id); version.validate(); version.save(); From c02552279070072b318e22d89f2557e1cf0fd03e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 15:29:05 -0400 Subject: [PATCH 048/323] bump gtfs-api --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 52b640521..fa2b5dc7f 100644 --- a/pom.xml +++ b/pom.xml @@ -168,7 +168,7 @@ com.conveyal gtfs-api - 0.4-SNAPSHOT + 0.5-SNAPSHOT From ff851c3ec8201b90c1f5bf99a25d9df7895f6177 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 15:29:25 -0400 Subject: [PATCH 049/323] fix feed storage --- .../manager/persistence/FeedStore.java | 49 +++---------------- 1 file changed, 8 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index 6b01bb2d1..f7d581b07 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -192,58 +192,30 @@ private File storeFeedLocally(String id, InputStream inputStream, FeedSource fee // store latest as feed-source-id.zip if (feedSource != null) { try { -// return copyFileUsingInputStream(id, inputStream, feedSource); - return copyFileUsingFilesCopy(id, inputStream, feedSource); + File version = writeFileUsingInputStream(id, inputStream, feedSource); + File latest = copyInputStreamToLatest(inputStream, feedSource); + return version; } catch (Exception e) { e.printStackTrace(); } -// return copyFileUsingFilesCopy(id, inputStream, feedSource); -// File copy = new File(path, feedSource.id + ".zip"); -// FileOutputStream copyStream; -// try { -// copyStream = new FileOutputStream(copy); -// } catch (FileNotFoundException e) { -// LOG.error("Unable to save latest at {}", copy); -// return null; -// } } - -// try { -// outStream = new FileOutputStream(out); -// } catch (FileNotFoundException e) { -// LOG.error("Unable to open {}", out); -// return null; -// } -// -// // copy the file -// ReadableByteChannel rbc = Channels.newChannel(inputStream); -// try { -// outStream.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); -// outStream.close(); -// return out; -// } catch (IOException e) { -// LOG.error("Unable to transfer from upload to saved file."); -// return null; -// } return null; } - private File copyFileUsingFilesCopy(String id, InputStream inputStream, FeedSource feedSource) { + private File copyInputStreamToLatest(InputStream inputStream, FeedSource feedSource) { final Path latest = Paths.get(String.valueOf(path), feedSource.id + ".zip"); - final Path version = Paths.get(String.valueOf(path), id); try { Files.copy(inputStream, latest, StandardCopyOption.REPLACE_EXISTING); - LOG.info("Storing feed locally {}", id); - Files.copy(inputStream, version, StandardCopyOption.REPLACE_EXISTING); - return version.toFile(); + LOG.info("Copying version to latest {}", feedSource); + return latest.toFile(); } catch (IOException e) { e.printStackTrace(); - LOG.error("Unable to save latest at {}", version); + LOG.error("Unable to save latest at {}", feedSource); } return null; } - private File copyFileUsingInputStream(String id, InputStream inputStream, FeedSource feedSource) throws IOException { + private File writeFileUsingInputStream(String id, InputStream inputStream, FeedSource feedSource) throws IOException { OutputStream output = null; File out = new File(path, id); try { @@ -260,11 +232,6 @@ private File copyFileUsingInputStream(String id, InputStream inputStream, FeedSo } finally { inputStream.close(); output.close(); - try { - System.out.println(out.length()); - } catch (Exception e) { - e.printStackTrace(); - } return out; } } From 525878f01bf5390fcd393101bc6e546160af42c4 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 15:29:50 -0400 Subject: [PATCH 050/323] remove ref to unused cacheDir --- src/main/java/com/conveyal/datatools/manager/DataManager.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index 7c88c85e4..17c629c09 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -70,7 +70,6 @@ public class DataManager { public static GTFSCache gtfsCache; public static String feedBucket; - public static String cacheDirectory; public static String bucketFolder; // public final AmazonS3Client s3Client; @@ -89,7 +88,6 @@ public static void main(String[] args) throws IOException { port(Integer.parseInt(getConfigPropertyAsText("application.port"))); } useS3 = getConfigPropertyAsText("application.data.use_s3_storage").equals("true"); - cacheDirectory = getConfigPropertyAsText("application.data.gtfs") + "/cache"; // initialize map of auto fetched projects for (Project p : Project.getAll()) { @@ -105,7 +103,7 @@ public static void main(String[] args) throws IOException { bucketFolder = FeedStore.s3Prefix; if (useS3) { - LOG.info("Initializing gtfs-api for bucket {}/{} and cache dir {}", feedBucket, bucketFolder, cacheDirectory); + LOG.info("Initializing gtfs-api for bucket {}/{} and cache dir {}", feedBucket, bucketFolder, FeedStore.basePath); gtfsCache = new GTFSCache(feedBucket, bucketFolder, FeedStore.basePath); } else { From ed08920b7f02df0008585d541b655af7d87c170a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 20 Oct 2016 16:56:43 -0400 Subject: [PATCH 051/323] fix npe --- .../manager/controllers/DumpController.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index d2b65fd3f..6f177aa94 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -4,6 +4,7 @@ import com.conveyal.datatools.manager.auth.Auth0Users; import com.conveyal.datatools.manager.models.Deployment; import com.conveyal.datatools.manager.models.FeedSource; +import com.conveyal.datatools.manager.models.FeedValidationResult; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.models.Note; @@ -196,11 +197,21 @@ private static void loadLegacyFeedVersion (JsonNode node) throws Exception { public static boolean validateAll (Request req, Response res) throws Exception { LOG.info("validating all feeds..."); - for(FeedVersion version: FeedVersion.getAll()) { - if(!req.queryParams("force").equals("true") && version.validationResult != null && version.validationResult.loadStatus.equals(LoadStatus.SUCCESS)) continue; + Collection allVersions = FeedVersion.getAll(); + for(FeedVersion version: allVersions) { + boolean force = req.queryParams("force") != null ? req.queryParams("force").equals("true") : false; + FeedValidationResult result = version.validationResult; + if(!force && result != null && result.loadStatus.equals(LoadStatus.SUCCESS)) { + continue; + } LOG.info("Validating {}", version.id); - version.validate(); - version.save(); + try { + version.validate(); + version.save(); + } catch (Exception e) { + LOG.error("Could not validate", e); + halt(400, "Error validating feed"); + } } LOG.info("Finished validation..."); return true; From de194714505f8c6ff4002f4d36491c0e3f60e756 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 11:34:35 -0400 Subject: [PATCH 052/323] store feed locally initially for both s3 and offline --- .../datatools/manager/persistence/FeedStore.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index f7d581b07..7bc5e9665 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -177,15 +177,8 @@ else if (s3Bucket != null) { * Create a new feed with the given ID. */ public File newFeed (String id, InputStream inputStream, FeedSource feedSource) { - // s3 storage (store locally and let gtfsCache handle loading feed to s3) - if (DataManager.useS3) { - return storeFeedLocally(id, inputStream, feedSource); - } - // local storage - else if (path != null) { - return storeFeedLocally(id, inputStream, feedSource); - } - return null; + // For s3 storage (store locally and let gtfsCache handle loading feed to s3) + return storeFeedLocally(id, inputStream, feedSource); } private File storeFeedLocally(String id, InputStream inputStream, FeedSource feedSource) { From 6cd186fc167d4bfbc9629878da84258bd532cd0c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 11:35:03 -0400 Subject: [PATCH 053/323] bump mapdb for issue with histograms --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fa2b5dc7f..a27891e03 100644 --- a/pom.xml +++ b/pom.xml @@ -138,7 +138,7 @@ org.mapdb mapdb - 1.0.6 + 1.0.8 From 93cb546c5db837ee4548b3cf46231b6f2b6c4d10 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 11:35:41 -0400 Subject: [PATCH 054/323] refactor transfer performance --- src/main/client/gtfs/components/gtfsmap.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/client/gtfs/components/gtfsmap.js b/src/main/client/gtfs/components/gtfsmap.js index 0c4887397..f4b29dafc 100644 --- a/src/main/client/gtfs/components/gtfsmap.js +++ b/src/main/client/gtfs/components/gtfsmap.js @@ -107,6 +107,17 @@ export default class GtfsMap extends Component { getIsochroneColor (time) { return time ? 'blue' : 'red' } + renderTransferPerformance (transferPerformance) { + console.log(transferPerformance) + if (!transferPerformance) + return

No transfers found

+ return ( +
    +
  • Typical case: {moment.duration(transferPerformance.typicalCase, 'seconds').humanize()}
  • +
  • Best case: {moment.duration(transferPerformance.bestCase, 'seconds').humanize()}
  • +
  • Worst case: {moment.duration(transferPerformance.worstCase, 'seconds').humanize()}
  • +
) + } renderIsochrones () { let comps = [] const bandTime = this.props.isochroneBand || 60 * 60 @@ -145,7 +156,7 @@ export default class GtfsMap extends Component { return comps } render () { - console.log(this.props) + console.log(this.props, this.state) var mapStyle = { width: this.props.width, // % or px height: `${this.props.height}px` // only px @@ -234,11 +245,7 @@ export default class GtfsMap extends Component { }) } -
    -
  • Typical case: {moment.duration(stop.transferPerformance[this.state[stop.stop_id] || 0].typicalCase, 'seconds').humanize()}
  • -
  • Best case: {moment.duration(stop.transferPerformance[this.state[stop.stop_id] || 0].bestCase, 'seconds').humanize()}
  • -
  • Worst case: {moment.duration(stop.transferPerformance[this.state[stop.stop_id] || 0].worstCase, 'seconds').humanize()}
  • -
+ {this.renderTransferPerformance(stop.transferPerformance[this.state[stop.stop_id] || 0])}
:

No transfers found

} From 22567f51126052c839ec5edb9b318eba2bbc0864 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:20:21 -0400 Subject: [PATCH 055/323] remove thread in fetch --- .../manager/controllers/api/FeedSourceController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index edf165d1b..c6cc8f440 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -236,7 +236,9 @@ public static FeedVersion fetch (Request req, Response res) throws JsonProcessin Auth0UserProfile userProfile = req.attribute("user"); FetchSingleFeedJob job = new FetchSingleFeedJob(s, userProfile.getUser_id()); - new Thread(job).start(); + + // Don't run in thread because we want to return the HTTP status of the fetch operation + job.run(); return job.result; } From fc3837ef243bb26b8347cba0694f2801f7bf1520 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:20:48 -0400 Subject: [PATCH 056/323] fix halts on transport network build --- .../api/FeedVersionController.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index e2f0ae4f2..160f52ee0 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -5,6 +5,7 @@ import com.conveyal.datatools.manager.jobs.BuildTransportNetworkJob; import com.conveyal.datatools.manager.jobs.CreateFeedVersionFromSnapshotJob; import com.conveyal.datatools.manager.jobs.ProcessSingleFeedJob; +import com.conveyal.datatools.manager.jobs.ReadTransportNetworkJob; import com.conveyal.datatools.manager.models.FeedDownloadToken; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; @@ -127,9 +128,10 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc FeedVersion latest = s.getLatest(); // Check that hashes don't match (as long as v and latest are not the same entry) - if (latest != null && latest.hash.equals(v.hash) && latest.id != v.id) { + if (latest != null && latest.hash.equals(v.hash)) { v.getGtfsFile().delete(); // Uploaded feed is same as latest version + v.delete(); halt(304); } @@ -212,7 +214,6 @@ public static JsonNode getIsochrones(Request req, Response res) { // if tn is null, check first if it's being built, else try reading in tn if (version.transportNetwork == null) { buildOrReadTransportNetwork(version, userProfile); - return null; } else { // remove version from list of reading network @@ -222,23 +223,25 @@ public static JsonNode getIsochrones(Request req, Response res) { AnalystClusterRequest clusterRequest = buildProfileRequest(req); return getRouterResult(version.transportNetwork, clusterRequest); } + return null; } private static void buildOrReadTransportNetwork(FeedVersion version, Auth0UserProfile userProfile) { InputStream is = null; try { - if (readingNetworkVersionList.contains(version.id)) { - halt(503, "Try again later. Reading transport network"); - } - else { + if (!readingNetworkVersionList.contains(version.id)) { is = new FileInputStream(version.getTransportNetworkPath()); readingNetworkVersionList.add(version.id); try { - version.transportNetwork = TransportNetwork.read(is); +// version.transportNetwork = TransportNetwork.read(is); + ReadTransportNetworkJob rtnj = new ReadTransportNetworkJob(version, userProfile.getUser_id()); + Thread readThread = new Thread(rtnj); + readThread.start(); } catch (Exception e) { e.printStackTrace(); } } + halt(202, "Try again later. Reading transport network"); } // Catch exception if transport network not built yet catch (Exception e) { @@ -248,7 +251,7 @@ private static void buildOrReadTransportNetwork(FeedVersion version, Auth0UserPr Thread tnThread = new Thread(btnj); tnThread.start(); } - halt(503, "Try again later. Building transport network"); + halt(202, "Try again later. Building transport network"); } } From 61117bed058ea7bb6924e3f53c633998f8b0250f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:21:30 -0400 Subject: [PATCH 057/323] fix halt code for waiting to deploy --- .../datatools/manager/controllers/api/DeploymentController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java index f8608b1bf..314f40263 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java @@ -253,7 +253,7 @@ public static Object deploy (Request req, Response res) throws IOException { if (currentJob != null && !currentJob.getStatus().completed) { // send a 503 service unavailable as it is not possible to deploy to this target right now; // someone else is deploying - halt(503, "Deployment currently in progress for target: " + target); + halt(202, "Deployment currently in progress for target: " + target); LOG.warn("Deployment currently in progress for target: " + target); } } From 90bbc7ea798a26d4921d1593e655fe49e774036e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:21:54 -0400 Subject: [PATCH 058/323] fix storing latest --- .../datatools/manager/persistence/FeedStore.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index 7bc5e9665..36df94486 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -19,6 +19,7 @@ import com.conveyal.gtfs.GTFSFeed; import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import com.amazonaws.auth.AWSCredentials; @@ -186,7 +187,7 @@ private File storeFeedLocally(String id, InputStream inputStream, FeedSource fee if (feedSource != null) { try { File version = writeFileUsingInputStream(id, inputStream, feedSource); - File latest = copyInputStreamToLatest(inputStream, feedSource); + copyVersionToLatest(version, feedSource); return version; } catch (Exception e) { e.printStackTrace(); @@ -195,17 +196,15 @@ private File storeFeedLocally(String id, InputStream inputStream, FeedSource fee return null; } - private File copyInputStreamToLatest(InputStream inputStream, FeedSource feedSource) { - final Path latest = Paths.get(String.valueOf(path), feedSource.id + ".zip"); + private void copyVersionToLatest(File version, FeedSource feedSource) { + File latest = new File(String.valueOf(path), feedSource.id + ".zip"); try { - Files.copy(inputStream, latest, StandardCopyOption.REPLACE_EXISTING); + FileUtils.copyFile(version, latest); LOG.info("Copying version to latest {}", feedSource); - return latest.toFile(); } catch (IOException e) { e.printStackTrace(); LOG.error("Unable to save latest at {}", feedSource); } - return null; } private File writeFileUsingInputStream(String id, InputStream inputStream, FeedSource feedSource) throws IOException { From 79fec85ba5c81f48635d1a1476797caab8b8b738 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:22:10 -0400 Subject: [PATCH 059/323] fix halts on feed fetch --- .../conveyal/datatools/manager/models/FeedSource.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index ed4855ab2..aea252165 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -195,7 +195,7 @@ public FeedVersion fetch (EventBus eventBus, String fetchUser) { // TODO: redirects else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - String message = String.format("Saving %s feed. (This may take a while with large feeds.)", this.name); + String message = String.format("Saving %s feed.", this.name); LOG.info(message); statusMap.put("message", message); statusMap.put("percentComplete", 75.0); @@ -211,6 +211,7 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { statusMap.put("percentComplete", 100.0); statusMap.put("error", true); eventBus.post(statusMap); + halt(400, message); return null; } } catch (IOException e) { @@ -221,6 +222,7 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { statusMap.put("error", true); eventBus.post(statusMap); e.printStackTrace(); + halt(400, message); return null; } catch (Exception e) { throw e; @@ -229,14 +231,17 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { // note that anything other than a new feed fetched successfully will have already returned from the function version.hash(); + if (latest != null && version.hash.equals(latest.hash)) { String message = String.format("Feed %s was fetched but has not changed; server operators should add If-Modified-Since support to avoid wasting bandwidth", this.name); LOG.warn(message); version.getGtfsFile().delete(); + version.delete(); statusMap.put("message", message); statusMap.put("percentComplete", 100.0); - statusMap.put("error", false); + statusMap.put("error", true); eventBus.post(statusMap); + halt(304); return null; } else { From afc922594f616eb5daa7471b7d1a08998db1c94d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 21 Oct 2016 13:23:00 -0400 Subject: [PATCH 060/323] comment on memory requirements --- scripts/load.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/load.py b/scripts/load.py index 20a0b4fd8..c9f61386c 100755 --- a/scripts/load.py +++ b/scripts/load.py @@ -2,6 +2,7 @@ # load the database to a fresh server # usage: load.py dump.json http://localhost:9000 # validation: curl -X POST http://localhost:9000/validateAll (add force=true query param to force validation) +# if validating multiple large feeds, you may need to run application with more GBs, e.g. java -Xmx6G -jar target/datatools.jar from sys import argv import urllib2 From c770f11257c521ca22f6ca04865088f40365356a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:37:29 -0400 Subject: [PATCH 061/323] renamed gtfsmap, moved FeedUpdater job --- .../components/{gtfsmap.js => GtfsMap.js} | 0 .../manager/{utils => jobs}/FeedUpdater.java | 41 +++++-------------- 2 files changed, 10 insertions(+), 31 deletions(-) rename src/main/client/gtfs/components/{gtfsmap.js => GtfsMap.js} (100%) rename src/main/java/com/conveyal/datatools/manager/{utils => jobs}/FeedUpdater.java (51%) diff --git a/src/main/client/gtfs/components/gtfsmap.js b/src/main/client/gtfs/components/GtfsMap.js similarity index 100% rename from src/main/client/gtfs/components/gtfsmap.js rename to src/main/client/gtfs/components/GtfsMap.js diff --git a/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java similarity index 51% rename from src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java rename to src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java index 8e2fd3ecd..3423ac768 100644 --- a/src/main/java/com/conveyal/datatools/manager/utils/FeedUpdater.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java @@ -1,14 +1,13 @@ -package com.conveyal.datatools.manager.utils; +package com.conveyal.datatools.manager.jobs; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.S3ObjectSummary; +import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.controllers.api.GtfsApiController; -import com.conveyal.datatools.manager.models.FeedVersion; import java.util.List; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,19 +17,19 @@ */ public class FeedUpdater { public List eTags; - private Timer timer; + private static Timer timer; private static AmazonS3Client s3; public static final Logger LOG = LoggerFactory.getLogger(FeedUpdater.class); public FeedUpdater(List eTagList, int delay, int seconds){ this.eTags = eTagList; - this.timer = new Timer(); +// this.timer = new Timer(); // TODO: check for credentials?? - this.s3 = new AmazonS3Client(); - +// this.s3 = new AmazonS3Client(); + DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, delay, seconds, TimeUnit.SECONDS); this.timer.schedule(new UpdateFeedsTask(), delay*1000, seconds*1000); @@ -53,29 +52,9 @@ class UpdateFeedsTask extends TimerTask { public void run() { LOG.info("Fetching feeds..."); LOG.info("Current eTag list " + eTags.toString()); - - ObjectListing gtfsList = s3.listObjects(GtfsApiController.feedBucket, GtfsApiController.cacheDirectory); - Boolean feedsUpdated = false; - for (S3ObjectSummary objSummary : gtfsList.getObjectSummaries()) { - - String eTag = objSummary.getETag(); - if (!eTags.contains(eTag)) { - String keyName = objSummary.getKey(); - if (keyName.equals(GtfsApiController.cacheDirectory)){ - continue; - } - LOG.info("Updating feed " + keyName); - String feedId = keyName.split("/")[1]; -// ApiMain.loadFeedFromBucket(GtfsApiController.feedBucket, feedId, GtfsApiController.directory); - try { - GtfsApiController.gtfsApi.registerFeedSource(feedId, FeedVersion.get(feedId).getGtfsFile()); - } catch (Exception e) { - e.printStackTrace(); - } - addFeedETag(eTag); - feedsUpdated = true; - } - } + List updatedTags = GtfsApiController.registerS3Feeds(eTags, GtfsApiController.feedBucket, GtfsApiController.bucketFolder); + Boolean feedsUpdated = updatedTags.isEmpty() ? false : true; + addFeedETags(updatedTags); if (!feedsUpdated) { LOG.info("No feeds updated..."); } From 698fcd06546901b53d923a36743efba83f4f1836 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:40:23 -0400 Subject: [PATCH 062/323] updates for MTC upgrade --- src/main/client/alerts/actions/alerts.js | 76 +--- src/main/client/alerts/actions/projects.js | 2 - .../client/alerts/actions/visibilityFilter.js | 14 + .../alerts/components/AffectedEntity.js | 399 +++++++++--------- .../client/alerts/components/AlertEditor.js | 153 ++++--- .../client/alerts/components/AlertsList.js | 150 ++++--- .../client/alerts/components/AlertsViewer.js | 14 +- .../client/alerts/components/CreateAlert.js | 1 + .../alerts/containers/ActiveAlertEditor.js | 5 - .../alerts/containers/MainAlertsViewer.js | 2 - .../alerts/containers/VisibleAlertsList.js | 42 +- .../client/alerts/reducers/activeAlert.js | 1 - src/main/client/alerts/reducers/alerts.js | 51 ++- src/main/client/alerts/util/index.js | 1 + src/main/client/gtfs/actions/filter.js | 8 +- src/main/client/gtfs/actions/general.js | 111 +++++ src/main/client/gtfs/actions/patterns.js | 12 +- src/main/client/gtfs/actions/routes.js | 17 +- src/main/client/gtfs/actions/stops.js | 12 +- src/main/client/gtfs/components/GtfsFilter.js | 3 +- src/main/client/gtfs/components/GtfsMap.js | 374 ++++++++-------- .../client/gtfs/components/gtfsmapsearch.js | 94 ++--- src/main/client/gtfs/components/gtfssearch.js | 42 +- .../client/gtfs/containers/ActiveGtfsMap.js | 15 +- .../gtfs/containers/GlobalGtfsFilter.js | 2 +- src/main/client/gtfs/reducers/filter.js | 7 + src/main/client/gtfs/reducers/patterns.js | 22 + src/main/client/gtfs/reducers/stops.js | 11 + src/main/client/gtfs/util/graphql.js | 62 +++ src/main/client/signs/actions/projects.js | 2 - src/main/client/signs/actions/signs.js | 47 +-- .../client/signs/components/AffectedEntity.js | 33 +- .../signs/components/DisplaySelector.js | 1 - .../client/signs/components/SignEditor.js | 16 +- .../client/signs/components/SignPreview.js | 11 +- src/main/client/signs/components/SignsList.js | 28 +- .../client/signs/components/SignsViewer.js | 10 +- .../signs/containers/ActiveSignEditor.js | 6 - .../signs/containers/MainSignsViewer.js | 2 - .../signs/containers/VisibleSignsList.js | 13 +- src/main/client/signs/reducers/activeSign.js | 11 - src/main/client/signs/reducers/signs.js | 57 +-- src/main/client/signs/util/index.js | 1 + 43 files changed, 1118 insertions(+), 823 deletions(-) create mode 100644 src/main/client/alerts/util/index.js create mode 100644 src/main/client/gtfs/actions/general.js create mode 100644 src/main/client/signs/util/index.js diff --git a/src/main/client/alerts/actions/alerts.js b/src/main/client/alerts/actions/alerts.js index 5d5bd6bd1..5f7600a6b 100644 --- a/src/main/client/alerts/actions/alerts.js +++ b/src/main/client/alerts/actions/alerts.js @@ -1,6 +1,8 @@ import { push } from 'react-router-redux' import { browserHistory } from 'react-router' +import clone from 'clone' +import { fetchStopsAndRoutes } from '../../gtfs/actions/general' import { secureFetch } from '../../common/util/util' import { getAlertsUrl, getFeedId } from '../../common/util/modules' import { setErrorMessage } from '../../manager/actions/status' @@ -112,15 +114,6 @@ export const requestRtdAlerts = () => { } } -export const receivedGtfsEntities = (gtfsObjects, gtfsAlerts) => { - return { - type: 'RECEIVED_ALERT_GTFS_ENTITIES', - gtfsObjects, - gtfsAlerts - } -} - - export const receivedRtdAlerts = (rtdAlerts, activeProject) => { return { type: 'RECEIVED_RTD_ALERTS', @@ -148,70 +141,10 @@ export function fetchRtdAlerts () { }).then((alerts) => { return dispatch(receivedRtdAlerts(alerts, getState().projects.active)) }).then(() => { - let feed = getState().projects.active - const fetchFunctions = getState().alerts.entities.map((entity) => { - return fetchEntity(entity, feed) - }) - return Promise.all(fetchFunctions) - .then((results) => { - let newEntities = getState().alerts.entities - for (var i = 0; i < newEntities.length; i++) { - newEntities[i].gtfs = results[i] - } - dispatch(receivedGtfsEntities(newEntities, getState().alerts.all)) - }).then((error) => { - console.log('error', error) - }) - + return dispatch(fetchStopsAndRoutes(getState().alerts.entities, 'ALERTS')) }) } } -// TODO: implement method for single alert fetch -// export const requestRtdAlert = () => { -// return { -// type: 'REQUEST_RTD_ALERT', -// } -// } -// -// export const receivedRtdAlert = (rtdAlerts, activeProject) => { -// return { -// type: 'RECEIVED_RTD_ALERT', -// rtdAlerts, -// activeProject -// } -// } -// -// export function fetchRtdAlert(alertId) { -// return function (dispatch, getState) { -// dispatch(requestRtdAlert()) -// return fetch(getAlertsUrl() + '/' + alertId).then((res) => { -// return res.json() -// }).then((alert) => { -// const project = getState().projects.active -// return dispatch(receivedRtdAlerts([alert], project)) -// }).then(() => { -// let feed = getState().projects.active -// const fetchFunctions = getState().alerts.entities.map((entity) => { -// return fetchEntity(entity, feed) -// }) -// return Promise.all(fetchFunctions) -// .then((results) => { -// let newEntities = getState().alerts.entities -// for (var i = 0; i < newEntities.length; i++) { -// newEntities[i].gtfs = results[i] -// } -// const alerts = getState().alerts.all -// const alert = alerts.find(a => a.id === +alertId) -// dispatch(receivedGtfsEntities(newEntities, alerts)) -// console.log('this alert', alert) -// dispatch(updateActiveAlert(alert)) -// }).then((error) => { -// console.log('error', error) -// }) -// -// }) -// } -// } export const updateActiveAlert = (alert) => { return { @@ -228,7 +161,6 @@ export function editAlert(alert) { } export function fetchEntity(entity, activeProject) { - console.log() const feed = activeProject.feedSources.find(f => getFeedId(f) === entity.entity.AgencyId) const feedId = getFeedId(feed) const url = entity.type === 'stop' ? `/api/manager/stops/${entity.entity.StopId}?feed=${feedId}` : `/api/manager/routes/${entity.entity.RouteId}?feed=${feedId}` @@ -258,7 +190,6 @@ export function saveAlert(alert) { StartDateTime: alert.start/1000 || 0, EndDateTime: alert.end/1000 || 0, ServiceAlertEntities: alert.affectedEntities.map((entity) => { - console.log('ent', entity) return { Id: entity.id, AlertId: alert.id, @@ -273,7 +204,6 @@ export function saveAlert(alert) { } }) } - console.log('saving', alert.id, json) const url = getAlertsUrl() + (alert.id < 0 ? '' : '/' + alert.id) const method = alert.id < 0 ? 'post' : 'put' diff --git a/src/main/client/alerts/actions/projects.js b/src/main/client/alerts/actions/projects.js index 7fdcda32b..65ddd6a46 100644 --- a/src/main/client/alerts/actions/projects.js +++ b/src/main/client/alerts/actions/projects.js @@ -26,7 +26,6 @@ export function fetchProjects () { return secureFetch('/api/manager/secure/project', getState()) .then(response => response.json()) .then((projects) => { - console.log('received projects', projects) dispatch(receiveProjects(projects)) if (getState().projects.active) { project = getState().projects.active @@ -38,7 +37,6 @@ export function fetchProjects () { return dispatch(fetchProjectFeeds(project.id)) }) .then(() => { - console.log('updating filter') dispatch(updateGtfsFilter(getState().projects.active, getState().user)) return dispatch(fetchRtdAlerts()) }) diff --git a/src/main/client/alerts/actions/visibilityFilter.js b/src/main/client/alerts/actions/visibilityFilter.js index efd8abb29..97897c0ed 100644 --- a/src/main/client/alerts/actions/visibilityFilter.js +++ b/src/main/client/alerts/actions/visibilityFilter.js @@ -13,3 +13,17 @@ export const setVisibilityFilter = (filter) => { filter } } + +export const setAlertAgencyFilter = (feedId) => { + return { + type: 'SET_ALERT_AGENCY_FILTER', + feedId + } +} + +export const setAlertSort = (sort) => { + return { + type: 'SET_ALERT_SORT', + sort + } +} diff --git a/src/main/client/alerts/components/AffectedEntity.js b/src/main/client/alerts/components/AffectedEntity.js index ac92a7700..30d7b4c05 100644 --- a/src/main/client/alerts/components/AffectedEntity.js +++ b/src/main/client/alerts/components/AffectedEntity.js @@ -1,202 +1,209 @@ import React from 'react' -import { Panel, Row, Col, ButtonGroup, Button, Glyphicon, FormControl, Label } from 'react-bootstrap' - +import { ListGroupItem, Row, Col, ButtonGroup, Button, Glyphicon, FormControl, Label, Collapse } from 'react-bootstrap' +import Icon from 'react-fa' import GtfsSearch from '../../gtfs/components/gtfssearch' import modes from '../modes' import { getFeed, getFeedId } from '../../common/util/modules' +import { getRouteNameAlerts } from '../../editor/util/gtfs' export default class AffectedEntity extends React.Component { constructor (props) { super(props) - } - render () { - const getMode = (id) => { - return modes.find((mode) => mode.gtfsType === +id) + this.state = { + active: false } - const getRouteName = (route) => { - let routeName = route.route_short_name && route.route_long_name ? `${route.route_short_name} - ${route.route_long_name}` - : route.route_long_name ? route.route_long_name - : route.route_short_name ? route.route_short_name - : null - return routeName + } + getEntitySummary (entity) { + const type = entity.type + const val = entity[type.toLowerCase()] + let agencyName = '' + if (typeof entity.agency !== 'undefined' && entity.agency !== null) { + agencyName = entity.agency.name } - const getEntitySummary = (entity) => { - console.log(entity) - const type = entity.type - const val = entity[type.toLowerCase()] - console.log('val', val) - let agencyName = '' - if (typeof entity.agency !== 'undefined' && entity.agency !== null) { - agencyName = entity.agency.name - } - else if (typeof entity.stop !== 'undefined' && entity.stop !== null) { - const feed = getFeed(this.props.feeds, entity.stop.feed_id) - agencyName = typeof feed !== 'undefined' ? feed.name : 'Unknown agency' - } - - const routeName = entity.route ? getRouteName(entity.route) : entity.route_id - let stopName = entity.stop - ? {entity.stop.stop_name} ({entity.stop.stop_id}) - : entity.stop_id - let summary = '' - switch (type) { - case 'AGENCY' : - return agencyName - case 'STOP' : - summary = stopName - if (routeName) { - summary += ` for ${routeName}` - } - return summary - case 'ROUTE' : - summary = routeName - if (stopName) { - summary += ` at ${stopName}` - } - return summary - case 'MODE' : - summary = val.name - if (stopName) { - summary += ` at ${stopName}` - } - return summary - } + else if (typeof entity.stop !== 'undefined' && entity.stop !== null) { + const feed = getFeed(this.props.feeds, entity.stop.feed_id) + agencyName = typeof feed !== 'undefined' ? feed.name : 'Unknown agency' } - return ( - - + const routeName = entity.route ? getRouteNameAlerts(entity.route) : entity.route_id + let stopName = entity.stop + ? `${entity.stop.stop_name} (${entity.stop.stop_id}) ${agencyName}` + : entity.stop_id + let summary = '' + switch (type) { + case 'AGENCY' : + return ( - { - this.props.entity.type === 'STOP' ? - : this.props.entity.type === 'ROUTE' ? - : this.props.entity.type + ':' - } -  {getEntitySummary(this.props.entity)} + {agencyName}
+ Note: this selection will apply to all stops and routes for {agencyName}.
- - - - - - - - }> - - {(() => { - var indent = { - paddingLeft: '30px' + ) + case 'STOP' : + summary = stopName + if (routeName) { + summary += ` for ${routeName}` } - let selectedFeeds = [this.props.entity.agency] || this.props.activeFeeds - console.log(selectedFeeds) - let selectedRoute = this.props.entity.route - let selectedStop = this.props.entity.stop - // console.log('filterByStop', selectedStop) - // console.log('filterByRoute', selectedRoute) - switch (this.props.entity.type) { - case 'AGENCY': - return ( -
- Agency: - -
- ) - case 'MODE': - return ( -
- Mode: - -
- Refine by Agency: - - Refine by Stop: - -
-
- ) - case 'STOP': - return ( -
- Stop: - -
- Refine by Route: - -
-
- ) - case 'ROUTE': - return ( -
- Route: - -
- Refine by Stop: - -
-
- ) - + return {summary} + case 'ROUTE' : + summary = routeName + if (stopName) { + summary += ` at ${stopName}` } - })()} - -
+ return {summary} + case 'MODE' : + summary = val.name + if (stopName) { + summary += ` at ${stopName}` + } + return ( + + {type}: {summary}
+ Note: this selection will apply to all {val.name.toLowerCase()} routes{stopName && ` stopping at ${stopName}`}. +
+ ) + } + } + renderHeader () { + return ( + this.setState({active: !this.state.active})} + style={{cursor: 'pointer'}} + > + +
+ + {this.getEntitySummary(this.props.entity)} +
+ + + + +
+ ) + } + renderEntity () { + var indent = { + paddingLeft: '30px' + } + let selectedFeeds = [this.props.entity.agency] || this.props.activeFeeds + let selectedRoute = this.props.entity.route + let selectedStop = this.props.entity.stop + switch (this.props.entity.type) { + case 'AGENCY': + return ( +
+ Agency: + +
+ ) + case 'MODE': + return ( +
+ Mode: + +
+ Refine by Agency: + + Refine by Stop: + +
+
+ ) + case 'STOP': + return ( +
+ Stop: + +
+ Refine by Route: + +
+
+ ) + case 'ROUTE': + return ( +
+ Route: + +
+ Refine by Stop: + +
+
+ ) + } + } + render () { + const getMode = (id) => { + return modes.find((mode) => mode.gtfsType === +id) + } + return ( + + + {this.renderEntity()} + + ) } } - class AgencySelector extends React.Component { - render () { const getMode = (id) => { return modes.find((mode) => mode.gtfsType === +id ) @@ -221,7 +228,6 @@ class AgencySelector extends React.Component { } class ModeSelector extends React.Component { - render () { const getMode = (id) => { return modes.find((mode) => mode.gtfsType === +id ) @@ -248,25 +254,14 @@ class ModeSelector extends React.Component { class RouteSelector extends React.Component { constructor (props) { super(props) - this.state = { - route: this.props.route - } } render () { - console.log('render route ent', this.props.route) const getMode = (id) => { return modes.find((mode) => mode.gtfsType === +id ) } - const getRouteName = (route) => { - let routeName = route.route_short_name && route.route_long_name ? `${route.route_short_name} - ${route.route_long_name}` : - route.route_long_name ? route.route_long_name : - route.route_short_name ? route.route_short_name : null - return routeName - } var routes = [] const feed = this.props.route ? getFeed(this.props.feeds, this.props.route.feed_id) : null const agencyName = feed ? feed.name : 'Unknown agency' - return (
{ if (typeof evt !== 'undefined' && evt !== null) this.props.entityUpdated(this.props.entity, 'ROUTE', evt.route, evt.agency) - else if (evt == null) - this.props.entityUpdated(this.props.entity, 'ROUTE', null, null) + else if (evt == null) { + if (this.props.filterByStop) { + this.props.entityUpdated(this.props.entity, 'ROUTE', null, feed) + } + else { + this.props.entityUpdated(this.props.entity, 'ROUTE', null, null) + } + } }} - value={this.props.route ? {'value': this.props.route.route_id, 'label': `${getRouteName(this.props.route)} (${agencyName})`} : ''} + value={ + this.props.route + ? { + route: this.props.route, + 'value': this.props.route.route_id, + 'label': `${getRouteNameAlerts(this.props.route)} (${agencyName})` + } + : '' + } />
) @@ -292,22 +301,14 @@ class RouteSelector extends React.Component { class StopSelector extends React.Component { constructor (props) { super(props) - // this.state = { - // stop: this.props.stop - // } } - // TODO: clear stop or route if parent select changes... - componentWillReceiveProps (nextProps) { - } - render () { - console.log('render stop ent', this.props.stop) const getMode = (id) => { return modes.find((mode) => mode.gtfsType === +id ) } var stops = [] const feed = this.props.stop ? getFeed(this.props.feeds, this.props.stop.feed_id) : null - const agencyName = feed ? feed.name : 'Unkown agency' + const agencyName = feed ? feed.name : 'Unknown agency' return (
e.type === 'AGENCY').length, + }, + { + singular: 'route', + plural: 'routes', + count: entities.filter(e => e.type === 'ROUTE').length, + }, + { + singular: 'stop', + plural: 'stops', + count: entities.filter(e => e.type === 'STOP').length, + }, + { + singular: 'mode', + plural: 'modes', + count: entities.filter(e => e.type === 'MODE').length, + }, + ] + return ( + + Affected Service{counts.map(c => { + return c.count + ? + : null + })} + + ) + } render () { - console.log('AlertEditor') - console.log(this.props.alert) if (!this.props.alert) { - return + return ( + + + + ) } var compare = function (a, b) { var aName = a.shortName || a.name @@ -207,7 +246,7 @@ export default class AlertEditor extends React.Component { value={this.props.alert.cause} > {causes.map((cause) => { - return + return })} @@ -221,7 +260,7 @@ export default class AlertEditor extends React.Component { value={this.props.alert.effect} > {effects.map((effect) => { - return + return })} @@ -254,55 +293,60 @@ export default class AlertEditor extends React.Component { - Affected Service}> - - - - - - - - - { - console.log('we need to add this entity to the store', evt) - if (typeof evt !== 'undefined' && evt !== null){ - if (evt.stop){ - this.props.onAddEntityClick('STOP', evt.stop, evt.agency, newEntityId) - } - else if (evt.route) - this.props.onAddEntityClick('ROUTE', evt.route, evt.agency, newEntityId) + {/* Affected Service panel */} + + + + + + + + + + { + console.log('we need to add this entity to the store', evt) + if (typeof evt !== 'undefined' && evt !== null){ + if (evt.stop){ + this.props.onAddEntityClick('STOP', evt.stop, evt.agency, newEntityId) } - }} - /> - - + else if (evt.route) + this.props.onAddEntityClick('ROUTE', evt.route, evt.agency, newEntityId) + } + }} + /> + + + {this.props.alert.affectedEntities .sort((a, b) => b.id - a.id) // reverse sort by entity id .map((entity) => { - return + return ( + + ) })} - - + @@ -324,7 +368,6 @@ export default class AlertEditor extends React.Component { newEntityId={newEntityId} /> - diff --git a/src/main/client/alerts/components/AlertsList.js b/src/main/client/alerts/components/AlertsList.js index 82b00bdb7..6cfeb4485 100644 --- a/src/main/client/alerts/components/AlertsList.js +++ b/src/main/client/alerts/components/AlertsList.js @@ -1,8 +1,11 @@ import React, { PropTypes, Component } from 'react' -import { Row, ButtonGroup, Button, FormControl, FormGroup } from 'react-bootstrap' +import { Row, Col, ButtonGroup, Button, FormControl, FormGroup, Badge, ControlLabel } from 'react-bootstrap' import Icon from 'react-fa' +import { sentence as toSentenceCase } from 'change-case' import AlertPreview from './AlertPreview' +import { FILTERS } from '../util' +import { getFeedId } from '../../common/util/modules' export default class AlertsList extends Component { static propTypes = { @@ -11,6 +14,7 @@ export default class AlertsList extends Component { isFetching: PropTypes.bool, editableFeeds: PropTypes.array, publishableFeeds: PropTypes.array, + filterCounts: PropTypes.object, onEditClick: PropTypes.func, onZoomClick: PropTypes.func, @@ -23,68 +27,106 @@ export default class AlertsList extends Component { super(props) } render () { - let sortedAlerts = this.props.alerts.sort((a, b) => { - if (a.id < b.id) return -1 - if (a.id > b.id) return 1 + // console.log(this.props) + var compare = function (a, b) { + var aName = a.shortName || a.name + var bName = b.shortName || b.name + if(aName < bName) return -1 + if(aName > bName) return 1 return 0 - }) + } + let sortedFeeds = this.props.editableFeeds.sort(compare) return (
- - this.props.searchTextChanged(evt.target.value)} - defaultValue={this.props.visibilityFilter.searchText} - /> - + + + this.props.searchTextChanged(evt.target.value)} + defaultValue={this.props.visibilityFilter.searchText} + /> + + + {/* Alert filters */} - - - - - - - - -
 
+ + + + {FILTERS.map(f => ( + + ))} + + + + Sort by + {' '} + { + let values = evt.target.value.split(':') + let sort = { + type: values[0], + direction: values[1] + } + this.props.sortChanged(sort) + }} + > + + + + + + + + + + + + Agency + {' '} + this.props.agencyFilterChanged(evt.target.value)} + > + + {sortedFeeds.map(fs => ( + + ))} + + +
+ {/* List of alerts */} - - {this.props.isFetching - ?

- : sortedAlerts.length - ? sortedAlerts.map((alert) => { - return - }) - :

No alerts found.

- } + + {this.props.isFetching + ?

+ : this.props.alerts.length + ? this.props.alerts.map((alert) => { + return + }) + :

No alerts found.

+ } +
) diff --git a/src/main/client/alerts/components/AlertsViewer.js b/src/main/client/alerts/components/AlertsViewer.js index 770d8aa48..c3cf69aaa 100644 --- a/src/main/client/alerts/components/AlertsViewer.js +++ b/src/main/client/alerts/components/AlertsViewer.js @@ -1,6 +1,6 @@ import React from 'react' import Helmet from 'react-helmet' - +import Icon from 'react-fa' import { Grid, Row, Col, Button } from 'react-bootstrap' import ManagerPage from '../../common/components/ManagerPage' @@ -32,10 +32,14 @@ export default class AlertsViewer extends React.Component { - +

+ Service Alerts + +

diff --git a/src/main/client/alerts/components/CreateAlert.js b/src/main/client/alerts/components/CreateAlert.js index e4b39d68e..8a15f626a 100644 --- a/src/main/client/alerts/components/CreateAlert.js +++ b/src/main/client/alerts/components/CreateAlert.js @@ -11,6 +11,7 @@ export default class CreateAlert extends React.Component { + : null + } +
+ + + ) + } + renderPattern (pattern, index = 0) { + if (!pattern) { + return null + } + const route = pattern.route + const feedId = route.feed_id || route.feed.feed_id + const feed = getFeed(this.props.feeds, feedId) + const popup = ( + +
+

{getRouteName(route)}

+
    +
  • ID: {route.route_id}
  • +
  • Agency:{' '} + {// TODO: change this back to feedName + // route.feed_id + feed && feed.name + } +
  • +
+ {this.props.onRouteClick + ? + :

[Must add stops first]

+ } +
+
+ ) + return ( + { + layer.feature.properties.patternId = pattern.pattern_id + layer._leaflet_id = pattern.pattern_id + }} + > + {popup} + + ) + } renderIsochrones () { let comps = [] const bandTime = this.props.isochroneBand || 60 * 60 - if (this.props.version && this.props.version.isochrones) { + if (this.props.version && this.props.version.isochrones && this.props.version.isochrones.features) { comps = this.props.version.isochrones.features.map((iso, index) => { if (iso.properties.time !== bandTime) return null return ( @@ -139,7 +265,6 @@ export default class GtfsMap extends Component { ) }) } - if(this.state && this.state.lastClicked) { comps.push( ) } - return comps } + layerAddHandler (e) { + // handle pattern panTo and popup open + const patternId = e.layer.feature && e.layer.feature.properties.patternId + if (patternId && patternId === this.props.searchFocus) { + this.refs.map && this.refs.map.leafletElement.fitBounds(e.layer.getBounds()) + e.layer.openPopup && e.layer.openPopup() + this.setState({searchFocus: null}) + } + // open popup for stop or pattern if searchFocus is set + if (this.props.stop && this.state.searchFocus === this.props.stop.stop_id) { + e.layer.openPopup && e.layer.openPopup() + this.setState({searchFocus: null}) + } + } render () { - console.log(this.props, this.state) var mapStyle = { width: this.props.width, // % or px height: `${this.props.height}px` // only px } let bounds = this.getBounds() - const layerAddHandler = (e) => { - // handle pattern: opening popup and fitting bounds - if (e.layer.feature && e.layer.feature.properties.patternId) { - this.refs.map.leafletElement.fitBounds(e.layer.getBounds()) - if (!this.props.disablePopup) { - e.layer.openPopup() - } - } - } return (
this.mapClicked(e)} - // onZoomEnd={(e) => { - // this.props.onZoomChange && this.props.onZoomChange(e) - // this.refs.map && !this.props.disableRefresh && this.refreshGtfsElements() - // }} - // onMoveEnd={() => this.refs.map && !this.props.disableRefresh && this.refreshGtfsElements()} - onLayerAdd={layerAddHandler} + onMoveEnd={(e) => this.mapMoved(e)} + onLayerAdd={(e) => this.layerAddHandler(e)} className='Gtfs-Map' > + {/* feed bounds */} {this.props.showBounds ? : null } - - {this.props.stops ? this.props.stops.map((stop, index) => { - if (stop) { - const busIcon = divIcon({ - html: ` - - - `, - className: '', - iconSize: [24, 24], - }) - return ( - { - // e.target.openPopup() - // }} - position={[stop.stop_lat, stop.stop_lon]} - key={`marker-${stop.stop_id}`} - > - -
-

{stop.stop_name} ({stop.stop_id})

- {stop.transferPerformance && stop.transferPerformance.length - ?
- Transfer performance - { - let state = {} - state[stop.stop_id] = +evt.target.value - this.setState(state)} - } - > - {stop.transferPerformance - // .sort((a, b) => { - // - // }) - .map((summary, index) => { - const fromRoute = this.props.routes.find(r => r.route_id === summary.fromRoute) - const toRoute = this.props.routes.find(r => r.route_id === summary.toRoute) - return - }) - } - - {this.renderTransferPerformance(stop.transferPerformance[this.state[stop.stop_id] || 0])} -
- :

No transfers found

- } - {this.props.onStopClick - ? - : null - } -
-
-
- ) + + {/* Stops from map bounds search */} + {this.props.stops + ? this.props.stops.map((stop, index) => this.renderStop(stop, index)) + : null } - }) - : null - } + {/* Stop from GtfsSearch */} + {this.renderStop(this.props.stop)} + + + {/* Patterns from map bounds search */} + {this.props.patterns + ? this.props.patterns.map((pattern, index) => this.renderPattern(pattern, index)) + : null + } + {/* Pattern from GtfsSearch */} + {this.renderPattern(this.props.pattern)} - - {this.props.patterns ? this.props.patterns.map((pattern, index) => { - if (pattern) { -// console.log(pattern) - const route = this.props.routes.find(r => r.route_id === pattern.route_id) - const routeName = route.route_short_name !== null ? route.route_short_name : route.route_long_name - const popup = ( - -
-

- - { - pattern.name // routeName - } - -

-
    -
  • ID: {route.route_id}
  • -
  • Agency:{' '} - {// TODO: change this back to feedName - route.feed_id - // getFeed(this.props.feeds, route.feed_id).name - } -
  • -
- {this.props.onRouteClick - ? - :

[Must add stops first]

- } -
-
- ) - return ( - { - layer.feature.properties.route = route - layer.feature.properties.patternId = pattern.pattern_id - layer._leaflet_id = pattern.pattern_id - // layer.feature.geometry.coordinates.push(pattern.geometry.coordinates) - }} - properties={route} - > - {popup} - - ) - } - }) - : null - } + + {/* Isochrones from map click */} + {this.props.showIsochrones && this.renderIsochrones()} - {this.props.showIsochrones && this.renderIsochrones()}
) } refreshGtfsElements (feeds, entities) { + const zoomLevel = this.refs.map.leafletElement.getZoom() const feedIds = (feeds || this.props.feeds).map(getFeedId) const ents = (entities || this.props.entities || ['routes', 'stops']) - const zoomLevel = this.refs.map.leafletElement.getZoom() if (feedIds.length === 0 || zoomLevel <= 13) { - this.setState({ stops: [], patterns: [], routes: [] }) - return - } - console.log('refresh GTFS', feedIds) - const bounds = this.refs['map'].leafletElement.getBounds() - const maxLat = bounds.getNorth() - const maxLng = bounds.getEast() - const minLat = bounds.getSouth() - const minLng = bounds.getWest() - - const getStops = fetch(`/api/manager/stops?max_lat=${maxLat}&max_lon=${maxLng}&min_lat=${minLat}&min_lon=${minLng}&feed=${feedIds.toString()}`) - .then((response) => { - return response.json() - }) - - const getRoutes = fetch(`/api/manager/routes?max_lat=${maxLat}&max_lon=${maxLng}&min_lat=${minLat}&min_lon=${minLng}&feed=${feedIds.toString()}`) - .then((response) => { - return response.json() - }) - - let entitySearches = [] - if (ents.indexOf('stops') > -1) { - entitySearches.push(getStops) + // this.props.clearGtfsElements() } else { - entitySearches.push(null) + this.props.refreshGtfsElements(feedIds, ents) } - if (ents.indexOf('routes') > -1) { - entitySearches.push(getRoutes) - } else { - entitySearches.push(null) - } - Promise.all(entitySearches).then((results) => { - const stops = results[0] ? results[0] : [] - const patterns = results[1] ? results[1] : [] - const routes = patterns.map(p => p.associatedRoutes[0]) - this.setState({ stops, patterns, routes }) - }) } } diff --git a/src/main/client/gtfs/components/gtfsmapsearch.js b/src/main/client/gtfs/components/gtfsmapsearch.js index 8f957ba83..81a897658 100644 --- a/src/main/client/gtfs/components/gtfsmapsearch.js +++ b/src/main/client/gtfs/components/gtfsmapsearch.js @@ -3,7 +3,8 @@ import fetch from 'isomorphic-fetch' import { Button } from 'react-bootstrap' import { PureComponent, shallowEqual } from 'react-pure-render' -import GtfsMap from './gtfsmap' +import ActiveGtfsMap from '../containers/ActiveGtfsMap' +import GtfsMap from './GtfsMap' import GtfsSearch from './gtfssearch' export default class GtfsMapSearch extends Component { @@ -11,64 +12,60 @@ export default class GtfsMapSearch extends Component { constructor(props) { super(props) this.state = { - stops: [], - routes: [], - patterns: [], - position: null, + stop: null, + pattern: null, message: '', searching: ['stops', 'routes'], map: {} } } - - componentDidMount() { - // this.fetchUsers() - console.log(this.props) + getPatterns (input) { + return fetch(`/api/manager/patterns?route=${input.route.route_id}&feed=${input.route.feed_id}`) + .then((response) => { + return response.json() + }) + .then((json) => { + const pattern = json[0] + // hack to associate route to pattern + pattern.route = input.route + return pattern + }) + } + handleSelection (input) { + if (!input) { + this.setState({stop: null, pattern: null, searchFocus: null}) + } + else if (input && input.stop) { + const pattern = null + const stop = input.stop + this.setState({ stop, pattern, searchFocus: stop.stop_id }) + } + else if (input && input.route) { + // TODO: replace with GraphQL + return Promise.all([this.getPatterns(input)]).then((results) => { + const pattern = results[0] + const stop = null + this.setState({ pattern, stop, searchFocus: pattern.pattern_id }) + }) + } } - render() { let zoomMessage = 'Zoom in to view ' + this.state.searching.join(' and ') if (this.refs.map && this.refs.map.refs.map) { let mapZoom = this.refs.map.refs.map.leafletElement.getZoom() zoomMessage = mapZoom <= 13 ? zoomMessage : '' } - console.log(zoomMessage) const onZoomChange = (e) => { let mapZoom = e.target._zoom zoomMessage = mapZoom <= 13 ? zoomMessage : '' } const {attribution, centerCoordinates, geojson, markers, transitive, url, zoom} = this.props - const getPatterns = (input) => { - return fetch(`/api/manager/patterns?route=${input.route.route_id}&feed=${input.route.feed_id}`) - .then((response) => { - return response.json() - }) - .then((json) => { - - const pattern = json[0] - console.log(pattern) - // hack to associate route to pattern - pattern.associatedRoutes = [] - pattern.associatedRoutes.push(input.route) - return pattern - }) + const searchProps = { + stop: this.state.stop, + pattern: this.state.pattern, + searchFocus: this.state.searchFocus, + entities: this.state.searching, } - const handleSelection = (input) => { - if (!input) { - this.setState({stops: null, routes: null, patterns: null, searchFocus: false}) - } - else if (input && input.stop) { - this.setState(Object.assign({}, this.state, { stops: [input.stop], position: [input.stop.stop_lat, input.stop.stop_lon], routes: null, patterns: null, searchFocus: true })) - } - else if (input && input.route) { - return Promise.all([getPatterns(input)]).then((results) => { - const patterns = results[0] - console.log('patterns for route ' + input.route.route_id, patterns) - this.setState(Object.assign({}, this.state, { routes: [input.route], patterns: [patterns], stops: null, searchFocus: true })) - }) - } - } - return (
this.handleSelection(input)} entities={this.state.searching} />
    @@ -88,7 +85,8 @@ export default class GtfsMapSearch extends Component { this.state.searching.indexOf('routes') > -1 && this.state.searching.indexOf('stops') > -1 ? this.setState({searching: ['routes']}) : this.state.searching.indexOf('stops') === -1 - ? this.setState({searching: ['stops']}) : this.setState({searching: ['stops', 'routes']}) + ? this.setState({searching: ['stops']}) + : this.setState({searching: ['stops', 'routes']}) }} > Searching {this.state.searching.join(' and ')} @@ -96,21 +94,17 @@ export default class GtfsMapSearch extends Component {
  • {zoomMessage}
-
) diff --git a/src/main/client/gtfs/components/gtfssearch.js b/src/main/client/gtfs/components/gtfssearch.js index 89d24de94..f89f4f60f 100644 --- a/src/main/client/gtfs/components/gtfssearch.js +++ b/src/main/client/gtfs/components/gtfssearch.js @@ -32,11 +32,10 @@ export default class GtfsSearch extends React.Component { return {option.stop ? : } {option.label} {option.link} } onChange (value) { - // if (typeof value !== 'undefined') - this.setState({value}) + this.props.onChange && this.props.onChange(value) + this.setState({value}) } render() { - console.log('render search feeds', this.props.feeds) const getRouteName = (route) => { let routeName = route.route_short_name && route.route_long_name ? `${route.route_short_name} - ${route.route_long_name}` : route.route_long_name ? route.route_long_name : @@ -76,7 +75,6 @@ export default class GtfsSearch extends React.Component { } const getRoutes = (input) => { const feedIds = this.props.feeds.map(getFeedId) - console.log(feedIds) if (!feedIds.length) return [] @@ -123,13 +121,6 @@ export default class GtfsSearch extends React.Component { return options }) } - const handleChange = (input) => { - console.log(input) - this.onChange(input) - this.props.onChange(input) - console.log(this.state) - } - const onFocus = (input) => { // clear options to onFocus to ensure only valid route/stop combinations are selected this.refs.gtfsSelect.loadOptions('') @@ -137,20 +128,21 @@ export default class GtfsSearch extends React.Component { const placeholder = 'Begin typing to search for ' + this.props.entities.join(' or ') + '...' return ( - + this.onChange(value)} + /> ) } } diff --git a/src/main/client/gtfs/containers/ActiveGtfsMap.js b/src/main/client/gtfs/containers/ActiveGtfsMap.js index 2987e209f..31b12f34a 100644 --- a/src/main/client/gtfs/containers/ActiveGtfsMap.js +++ b/src/main/client/gtfs/containers/ActiveGtfsMap.js @@ -1,10 +1,12 @@ import React from 'react' import { connect } from 'react-redux' -import GtfsMap from '../components/gtfsmap' +import GtfsMap from '../components/GtfsMap' import { fetchPatterns } from '../actions/patterns' +import { clearGtfsElements, refreshGtfsElements } from '../actions/general' import { fetchStops, stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../actions/stops' import { fetchRoutes } from '../actions/routes' +import { updateMapState } from '../actions/filter' import { fetchFeedVersionIsochrones } from '../../manager/actions/feeds' @@ -19,7 +21,7 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedId = ownProps.version.id.replace('.zip', '') + const feedId = ownProps.version && ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { // if(!initialProps.routes.fetchStatus.fetched) { @@ -29,6 +31,15 @@ const mapDispatchToProps = (dispatch, ownProps) => { // dispatch(fetchPatterns(feedId, null)) // } }, + updateMapState: (props) => { + dispatch(updateMapState(props)) + }, + clearGtfsElements: () => { + dispatch(clearGtfsElements()) + }, + refreshGtfsElements: (feedIds, entities) => { + dispatch(refreshGtfsElements(feedIds, entities)) + }, stopRouteFilterChange: (newValue) => { dispatch(stopRouteFilterChange(feedId, newValue)) }, diff --git a/src/main/client/gtfs/containers/GlobalGtfsFilter.js b/src/main/client/gtfs/containers/GlobalGtfsFilter.js index 42ffd2cd1..22d893ba5 100644 --- a/src/main/client/gtfs/containers/GlobalGtfsFilter.js +++ b/src/main/client/gtfs/containers/GlobalGtfsFilter.js @@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { let filter = initialProps.permissionFilter || 'view-feed' - dispatch(updatePermissionFilter(filter)) + // dispatch(updatePermissionFilter(filter)) if (initialProps.project && initialProps.user) dispatch(updateGtfsFilter(initialProps.project, initialProps.user)) }, diff --git a/src/main/client/gtfs/reducers/filter.js b/src/main/client/gtfs/reducers/filter.js index 64ef18755..bb1a41809 100644 --- a/src/main/client/gtfs/reducers/filter.js +++ b/src/main/client/gtfs/reducers/filter.js @@ -5,6 +5,11 @@ const gtfsFilter = (state = { allFeeds: [], activeFeeds: [], loadedFeeds: [], + typeFilter: ['stops', 'routes'], + map: { + bounds: [], + zoom: null, + }, permissionFilter: 'view-feed', version: null, dateTimeFilter: { @@ -18,6 +23,8 @@ const gtfsFilter = (state = { switch (action.type) { case 'SET_ACTIVE_FEEDVERSION': return update(state, {version: {$set: action.feedVersion ? action.feedVersion.id : null}}) + case 'UPDATE_GTFS_MAP_STATE': + return update(state, {map: {$set: action.props}}) case 'UPDATE_GTFS_PERMISSION_FILTER': return update(state, {permissionFilter: {$set: action.permission}}) case 'UPDATE_GTFS_DATETIME_FILTER': diff --git a/src/main/client/gtfs/reducers/patterns.js b/src/main/client/gtfs/reducers/patterns.js index 5de317c84..bff84605a 100644 --- a/src/main/client/gtfs/reducers/patterns.js +++ b/src/main/client/gtfs/reducers/patterns.js @@ -29,6 +29,17 @@ export default function reducer (state = defaultState, action) { data: {$set: []} } ) + // case 'CLEAR_GRAPHQL_PATTERNS': + // return update(state, { + // fetchStatus: { + // $set: { + // fetched: false, + // fetching: false, + // error: false + // }, + // data: {$set: []} + // } + // }) case 'FETCH_GRAPHQL_PATTERNS_REJECTED': return update(state, { fetchStatus: { @@ -39,6 +50,17 @@ export default function reducer (state = defaultState, action) { } } }) + case 'RECEIVED_GTFS_ELEMENTS': + return update(state, { + fetchStatus: { + $set: { + fetched: true, + fetching: false, + error: false + } + }, + data: {$set: action.patterns} + }) case 'FETCH_GRAPHQL_PATTERNS_FULFILLED': let allRoutes = action.data ? action.data.routes : [] let allPatterns = [] diff --git a/src/main/client/gtfs/reducers/stops.js b/src/main/client/gtfs/reducers/stops.js index e217164ca..003cb18ff 100644 --- a/src/main/client/gtfs/reducers/stops.js +++ b/src/main/client/gtfs/reducers/stops.js @@ -41,6 +41,17 @@ export default function reducer (state = defaultState, action) { data: {$set: []} } }) + case 'RECEIVED_GTFS_ELEMENTS': + return update(state, { + fetchStatus: { + $set: { + fetched: true, + fetching: false, + error: false + } + }, + data: {$set: action.stops} + }) case 'FETCH_GRAPHQL_STOPS_FULFILLED': let allRoutes = action.data.routes || [] let allPatterns = [] diff --git a/src/main/client/gtfs/util/graphql.js b/src/main/client/gtfs/util/graphql.js index 262026e07..756d54161 100644 --- a/src/main/client/gtfs/util/graphql.js +++ b/src/main/client/gtfs/util/graphql.js @@ -1,3 +1,4 @@ +// variable names/keys must match those specified in GraphQL schema export function compose (query, variables) { return `/api/manager/graphql?query=${encodeURIComponent(query)}&variables=${encodeURIComponent(JSON.stringify(variables))}` } @@ -92,6 +93,67 @@ query allStopsQuery($feedId: [String]) { } ` +export const patternsAndStopsForBoundingBox = (feedId, entities, max_lat, max_lon, min_lat, min_lon) => ` + query patternsAndStopsGeo($feedId: [String], $max_lat: Float, $max_lon: Float, $min_lat: Float, $min_lon: Float){ + ${entities.indexOf('routes') !== -1 + ? `patterns(feed_id: $feedId, max_lat: $max_lat, max_lon: $max_lon, min_lat: $min_lat, min_lon: $min_lon){ + pattern_id, + geometry, + name, + route{ + route_id, + route_short_name, + route_long_name, + route_color, + feed{ + feed_id + }, + } + },` + : '' + } + ${entities.indexOf('stops') !== -1 + ? `stops(feed_id: $feedId, max_lat: $max_lat, max_lon: $max_lon, min_lat: $min_lat, min_lon: $min_lon){ + stop_id, + stop_code, + stop_name, + stop_desc, + stop_lat, + stop_lon, + feed{ + feed_id + } + }` + : '' + } + } +` + +// for use in entity fetching for signs / alerts +export const stopsAndRoutes = (feedId, routeId, stopId) => ` + query routeStopQuery($feedId: [String], ${routeId ? '$routeId: [String],' : ''}, ${stopId ? '$stopId: [String],' : ''}){ + feeds(feed_id: $feedId){ + feed_id, + ${stopId + ? `stops (stop_id: $stopId){ + stop_id, + stop_name, + stop_code + },` + : '' + } + ${routeId + ? `routes(route_id: $routeId){ + route_id, + route_short_name, + route_long_name, + },` + : '' + } + } + } +` + // TODO: add back in patternId filter export const stopsFiltered = (feedId, routeId, patternId, date, from, to) => { const hasFrom = typeof from !== 'undefined' && from !== null diff --git a/src/main/client/signs/actions/projects.js b/src/main/client/signs/actions/projects.js index f0545db4f..8cf9f99c7 100644 --- a/src/main/client/signs/actions/projects.js +++ b/src/main/client/signs/actions/projects.js @@ -25,13 +25,11 @@ export function fetchProjects () { return secureFetch('/api/manager/secure/project', getState()) .then(response => response.json()) .then((projects) => { - console.log('received projects', projects) dispatch(receiveProjects(projects)) project = getState().projects.active || projects.find(proj => proj.id === getConfigProperty('application.active_project')) return dispatch(fetchProjectFeeds(project.id)) }) .then(() => { - console.log('updating filter') dispatch(updateGtfsFilter(getState().projects.active, getState().user)) return dispatch(fetchRtdSigns()) }) diff --git a/src/main/client/signs/actions/signs.js b/src/main/client/signs/actions/signs.js index 5a9148f5c..6ed398d86 100644 --- a/src/main/client/signs/actions/signs.js +++ b/src/main/client/signs/actions/signs.js @@ -1,5 +1,6 @@ import { browserHistory } from 'react-router' import fetch from 'isomorphic-fetch' +import { fetchStopsAndRoutes } from '../../gtfs/actions/general' import { getSignConfigUrl, getDisplaysUrl, getFeedId } from '../../common/util/modules' // signs management action @@ -82,6 +83,13 @@ export const requestRtdSigns = () => { } } +export const requestGtfsEntities = (feedIds, routeids, stopIds) => { + return { + type: 'REQUEST_SIGN_GTFS_ENTITIES', + feedIds, routeids, stopIds + } +} + export const receivedGtfsEntities = (gtfsObjects, gtfsSigns) => { return { type: 'RECEIVED_SIGN_GTFS_ENTITIES', @@ -120,7 +128,7 @@ export function fetchRtdDisplays () { }).then((displays) => { // console.log(displays) dispatch(receivedRtdDisplays(displays, getState().projects.active)) - }) + }).catch(error => console.log(error)) } } @@ -133,21 +141,7 @@ export function fetchRtdSigns () { dispatch(receivedRtdSigns(signs, getState().projects.active)) dispatch(fetchRtdDisplays()) }).then(() => { - let feed = getState().projects.active - const fetchFunctions = getState().signs.entities.map((entity) => { - return fetchEntity(entity, feed) - }) - return Promise.all(fetchFunctions) - .then((results) => { - console.log('got entities', results) - let newEntities = getState().signs.entities - for (var i = 0; i < newEntities.length; i++) { - newEntities[i].gtfs = results[i] - } - dispatch(receivedGtfsEntities(newEntities, getState().signs.all)) - }).then((error) => { - console.log('error', error) - }) + return dispatch(fetchStopsAndRoutes(getState().signs.entities, 'SIGNS')) }) } } @@ -166,23 +160,6 @@ export function editSign (sign) { } } -export function fetchEntity (entity, activeProject) { - const feed = activeProject.feedSources.find(f => getFeedId(f) === entity.entity.AgencyId) - const feedId = getFeedId(feed) - const url = entity.type === 'stop' - ? `/api/manager/stops/${entity.entity.StopId}?feed=${feedId}` - : `/api/manager/routes/${entity.entity.RouteId}?feed=${feedId}` - return fetch(url) - .then((response) => { - return response.json() - }) - .then((object) => { - return object - }).catch((error) => { - // console.log('caught', error) - }) -} - export const createDisplay = (name) => { return function (dispatch, getState) { console.log('creating display', name) @@ -219,7 +196,7 @@ export function saveDisplay (display, user) { body: JSON.stringify(display) }).then((res) => { console.log('status='+res.status) - }) + }).catch(error => console.log(error)) // } } @@ -282,6 +259,6 @@ export function saveSign (sign) { browserHistory.push('/signs') dispatch(fetchRtdSigns()) }) - }) + }).catch(error => console.log(error)) } } diff --git a/src/main/client/signs/components/AffectedEntity.js b/src/main/client/signs/components/AffectedEntity.js index 24a916106..112e2b3d7 100644 --- a/src/main/client/signs/components/AffectedEntity.js +++ b/src/main/client/signs/components/AffectedEntity.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Component, PropTypes } from 'react' import { Panel, Row, Col, ButtonGroup, Button, Glyphicon, Label } from 'react-bootstrap' @@ -6,7 +6,7 @@ import GtfsSearch from '../../gtfs/components/gtfssearch' import { getFeed } from '../../common/util/modules' -export default class AffectedEntity extends React.Component { +export default class AffectedEntity extends Component { constructor (props) { super(props) } @@ -24,15 +24,16 @@ export default class AffectedEntity extends React.Component { } let labelComponents = [] const stopName = entity.stop - ? {entity.stop.stop_name} ({entity.stop.stop_id}) - : entity.stop_id + ? {entity.stop.stop_name} ({entity.stop.stop_id}) + : entity.stop_id labelComponents.push(stopName) - const routes = entity.route ? for {entity.route.length} routes - : entity.route_id ? for {entity.route_id.length} routes - : [add routes] + const routes = entity.route + ? for {entity.route.length} routes + : entity.route_id + ? for {entity.route_id} + : [add routes] labelComponents.push(routes) - console.log(stopName) return ( {labelComponents ? labelComponents.map(l => (l)) : null} @@ -61,10 +62,8 @@ export default class AffectedEntity extends React.Component { paddingLeft: '30px' } let selectedFeeds = [this.props.entity.agency] || this.props.activeFeeds - console.log(selectedFeeds) let selectedRoute = this.props.entity.route let selectedStop = this.props.entity.stop - console.log(selectedStop) switch (this.props.entity.type) { case 'STOP': return ( @@ -98,12 +97,11 @@ export default class AffectedEntity extends React.Component { } } -class RouteSelector extends React.Component { +class RouteSelector extends Component { constructor (props) { super(props) } render () { - console.log('render route ent', this.props.route) const getRouteName = (route) => { let routeName = route.route_short_name && route.route_long_name ? `${route.route_short_name} - ${route.route_long_name}` @@ -113,10 +111,8 @@ class RouteSelector extends React.Component { : null return routeName } - var routes = [] const feed = this.props.route ? getFeed(this.props.feeds, this.props.route.feed_id) : null const agencyName = feed ? feed.name : 'Unknown agency' - return (
{ - console.log(evt) if (evt) { let routes = evt.map(e => e.route) this.props.entityUpdated(this.props.entity, 'ROUTES', routes) } - else if (evt == null) + else { this.props.entityUpdated(this.props.entity, 'ROUTES', []) + } }} value={ this.props.route @@ -147,15 +143,14 @@ class RouteSelector extends React.Component { } } -class StopSelector extends React.Component { +class StopSelector extends Component { constructor (props) { super(props) } render () { - console.log('render stop ent', this.props.stop) var stops = [] const feed = this.props.stop ? getFeed(this.props.feeds, this.props.stop.feed_id) : null - const agencyName = feed ? feed.name : 'Unkown agency' + const agencyName = feed ? feed.name : 'Unknown agency' return (
{ - console.log(displays) const displayOptions = displays !== null && displays.length > 0 ? displays.map(display => ({display, value: display.Id, label: display.DisplayTitle, location: display.LocationDescription })) : [] return { options: displayOptions } }) diff --git a/src/main/client/signs/components/SignEditor.js b/src/main/client/signs/components/SignEditor.js index d3e9bcc0a..309304437 100644 --- a/src/main/client/signs/components/SignEditor.js +++ b/src/main/client/signs/components/SignEditor.js @@ -1,7 +1,7 @@ import React from 'react' import Helmet from 'react-helmet' -import { Grid, Row, Col, ButtonGroup, Button, Input, Panel, Glyphicon } from 'react-bootstrap' +import { Grid, Row, Col, ButtonGroup, Button, Input, Panel, Glyphicon, FormGroup, ControlLabel, FormControl } from 'react-bootstrap' import DisplaySelector from './DisplaySelector' import ManagerPage from '../../common/components/ManagerPage' @@ -43,7 +43,6 @@ export default class SignEditor extends React.Component { ? 1 + this.props.sign.affectedEntities.map(e => e.id).reduce((initial, current) => initial > current ? initial : current) : 1 - console.log('displays', this.props.sign.displays) return ( - + + + + + - diff --git a/src/main/client/signs/components/SignPreview.js b/src/main/client/signs/components/SignPreview.js index f08f1d8b4..6d950cc92 100644 --- a/src/main/client/signs/components/SignPreview.js +++ b/src/main/client/signs/components/SignPreview.js @@ -72,10 +72,13 @@ export default class SignPreview extends React.Component { {publishedLabel} {this.props.sign.published ? 'Published' : 'Draft'}

Stops:  - {this.props.sign.affectedEntities ? this.props.sign.affectedEntities.map(e => - { - return e.stop ? ({e.stop.stop_name} ) : e.stop_id - }) : '' + {this.props.sign.affectedEntities + ? this.props.sign.affectedEntities.map((e, index) => { + return e.stop + ? ({e.stop.stop_name} ) + : e.stop_id + }) + : '' }

diff --git a/src/main/client/signs/components/SignsList.js b/src/main/client/signs/components/SignsList.js index dd0159a4d..b380fa672 100644 --- a/src/main/client/signs/components/SignsList.js +++ b/src/main/client/signs/components/SignsList.js @@ -1,8 +1,10 @@ import React, { Component, PropTypes } from 'react' -import { Row, ButtonGroup, Button, FormControl, FormGroup } from 'react-bootstrap' +import { Row, ButtonGroup, Button, FormControl, FormGroup, Badge } from 'react-bootstrap' import Icon from 'react-fa' +import { sentence as toSentenceCase } from 'change-case' import SignPreview from './SignPreview' +import { FILTERS } from '../util' export default class SignsList extends Component { static propTypes = { @@ -43,18 +45,16 @@ export default class SignsList extends Component { - - - + {FILTERS.map(f => ( + + ))}

 
@@ -74,7 +74,7 @@ export default class SignsList extends Component { onDeleteClick={this.props.onDeleteClick} /> }) - :

No alerts found.

+ :

No signs found.

}
diff --git a/src/main/client/signs/components/SignsViewer.js b/src/main/client/signs/components/SignsViewer.js index fc0ade36a..44cd9a97c 100644 --- a/src/main/client/signs/components/SignsViewer.js +++ b/src/main/client/signs/components/SignsViewer.js @@ -43,9 +43,13 @@ export default class SignsViewer extends React.Component { - + + + + + { return { onComponentMount: (initialProps) => { const signId = initialProps.location.pathname.split('/sign/')[1] - console.log(signId) if (initialProps.sign) return if (!signId) { - console.log('sign', initialProps.sign) dispatch(fetchProjects()) .then((activeProject) => { - console.log('done fetching projects') if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')){ console.log('cannot create sign!') browserHistory.push('/signs') @@ -54,16 +51,13 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) } else { - console.log('need to set active sign') dispatch(fetchProjects()) .then((activeProject) => { - console.log('done fetching projects') if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')){ console.log('cannot create sign!') browserHistory.push('/signs') return } - console.log('getting', signId) dispatch(setActiveSign(+signId)) }) } diff --git a/src/main/client/signs/containers/MainSignsViewer.js b/src/main/client/signs/containers/MainSignsViewer.js index 5deb75f99..a83f3504c 100644 --- a/src/main/client/signs/containers/MainSignsViewer.js +++ b/src/main/client/signs/containers/MainSignsViewer.js @@ -21,9 +21,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { - console.log(initialProps) if (!initialProps.signs || initialProps.signs.length === 0 || !initialProps.project.feedSources) { - console.log('fetching projects...') dispatch(fetchProjects()) } diff --git a/src/main/client/signs/containers/VisibleSignsList.js b/src/main/client/signs/containers/VisibleSignsList.js index d99771b52..b62aa31a1 100644 --- a/src/main/client/signs/containers/VisibleSignsList.js +++ b/src/main/client/signs/containers/VisibleSignsList.js @@ -8,6 +8,7 @@ import { setVisibilitySearchText, setVisibilityFilter } from '../actions/visibil import SignsList from '../components/SignsList' import { getFeedsForPermission } from '../../common/util/permissions' +import { FILTERS } from '../util' const getVisibleSigns = (signs, visibilityFilter) => { if (!signs) return [] @@ -21,19 +22,27 @@ const getVisibleSigns = (signs, visibilityFilter) => { return visibleSigns.filter(sign => sign.published) case 'DRAFT': return visibleSigns.filter(sign => !sign.published) + default: + return visibleSigns } return visibleSigns } const mapStateToProps = (state, ownProps) => { - console.log('all signs', state.signs.all) // if (state.projects.active !== null && state.projects.active.feeds !== null ) + let filterCounts = {} + if (!state.signs.isFetching) { + FILTERS.map(f => { + filterCounts[f] = getVisibleSigns(state.signs.all, {filter: f}).length + }) + } return { isFetching: state.signs.isFetching, signs: getVisibleSigns(state.signs.all, state.signs.filter), visibilityFilter: state.signs.filter, editableFeeds: getFeedsForPermission(state.projects.active, state.user, 'edit-etid'), - publishableFeeds: getFeedsForPermission(state.projects.active, state.user, 'approve-etid') + publishableFeeds: getFeedsForPermission(state.projects.active, state.user, 'approve-etid'), + filterCounts } } diff --git a/src/main/client/signs/reducers/activeSign.js b/src/main/client/signs/reducers/activeSign.js index bb2d23d5d..16f2c9fbd 100644 --- a/src/main/client/signs/reducers/activeSign.js +++ b/src/main/client/signs/reducers/activeSign.js @@ -4,7 +4,6 @@ const activeSign = (state = null, action) => { let entities, foundIndex, displayIndex switch (action.type) { case 'UPDATE_ACTIVE_SIGN': - console.log('update active sign', action.sign) return update(state, {$set: action.sign}) case 'CREATE_SIGN': case 'EDIT_SIGN': @@ -13,7 +12,6 @@ const activeSign = (state = null, action) => { case 'SET_ACTIVE_SIGN_TITLE': return update(state, {title: {$set: action.title}}) case 'SET_ACTIVE_SIGN_DESCRIPTION': - console.log(action) return update(state, {description: {$set: action.description}}) case 'SET_ACTIVE_SIGN_URL': return update(state, {url: {$set: action.url}}) @@ -80,14 +78,11 @@ const activeSign = (state = null, action) => { case 'UPDATE_DISPLAYS': return update(state, {displays: {$set: action.displays}}) case 'TOGGLE_CONFIG_FOR_DISPLAY': - console.log('TOGGLE_CONFIG_FOR_DISPLAY', action) displayIndex = state.displays.findIndex(d => d.Id === action.display.Id) switch (action.configType) { case 'DRAFT': - console.log('changing draft id', action.configId) return update(state, {displays: {[displayIndex]: {$merge: {DraftDisplayConfigurationId: action.configId}}}}) case 'PUBLISHED': - console.log('changing pub id', action.configId) // if setting published config to new value (not null), set draft config to null // if (action.configId) // return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId, DraftDisplayConfigurationId: null}}}}) @@ -97,7 +92,6 @@ const activeSign = (state = null, action) => { } return state case 'UPDATE_ACTIVE_SIGN_ENTITY': - console.log('update entity', action.entity, action.field, action.value) foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) if (foundIndex !== -1) { switch (action.field) { @@ -158,13 +152,8 @@ const activeSign = (state = null, action) => { ] return update(state, {affectedEntities: {$set: entities}}) case 'ROUTES': - console.log(action) - // let routeId = action.value !== null ? action.value.route_id : null updatedEntity = update(action.entity, { route: {$set: action.value}, - // route_id: {$set: routeId}, - // agency: {$set: action.agency}, - // TODO: update agency id from feed id? }) entities = [ ...state.affectedEntities.slice(0, foundIndex), diff --git a/src/main/client/signs/reducers/signs.js b/src/main/client/signs/reducers/signs.js index 897cb9611..b12a7a82d 100644 --- a/src/main/client/signs/reducers/signs.js +++ b/src/main/client/signs/reducers/signs.js @@ -1,4 +1,6 @@ import update from 'react-addons-update' +import clone from 'clone' + import { getFeedId } from '../../common/util/modules' const signs = (state = { @@ -10,7 +12,7 @@ const signs = (state = { filter: 'ALL' } }, action) => { - let foundIndex + let foundIndex, signs, entities switch (action.type) { case 'SET_SIGN_VISIBILITY_SEARCH_TEXT': return update(state, {filter: {searchText: {$set: action.text}}}) @@ -35,22 +37,37 @@ const signs = (state = { filter: 'ALL' } } - case 'RECEIVED_SIGN_GTFS_ENTITIES': - let index = 0 - for (var i = 0; i < action.gtfsObjects.length; i++) { - let ent = action.gtfsObjects[i] - // console.log(ent.gtfs) - if (action.gtfsSigns) { - let sign = action.gtfsSigns.find(a => a.id === ent.entity.DisplayConfigurationId) - console.log(sign) - console.log(ent) - let selectedEnt = - sign.affectedEntities.find(e => e.agencyAndStop === ent.entity.agencyAndStop) // || - // sign.affectedEntities.find(e => e.agency_id === ent.entity.AgencyId && ent.stop_id === ent.entity.StopId) - console.log(selectedEnt) + case 'RECEIVED_GTFS_STOPS_AND_ROUTES': + if (action.module !== 'SIGNS') { + return state + } + entities = state.entities + signs = clone(state.all) + // for those entities we requested, assign the gtfs data to the saved entities + for (var i = 0; i < entities.length; i++) { + let feed = action.results.feeds.find(f => f.feed_id === entities[i].entity.AgencyId) + if (feed) { + let gtfs = entities[i].type === 'stop' + ? feed.stops.find(s => s.stop_id === entities[i].entity.StopId) + : entities[i].type === 'route' + ? feed.routes.find(s => s.route_id === entities[i].entity.RouteId) + : null + if (gtfs) { + gtfs.feed_id = feed.feed_id + } + entities[i].gtfs = gtfs + } + } + // iterate over processed gtfs entities + for (var i = 0; i < entities.length; i++) { + let ent = entities[i] + if (ent.gtfs && signs){ + let sign = signs.find(s => s.id === ent.entity.DisplayConfigurationId) + let selectedEnt = sign && sign.affectedEntities.find(e => e.agencyAndStop === ent.entity.agencyAndStop) if (selectedEnt && ent.type === 'stop'){ selectedEnt.stop = ent.gtfs } + // route is an array for signs if (ent.type === 'route'){ let route = ent.gtfs ? ent.gtfs : ent.entity selectedEnt.route.push(route) @@ -60,7 +77,7 @@ const signs = (state = { return { isFetching: false, - all: action.gtfsSigns, + all: signs, entities: [], filter: { searchText: null, @@ -68,7 +85,6 @@ const signs = (state = { } } case 'RECEIVED_RTD_DISPLAYS': - console.log('got displays', state, action.rtdDisplays) // let signIndex = state.all.find if (state.all !== null) { let displayMap = {} @@ -78,7 +94,6 @@ const signs = (state = { if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) continue count++ - console.log(d) if (d.DraftDisplayConfigurationId) { if (displayMap[d.DraftDisplayConfigurationId] && displayMap[d.DraftDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { displayMap[d.DraftDisplayConfigurationId].push(d) @@ -96,20 +111,16 @@ const signs = (state = { } } } - console.log(count) - console.log('display map', displayMap) let newSigns = state.all.map(s => { s.displays = displayMap[s.id] ? displayMap[s.id] : [] return s }) - console.log(newSigns) return update(state, {all: {$set: newSigns}}) } return state case 'RECEIVED_RTD_SIGNS': const entityList = [] - console.log(action.rtdSigns) - let signs = action.rtdSigns + signs = action.rtdSigns for (var i = 0; i < signs.length; i++) { let action = signs[i] if (typeof action !== 'undefined' && action.DisplayConfigurationDetails && action.DisplayConfigurationDetails.length > 0) { @@ -125,8 +136,6 @@ const signs = (state = { } } } - console.log('entityList', entityList) - const allSigns = action.rtdSigns.map((rtdSign) => { let project = action.activeProject let details = rtdSign.DisplayConfigurationDetails || [] diff --git a/src/main/client/signs/util/index.js b/src/main/client/signs/util/index.js new file mode 100644 index 000000000..3a44c30cf --- /dev/null +++ b/src/main/client/signs/util/index.js @@ -0,0 +1 @@ +export const FILTERS = ['ALL', 'PUBLISHED', 'DRAFT'] From 275e3fe6672ab7257e13844a51da2465b04371e1 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:41:48 -0400 Subject: [PATCH 063/323] sidebar active styles and refactoring --- src/main/client/common/components/Sidebar.js | 49 ++++++++----- .../common/components/SidebarNavItem.js | 68 +++++++++++++------ 2 files changed, 77 insertions(+), 40 deletions(-) diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index c1d76cad7..55856c5b0 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -7,9 +7,10 @@ import Icon from 'react-fa' import SidebarNavItem from './SidebarNavItem' import SidebarPopover from './SidebarPopover' import JobMonitor from './JobMonitor' -import { getComponentMessages, getMessage } from '../util/config' +import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' import icon from '../../assets/application_icon.png' +import longIcon from '../../assets/application_logo.png' export default class Sidebar extends Component { @@ -48,10 +49,10 @@ export default class Sidebar extends Component { const messages = getComponentMessages('DatatoolsNavbar') const navbarStyle = { - width: this.props.expanded ? 150 : 50, + width: this.props.expanded ? 130 : 50, height: '100%', position: 'fixed', - borderRadius: 0 + borderRadius: 0, } const logoContainerStyle = { @@ -75,34 +76,44 @@ export default class Sidebar extends Component { const logoLabelStyle = { marginLeft: 40, + marginTop: 6, lineHeight: '95%', color: '#bbb', - fontSize: 16 + fontSize: 13, + fontWeight: 'bold' } - + const expandedIcon =
const closePopover = () => this.setState({visiblePopover: null}) - - return
- -
{ browserHistory.push('/home') }} + const brand = ( + +
{this.props.expanded - ?
GTFS Data
Manager
+ ?
GTFS Data
Manager
// TODO: replace with long icon : null }
+ + ) + return
+ + {brand} + {/* Top nav */}
{this.props.children}
-
+ {/* Bottom nav */} +
this.navSelected('job')} /> @@ -126,7 +137,9 @@ export default class Sidebar extends Component { /> {/* User Popover */} - this.state.visiblePopover === 'user' } close={() => closePopover()} @@ -153,14 +166,14 @@ export default class Sidebar extends Component { { this.props.setSidebarExpanded(!this.props.expanded) }} + onChange={() => { this.props.setSidebarExpanded(!this.props.expanded) }} > Show Sidebar Labels { this.props.setTutorialHidden(!this.props.hideTutorial) }} + onChange={() => { this.props.setTutorialHidden(!this.props.hideTutorial) }} > Hide editor tutorial diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index 525bc2052..c9a7a8948 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -1,5 +1,6 @@ import React, { Component, PropTypes } from 'react' import { Icon } from 'react-fa' +import { Link } from 'react-router' import { Tooltip, OverlayTrigger } from 'react-bootstrap' export default class SidebarNavItem extends Component { @@ -10,7 +11,8 @@ export default class SidebarNavItem extends Component { icon: PropTypes.string, label: PropTypes.string, onClick: PropTypes.func, - image: PropTypes.string + image: PropTypes.string, + link: PropTypes.string } constructor (props) { @@ -23,12 +25,21 @@ export default class SidebarNavItem extends Component { } render () { + const activeColor = this.props.active ? '#fff' : this.state.hover ? '#fff' : '#ccc' const containerStyle = { - marginBottom: 20, + paddingTop: 10, + paddingBottom: 10, + paddingRight: this.props.expanded ? 10 : 10, + paddingLeft: 10, + marginTop: 5, + marginBottom: 5, + marginLeft: -15, cursor: 'pointer', - color: this.props.active ? '#fff' : this.state.hover ? '#fff' : '#ccc', - // backgroundColor: this.props.active ? '#bbb' : 'rgba(0,0,0,0)', - // padding: 5 + color: activeColor, + borderLeft: this.props.active ? '4px solid #2889CA' : '4px solid rgba(0,0,0,0)', + textDecoration: 'none', + borderRight: this.props.active ? '5px solid rgba(0,0,0,0)' : '5px solid rgba(0,0,0,0)', + backgroundColor: this.props.active ? '#313131' : 'rgba(0,0,0,0)', } const iconContainerStyle = { @@ -50,6 +61,8 @@ export default class SidebarNavItem extends Component { const labelStyle = { fontWeight: 'bold', + marginTop: 3, + fontSize: 12, marginLeft: 30 } const icon = this.props.image @@ -60,23 +73,34 @@ export default class SidebarNavItem extends Component {
const tooltip = {this.props.label} + let containerProps = { + onMouseEnter: () => this.toggleHover(), + onMouseLeave: () => this.toggleHover(), + } + if (!this.props.link) { + containerProps.onClick = () => this.props.onClick() + } + const container = ( +
+ {icon} + {this.props.expanded + ?
{this.props.label}
+ : null + } +
+
+ ) + const navItem = this.props.link + ? + {container} + + : container return this.props.expanded - ?
this.toggleHover()} onMouseLeave={() => this.toggleHover()} - onClick={() => this.props.onClick()} - > - {icon} -
{this.props.label}
-
-
- : -
this.toggleHover()} onMouseLeave={() => this.toggleHover()} - onClick={() => this.props.onClick()} - > - {icon} -
-
- + ? navItem + : + {navItem} + } } From 79e6105af218edf72e835c7f29d729c7e5022de1 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:43:20 -0400 Subject: [PATCH 064/323] fixed proptype --- src/main/client/common/containers/WatchButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/common/containers/WatchButton.js b/src/main/client/common/containers/WatchButton.js index a6a80bbfb..dd2d69dce 100644 --- a/src/main/client/common/containers/WatchButton.js +++ b/src/main/client/common/containers/WatchButton.js @@ -8,7 +8,7 @@ import { getComponentMessages, getMessage, getConfigProperty } from '../util/con class WatchButton extends Component { static propTypes = { dispatch: PropTypes.func, - isWatching: PropTypes.string, + isWatching: PropTypes.bool, user: PropTypes.object, target: PropTypes.string, subscriptionType: PropTypes.string, From 638ddaac7e330a1dd49756513e6ffb773d1891b5 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:44:24 -0400 Subject: [PATCH 065/323] change sidebar width --- src/main/client/common/containers/PageContent.js | 2 +- src/main/client/editor/components/EditorMap.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/common/containers/PageContent.js b/src/main/client/common/containers/PageContent.js index 26218cc26..190a64fd5 100644 --- a/src/main/client/common/containers/PageContent.js +++ b/src/main/client/common/containers/PageContent.js @@ -21,7 +21,7 @@ class Content extends Component { return (
Date: Mon, 31 Oct 2016 08:46:20 -0400 Subject: [PATCH 066/323] fix deactivate snapshot on process snapshot merge --- .../controllers/api/SnapshotController.java | 2 +- .../editor/datastore/VersionedDataStore.java | 2 +- .../editor/jobs/ProcessGtfsSnapshotMerge.java | 10 ++++----- .../datatools/editor/models/Snapshot.java | 22 ++++++++++++++++++- 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java index 4f59a13e2..27c1211f2 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java @@ -186,7 +186,7 @@ public static Object restoreSnapshot (Request req, Response res) { // the snapshot we have just restored is now current; make the others not current // TODO: add this loop back in... taken out in order to compile - Collection snapshots = gtx.snapshots.subMap(new Tuple2(local.feedId, null), new Tuple2(local.feedId, Fun.HI)).values(); + Collection snapshots = Snapshot.getSnapshots(local.feedId); for (Snapshot o : snapshots) { if (o.id.equals(local.id)) continue; diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java index 818a9987d..ec6b4217c 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java @@ -149,7 +149,7 @@ public static List restore (Snapshot s) { LOG.info("Restoring snapshot {} of agency {}", s.version, s.feedId); long startTime = System.currentTimeMillis(); List ret = tx.restore(s.feedId); - LOG.info("Restored snapshot in %.2f seconds", (System.currentTimeMillis() - startTime) / 1000D); + LOG.info(String.format("Restored snapshot in %.2f seconds", (System.currentTimeMillis() - startTime) / 1000D)); return ret; } finally { tx.close(); diff --git a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java index fb1bd991c..edba4f3fc 100755 --- a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java +++ b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java @@ -41,6 +41,7 @@ import java.util.*; import java.util.Map.Entry; +import static com.conveyal.datatools.editor.models.Snapshot.deactivateSnapshots; import static spark.Spark.halt; @@ -113,14 +114,12 @@ public void run () { for(String key : feedTx.trips.keySet()) feedTx.trips.remove(key); LOG.info("Cleared old data"); - // input = feedVersion.getGtfsFeed(); - // TODO: use GtfsCache? synchronized (status) { status.message = "Loading GTFS file..."; status.percentComplete = 5; } input = feedVersion.getGtfsFeed(); - if(input == null) return; + if (input == null) return; LOG.info("GtfsImporter: importing feed..."); synchronized (status) { @@ -430,12 +429,14 @@ public Agency apply(Tuple2 input) { } // commit the feed TXs first, so that we have orphaned data rather than inconsistent data on a commit failure feedTx.commit(); - gtx.commit(); + + deactivateSnapshots(feedVersion.feedSourceId, null); // create an initial snapshot for this FeedVersion Snapshot snapshot = VersionedDataStore.takeSnapshot(feed.id, "Snapshot of " + feedVersion.getName(), "none"); + LOG.info("Imported GTFS file: " + agencyCount + " agencies; " + routeCount + " routes;" + stopCount + " stops; " + stopTimeCount + " stopTimes; " + tripCount + " trips;" + shapePointCount + " shapePoints"); synchronized (status) { status.message = "Import complete!"; @@ -453,7 +454,6 @@ public Agency apply(Tuple2 input) { finally { feedTx.rollbackIfOpen(); gtx.rollbackIfOpen(); - // set job as complete jobFinished(); } diff --git a/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java b/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java index 28db2c4ef..7e947709c 100644 --- a/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java +++ b/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java @@ -81,11 +81,31 @@ public Snapshot clone () { } @JsonIgnore - public static Collection getSnapshots(String feedId) { + public static Collection getSnapshots (String feedId) { GlobalTx gtx = VersionedDataStore.getGlobalTx(); return gtx.snapshots.subMap(new Tuple2(feedId, null), new Tuple2(feedId, Fun.HI)).values(); } + public static void deactivateSnapshots (String feedId, Snapshot ignore) { + GlobalTx gtx = VersionedDataStore.getGlobalTx(); + Collection snapshots = Snapshot.getSnapshots(feedId); + try { + for (Snapshot o : snapshots) { + if (ignore != null && o.id.equals(ignore.id)) + continue; + + Snapshot cloned = o.clone(); + cloned.current = false; + gtx.snapshots.put(o.id, cloned); + } + gtx.commit(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + gtx.rollbackIfOpen(); + } + } + public static Snapshot get(String snapshotId) { Tuple2 decodedId; try { From 02e6cb0fa1e8ef06449056fafca547cbfd2ed62a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:46:44 -0400 Subject: [PATCH 067/323] lang tweak --- i18n/english.yml | 2 ++ i18n/espanol.yml | 1 + i18n/francais.yml | 1 + 3 files changed, 4 insertions(+) diff --git a/i18n/english.yml b/i18n/english.yml index e73631d77..33d410619 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -78,6 +78,7 @@ EditorFeedSourcePanel: date: Date name: Name download: Download + active: Active restore: Make active load: Load for Editing confirmLoad: This will override all active GTFS Editor data for this Feed Source with the data from this version. If there is unsaved work in the Editor you want to keep, you must snapshot the current Editor data first. Are you sure you want to continue? @@ -148,6 +149,7 @@ ResultTable: problemType: Problem Type priority: Priority affectedIds: Affected ID(s) + line: Line description: Description DeploymentViewer: versions: Feed Versions diff --git a/i18n/espanol.yml b/i18n/espanol.yml index 9e464e481..f851f78ec 100644 --- a/i18n/espanol.yml +++ b/i18n/espanol.yml @@ -121,6 +121,7 @@ ResultTable: problemType: Problem Type priority: Priority affectedIds: Affected ID(s) + line: Line description: Description DeploymentViewer: versions: Feed Versions diff --git a/i18n/francais.yml b/i18n/francais.yml index 3617056fd..37b8050ae 100644 --- a/i18n/francais.yml +++ b/i18n/francais.yml @@ -121,6 +121,7 @@ ResultTable: problemType: Problem Type priority: Priority affectedIds: Affected ID(s) + line: Line description: Description DeploymentViewer: versions: Feed Versions From 02017208f73eabba20a2be8714b8d104ce57b6b2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:47:49 -0400 Subject: [PATCH 068/323] sidebar width + change onCLick to link --- .../common/components/DatatoolsNavbar.js | 2 +- .../client/common/components/ManagerPage.js | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/main/client/common/components/DatatoolsNavbar.js b/src/main/client/common/components/DatatoolsNavbar.js index b51a6c3cf..c9855977c 100644 --- a/src/main/client/common/components/DatatoolsNavbar.js +++ b/src/main/client/common/components/DatatoolsNavbar.js @@ -92,7 +92,7 @@ export default class DatatoolsNavbar extends Component { ) const navBarStyle = { - left: this.props.sidebarExpanded ? 150 : 50, + left: this.props.sidebarExpanded ? 130 : 50, height: '40px' } diff --git a/src/main/client/common/components/ManagerPage.js b/src/main/client/common/components/ManagerPage.js index 789247471..e97f34b2f 100644 --- a/src/main/client/common/components/ManagerPage.js +++ b/src/main/client/common/components/ManagerPage.js @@ -16,7 +16,7 @@ import { getConfigProperty, isModuleEnabled } from '../util/config' export default class ManagerPage extends Component { static propTypes = { - children: PropTypes.object + // children: PropTypes.object } constructor (props) { super(props) @@ -33,7 +33,9 @@ export default class ManagerPage extends Component { showSelectFileModal (props) { this.refs.selectFileModal.open(props) } - + isActive (path) { + return window.location.pathname.split('/')[1] === path + } render () { return (
@@ -47,17 +49,18 @@ export default class ManagerPage extends Component { - {/* browserHistory.push(`/project`) } /> - browserHistory.push(`/admin`) } /> - browserHistory.push(`/`) } />*/} + {isModuleEnabled('alerts') ? browserHistory.push(`/alerts`) } + link={`/alerts`} + active={this.isActive('alerts')} /> : null } @@ -65,7 +68,8 @@ export default class ManagerPage extends Component { ? browserHistory.push(`/signs`) } + link={`/signs`} + active={this.isActive('signs')} /> : null } From 51ad60d847c9f9af98a79b719a9c575dd3fe78f6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:48:25 -0400 Subject: [PATCH 069/323] todo --- src/main/client/common/util/modules.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/common/util/modules.js b/src/main/client/common/util/modules.js index f871e525e..5ce8d127a 100644 --- a/src/main/client/common/util/modules.js +++ b/src/main/client/common/util/modules.js @@ -2,6 +2,7 @@ import { getConfigProperty } from './config' export const getFeed = (feeds, id) => { // console.log(feeds, id) + // TODO: move use_extension to extension enabled?? const useMtc = getConfigProperty('modules.gtfsapi.use_extension') === 'mtc' const feed = feeds ? feeds.find(f => useMtc ? f.externalProperties.MTC.AgencyId === id : f.id === id) : null return feed From 0f6dbd6f718dc522f3608631b16b5d10debe385f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:49:25 -0400 Subject: [PATCH 070/323] onClick to link --- .../client/editor/components/EditorSidebar.js | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/client/editor/components/EditorSidebar.js b/src/main/client/editor/components/EditorSidebar.js index 2cca5794b..70541c799 100644 --- a/src/main/client/editor/components/EditorSidebar.js +++ b/src/main/client/editor/components/EditorSidebar.js @@ -17,28 +17,31 @@ export default class EditorSidebar extends Component { feedInfo: PropTypes.object, setActiveEntity: PropTypes.func } - + isActive (item, component) { + return component === item.id || component === 'scheduleexception' && item.id === 'calendar' + } render () { const { activeComponent, feedSource, setActiveEntity } = this.props return ( - browserHistory.push(`/feed/${feedSource.id}`) } /> + {gtfsIcons.map(item => { return item.hideSidebar ? null : { - if (activeComponent === item.id) { - browserHistory.push(`/feed/${feedSource.id}/edit/`) - } else { - setActiveEntity(feedSource.id, item.id) - } - }} + active={this.isActive(item, activeComponent)} + link={!feedSource + ? '/home' + : activeComponent === item.id + ? `/feed/${feedSource.id}/edit/` + : `/feed/${feedSource.id}/edit/${item.id}`} /> })} From 9f49ee13c1cb20dc37b18f67a8d8f1089897bfe3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:51:07 -0400 Subject: [PATCH 071/323] remove edit alert, feedInfo force, and fetch snapshots on restore --- src/main/client/editor/actions/editor.js | 6 +++--- src/main/client/editor/actions/feedInfo.js | 8 +++++--- src/main/client/editor/actions/snapshots.js | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/client/editor/actions/editor.js b/src/main/client/editor/actions/editor.js index fad76104b..2bef089ce 100644 --- a/src/main/client/editor/actions/editor.js +++ b/src/main/client/editor/actions/editor.js @@ -102,9 +102,9 @@ export function settingActiveGtfsEntity (feedSourceId, component, entityId, subC export function setActiveGtfsEntity (feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId) { return function (dispatch, getState) { // TODO: figure out a good way to handle route changes without confirm window - if (getState().editor.active.edited && !window.confirm('You have unsaved changes. Discard changes?')) { - return false - } + // if (getState().editor.active.edited && !window.confirm('You have unsaved changes. Discard changes?')) { + // return false + // } let previousFeedSourceId = getState().editor.feedSourceId if (previousFeedSourceId && feedSourceId !== previousFeedSourceId) { dispatch(clearGtfsContent()) diff --git a/src/main/client/editor/actions/feedInfo.js b/src/main/client/editor/actions/feedInfo.js index c640462e1..b2ecd5d99 100644 --- a/src/main/client/editor/actions/feedInfo.js +++ b/src/main/client/editor/actions/feedInfo.js @@ -33,9 +33,11 @@ export function fetchFeedInfo (feedId) { .then(res => res.json()) .then(feedInfo => { dispatch(receiveFeedInfo(feedInfo)) - if (!feedInfo) { - dispatch(setActiveGtfsEntity(feedId, 'feedinfo')) - } + // TODO: should we force users to create feed info? + // probably not because it's not a required table + // if (!feedInfo) { + // dispatch(setActiveGtfsEntity(feedId, 'feedinfo')) + // } }) } } diff --git a/src/main/client/editor/actions/snapshots.js b/src/main/client/editor/actions/snapshots.js index bfbaf7a67..f67340234 100644 --- a/src/main/client/editor/actions/snapshots.js +++ b/src/main/client/editor/actions/snapshots.js @@ -53,6 +53,7 @@ export function restoreSnapshot (feedSource, snapshot) { return response.json() }).then((stops) => { dispatch(restoredSnapshot(snapshot.name)) + dispatch(fetchSnapshots(feedSource)) dispatch(clearGtfsContent()) dispatch(fetchBaseGtfs(feedSource.id)) }) From b19e9a645e47033f9a9cd64181319192354c1b47 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:51:39 -0400 Subject: [PATCH 072/323] correctly render active v inactive snapshots --- .../components/EditorFeedSourcePanel.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 2c850dcc6..a64fb2ccb 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -34,6 +34,13 @@ export default class EditorFeedSourcePanel extends Component { const currentSnapshot = this.props.feedSource.editorSnapshots && this.props.feedSource.editorSnapshots.length ? this.props.feedSource.editorSnapshots.find(s => s.current) : null + const activeSnapshots = this.props.feedSource.editorSnapshots + ? this.props.feedSource.editorSnapshots.filter(s => s.current) + : [] + const inactiveSnapshots = this.props.feedSource.editorSnapshots + ? this.props.feedSource.editorSnapshots.filter(s => !s.current) + : [] + console.log(inactiveSnapshots) return ( @@ -52,9 +59,9 @@ export default class EditorFeedSourcePanel extends Component { Inactive snapshots}> - {this.props.feedSource.editorSnapshots.length === 1 + {inactiveSnapshots.length === 0 ? No other snapshots - : this.props.feedSource.editorSnapshots.map(s => { + : inactiveSnapshots.map(s => { return ( ) @@ -113,10 +120,15 @@ class SnapshotItem extends Component {

- { // - {moment(snapshot.snapshotTime).fromNow()} + created {moment(snapshot.snapshotTime).fromNow()}

) From 0a5eb16dfbb30fc4c0cd63cb6cb9e7bb2634ffe3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:51:56 -0400 Subject: [PATCH 073/323] proptype --- src/main/client/editor/components/EntityDetails.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 0d1682c62..e705c5ea7 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -30,7 +30,7 @@ export default class EntityDetails extends Component { mapState: PropTypes.object, - activeEntityId: PropTypes.string.isRequired, + activeEntityId: PropTypes.string, width: PropTypes.number.isRequired, setActiveEntity: PropTypes.func.isRequired, From 7e675c5d42c9bde4c85ed27e4c02fc3eaa636303 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:52:17 -0400 Subject: [PATCH 074/323] sidebar width --- src/main/client/editor/components/GtfsEditor.js | 2 +- src/main/client/editor/components/TimetableEditor.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index afbfc3de7..e077557be 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -150,7 +150,7 @@ export default class GtfsEditor extends Component { />
Date: Mon, 31 Oct 2016 08:53:27 -0400 Subject: [PATCH 075/323] clean up code, shouldComponentUpdate check for feedsource changes --- src/main/client/manager/components/ProjectViewer.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index db2b81b6b..8cf1ea213 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -59,7 +59,13 @@ export default class ProjectViewer extends Component { errorMessage: 'Uploaded file must be a valid zip file (.zip).' }) } - + shouldComponentUpdate (newProps) { + if (!shallowEqual(newProps, this.props)) { + return true + } else { + return false + } + } componentWillMount () { this.props.onComponentMount(this.props) } @@ -122,7 +128,7 @@ export default class ProjectViewer extends Component { {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') - ? Sync}> + ? Sync}> {isExtensionEnabled('transitland') ? { - console.log(this.props.project) this.props.updateAllFeeds(this.props.project) }} > From 98025d41cd407d602813e36ed2fe78eb8f0113bb Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:54:13 -0400 Subject: [PATCH 076/323] fix gtfs-api for mtc --- .../controllers/api/GtfsApiController.java | 148 +++++++++--------- 1 file changed, 73 insertions(+), 75 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java index bb0261534..b112475b1 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java @@ -2,21 +2,24 @@ import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.ObjectListing; +import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; import com.conveyal.datatools.manager.DataManager; -import com.conveyal.datatools.manager.models.FeedSource; -import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.persistence.FeedStore; -import com.conveyal.datatools.manager.utils.FeedUpdater; +import com.conveyal.datatools.manager.jobs.FeedUpdater; import com.conveyal.gtfs.api.ApiMain; import com.conveyal.gtfs.api.Routes; +import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; /** * Created by landon on 4/12/16. @@ -27,107 +30,102 @@ public class GtfsApiController { public static FeedUpdater feedUpdater; private static AmazonS3Client s3 = new AmazonS3Client(); public static ApiMain gtfsApi; - public static String cacheDirectory; public static String bucketFolder; public static void register (String apiPrefix) throws IOException { // store list of GTFS feed eTags here - List eTagList = new ArrayList<>(); + Map eTagMap = new HashMap<>(); + // uses bucket, folder, and local cache according to main app config gtfsApi.initialize(DataManager.gtfsCache); - // check for use extension... + // check for use of extension... String extensionType = DataManager.getConfigPropertyAsText("modules.gtfsapi.use_extension"); - - if ("mtc".equals(extensionType)){ - LOG.info("Using extension " + extensionType + " for service alerts module"); - feedBucket = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_bucket"); - bucketFolder = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_download_prefix"); - - eTagList.addAll(registerS3Feeds(feedBucket, cacheDirectory)); - - // set feedUpdater to poll for new feeds at specified frequency (in seconds) - feedUpdater = new FeedUpdater(eTagList, 0, DataManager.getConfigProperty("modules.gtfsapi.update_frequency").asInt()); - } - // if not using MTC extension - else { - LOG.warn("No extension provided for GTFS API"); - if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { - feedBucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); - bucketFolder = FeedStore.s3Prefix; - } - else { - feedBucket = null; - } + switch (extensionType) { + case "mtc": + LOG.info("Using extension " + extensionType + " for service alerts module"); + feedBucket = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_bucket"); + bucketFolder = DataManager.getConfigPropertyAsText("extensions." + extensionType + ".s3_download_prefix"); + + // Adds feeds on startup + eTagMap.putAll(registerS3Feeds(null, feedBucket, bucketFolder)); + break; + default: + LOG.warn("No extension provided for GTFS API"); + // use application s3 bucket and s3Prefix + if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { + feedBucket = DataManager.getConfigPropertyAsText("application.data.gtfs_s3_bucket"); + bucketFolder = FeedStore.s3Prefix; + } + else { + feedBucket = null; + } + break; } - // check for load on startup - if ("true".equals(DataManager.getConfigPropertyAsText("modules.gtfsapi.load_on_startup"))) { - LOG.warn("Loading all feeds into gtfs api (this may take a while)..."); - // use s3 - if ("true".equals(DataManager.getConfigPropertyAsText("application.data.use_s3_storage"))) { - eTagList.addAll(registerS3Feeds(feedBucket, cacheDirectory)); - - // set feedUpdater to poll for new feeds at specified frequency (in seconds) - feedUpdater = new FeedUpdater(eTagList, 0, DataManager.config.get("modules").get("gtfsapi").get("update_frequency").asInt()); - } -// else, use local directory - else { - // iterate over latest feed versions - for (FeedSource fs : FeedSource.getAll()) { - FeedVersion v = fs.getLatest(); - if (v != null) { - try { - gtfsApi.registerFeedSource(fs.id, v.getGtfsFile()); - eTagList.add(v.hash); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } + // check for update interval (in seconds) and initialize feedUpdater + JsonNode updateFrequency = DataManager.getConfigProperty("modules.gtfsapi.update_frequency"); + if (updateFrequency != null) { + feedUpdater = new FeedUpdater(eTagMap, 0, updateFrequency.asInt()); } // set gtfs-api routes with apiPrefix Routes.routes(apiPrefix); } -// public static void registerFeedSourceLatest(String id) { -// FeedSource fs = FeedSource.get(id); -// FeedVersion v = fs.getLatest(); -// if (v != null) { -// try { -// gtfsApi.registerFeedSource(id, v.getGtfsFile()); -// } catch (Exception e) { -// e.printStackTrace(); -// } -// } -// } - - public static List registerS3Feeds (String bucket, String dir) { - List eTags = new ArrayList<>(); + public static Map registerS3Feeds (Map eTags, String bucket, String dir) { + if (eTags == null) { + eTags = new HashMap<>(); + } + Map newTags = new HashMap<>(); // iterate over feeds in download_prefix folder and register to gtfsApi (MTC project) ObjectListing gtfsList = s3.listObjects(bucket, dir); for (S3ObjectSummary objSummary : gtfsList.getObjectSummaries()) { String eTag = objSummary.getETag(); - if (!eTags.contains(eTag)) { + if (!eTags.containsValue(eTag)) { String keyName = objSummary.getKey(); // don't add object if it is a dir if (keyName.equals(dir)){ continue; } - LOG.info("Adding feed " + keyName); - String feedId = keyName.split("/")[1]; + String filename = keyName.split("/")[1]; + String feedId = filename.replace(".zip", ""); try { - gtfsApi.registerFeedSource(feedId, FeedVersion.get(feedId).getGtfsFile()); + LOG.warn("New version found for " + keyName + " is null. Downloading from s3..."); + S3Object object = s3.getObject(bucket, keyName); + InputStream in = object.getObjectContent(); + byte[] buf = new byte[1024]; + File file = new File(FeedStore.basePath, filename); + OutputStream out = new FileOutputStream(file); + int count; + while( (count = in.read(buf)) != -1) + { + if( Thread.interrupted() ) + { + throw new InterruptedException(); + } + out.write(buf, 0, count); + } + out.close(); + in.close(); + + // delete old mapDB files + String[] dbFiles = {".db", ".db.p"}; + for (String type : dbFiles) { + File db = new File(FeedStore.basePath, feedId + type); + db.delete(); + } + newTags.put(feedId, eTag); + + // initiate load of feed source into API with get call + gtfsApi.getFeedSource(feedId); } catch (Exception e) { - e.printStackTrace(); + LOG.warn("Could not load feed " + keyName, e); } - eTags.add(eTag); } } - return eTags; + return newTags; } } From c1c7e7b30752659bccc93294ef3a9c1add1eeb54 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:54:40 -0400 Subject: [PATCH 077/323] isExtensionEnabled --- .../com/conveyal/datatools/manager/DataManager.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index 17c629c09..fa64f77ea 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -136,6 +136,9 @@ public static void main(String[] args) throws IOException { FareController.register(apiPrefix); } + // log all exceptions to system.out + exception(Exception.class, (e, req, res) -> LOG.error("error", e)); + // module-specific controllers if (isModuleEnabled("deployment")) { DeploymentController.register(apiPrefix); @@ -256,19 +259,23 @@ public static boolean isModuleEnabled(String moduleName) { return "true".equals(getConfigPropertyAsText("modules." + moduleName + ".enabled")); } + public static boolean isExtensionEnabled(String extensionName) { + return "true".equals(getConfigPropertyAsText("extensions." + extensionName + ".enabled")); + } + private static void registerExternalResources() { - if ("true".equals(getConfigPropertyAsText("extensions.mtc.enabled"))) { + if (isExtensionEnabled("mtc")) { LOG.info("Registering MTC Resource"); registerExternalResource(new MtcFeedResource()); } - if ("true".equals(getConfigPropertyAsText("extensions.transitland.enabled"))) { + if (isExtensionEnabled("transitland")) { LOG.info("Registering TransitLand Resource"); registerExternalResource(new TransitLandFeedResource()); } - if ("true".equals(getConfigPropertyAsText("extensions.transitfeeds.enabled"))) { + if (isExtensionEnabled("transitfeeds")) { LOG.info("Registering TransitFeeds Resource"); registerExternalResource(new TransitFeedsFeedResource()); } From 42d9449a36ffc65c484d95f965df4ff38f77be45 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:56:04 -0400 Subject: [PATCH 078/323] fix up status update --- .../jobs/BuildTransportNetworkJob.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java index f026385ff..6f136fec2 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/BuildTransportNetworkJob.java @@ -24,7 +24,7 @@ public class BuildTransportNetworkJob extends MonitorableJob { public FeedVersion feedVersion; - public TransportNetwork result; + private TransportNetwork result; public Status status; public BuildTransportNetworkJob (FeedVersion feedVersion, String owner) { @@ -39,7 +39,17 @@ public BuildTransportNetworkJob (FeedVersion feedVersion, String owner) { public void run() { System.out.println("Building network"); try { - feedVersion.buildTransportNetwork(eventBus); + if (feedVersion.validationResult != null) { + feedVersion.buildTransportNetwork(eventBus); + } + else { + synchronized (status) { + status.message = "Transport network skipped because of bad validation."; + status.percentComplete = 100; + status.error = true; + status.completed = true; + } + } } catch (Exception e) { e.printStackTrace(); synchronized (status) { @@ -48,12 +58,13 @@ public void run() { status.error = true; status.completed = true; } - } - synchronized (status) { - status.message = "Transport network built successfully!"; - status.percentComplete = 100; - status.completed = true; + if (!status.error) { + synchronized (status) { + status.message = "Transport network built successfully!"; + status.percentComplete = 100; + status.completed = true; + } } jobFinished(); } From 940b8a3ed3fb8c24299beecc66906a5a59fe363b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:58:24 -0400 Subject: [PATCH 079/323] update job statuses, feedVersion fields, and feedUpdater eTag tracking --- .../CreateFeedVersionFromSnapshotJob.java | 42 ++++++++++++------- .../datatools/manager/jobs/FeedUpdater.java | 29 +++++-------- .../manager/jobs/FetchSingleFeedJob.java | 10 ++++- .../manager/jobs/ProcessSingleFeedJob.java | 6 --- .../manager/jobs/ValidateFeedJob.java | 21 ++++------ 5 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/CreateFeedVersionFromSnapshotJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/CreateFeedVersionFromSnapshotJob.java index adb5adcc2..30c269566 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/CreateFeedVersionFromSnapshotJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/CreateFeedVersionFromSnapshotJob.java @@ -20,13 +20,13 @@ public class CreateFeedVersionFromSnapshotJob extends MonitorableJob { public static final Logger LOG = LoggerFactory.getLogger(CreateFeedVersionFromSnapshotJob.class); - private FeedSource feedSource; + private FeedVersion feedVersion; private String snapshotId; private Status status; - public CreateFeedVersionFromSnapshotJob (FeedSource feedSource, String snapshotId, String owner) { - super(owner, "Creating Feed Version from Snapshot for " + feedSource.name, JobType.CREATE_FEEDVERSION_FROM_SNAPSHOT); - this.feedSource = feedSource; + public CreateFeedVersionFromSnapshotJob (FeedVersion feedVersion, String snapshotId, String owner) { + super(owner, "Creating Feed Version from Snapshot for " + feedVersion.getFeedSource().name, JobType.CREATE_FEEDVERSION_FROM_SNAPSHOT); + this.feedVersion = feedVersion; this.snapshotId = snapshotId; this.status = new Status(); status.message = "Initializing..."; @@ -34,8 +34,6 @@ public CreateFeedVersionFromSnapshotJob (FeedSource feedSource, String snapshotI @Override public void run() { - FeedVersion v = new FeedVersion(feedSource); - File file = null; try { @@ -43,23 +41,35 @@ public void run() { SnapshotController.writeSnapshotAsGtfs(snapshotId, file); } catch (Exception e) { e.printStackTrace(); - LOG.error("Unable to create temp file for snapshot"); - halt(400); + String message = "Unable to create temp file for snapshot"; + LOG.error(message); + synchronized (status) { + status.error = true; + status.message = message; + status.completed = true; + } } try { - v.newGtfsFile(new FileInputStream(file)); + feedVersion.newGtfsFile(new FileInputStream(file)); } catch (Exception e) { LOG.error("Unable to open input stream from upload"); - halt(400, "Unable to read uploaded feed"); + String message = "Unable to read uploaded feed"; + synchronized (status) { + status.error = true; + status.message = message; + status.completed = true; + } } - v.name = Snapshot.get(snapshotId).name + " Snapshot Export"; - v.hash(); - v.save(); - - addNextJob(new ProcessSingleFeedJob(v, owner)); - + feedVersion.name = Snapshot.get(snapshotId).name + " Snapshot Export"; + feedVersion.hash(); + feedVersion.save(); + synchronized (status) { + status.message = "Version created successfully."; + status.completed = true; + status.percentComplete = 100.0; + } jobFinished(); } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java index 3423ac768..d98b68ce1 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FeedUpdater.java @@ -5,6 +5,7 @@ import com.conveyal.datatools.manager.controllers.api.GtfsApiController; import java.util.List; +import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; @@ -16,31 +17,23 @@ * Created by landon on 3/24/16. */ public class FeedUpdater { - public List eTags; + public Map eTags; private static Timer timer; private static AmazonS3Client s3; public static final Logger LOG = LoggerFactory.getLogger(FeedUpdater.class); - public FeedUpdater(List eTagList, int delay, int seconds){ - this.eTags = eTagList; -// this.timer = new Timer(); - - // TODO: check for credentials?? -// this.s3 = new AmazonS3Client(); - - DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, delay, seconds, TimeUnit.SECONDS); - this.timer.schedule(new UpdateFeedsTask(), delay*1000, seconds*1000); - - + public FeedUpdater(Map eTagMap, int delay, int seconds) { + this.eTags = eTagMap; + DataManager.scheduler.scheduleAtFixedRate(new UpdateFeedsTask(), delay, seconds, TimeUnit.SECONDS); } - public void addFeedETag(String eTag){ - this.eTags.add(eTag); + public void addFeedETag(String id, String eTag){ + this.eTags.put(id, eTag); } - public void addFeedETags(List eTagList){ - this.eTags.addAll(eTagList); + public void addFeedETags(Map eTagList){ + this.eTags.putAll(eTagList); } public void cancel(){ @@ -48,11 +41,11 @@ public void cancel(){ } - class UpdateFeedsTask extends TimerTask { + class UpdateFeedsTask implements Runnable { public void run() { LOG.info("Fetching feeds..."); LOG.info("Current eTag list " + eTags.toString()); - List updatedTags = GtfsApiController.registerS3Feeds(eTags, GtfsApiController.feedBucket, GtfsApiController.bucketFolder); + Map updatedTags = GtfsApiController.registerS3Feeds(eTags, GtfsApiController.feedBucket, GtfsApiController.bucketFolder); Boolean feedsUpdated = updatedTags.isEmpty() ? false : true; addFeedETags(updatedTags); if (!feedsUpdated) { diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java index a6bf0c648..ffad71200 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/FetchSingleFeedJob.java @@ -26,8 +26,14 @@ public FetchSingleFeedJob (FeedSource feedSource, String owner) { @Override public void run() { // TODO: fetch automatically vs. manually vs. in-house - result = feedSource.fetch(eventBus, owner); - jobFinished(); + try { + result = feedSource.fetch(eventBus, owner); + jobFinished(); + } catch (Exception e) { + jobFinished(); + // throw any halts that may have prevented this job from finishing + throw e; + } if (result != null) { new ProcessSingleFeedJob(result, this.owner).run(); } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java index 45cb41b42..e5554ceb3 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java @@ -51,12 +51,6 @@ public void run() { } new Thread(validateJob).start(); - - // notify any extensions of the change - for(String resourceType : DataManager.feedResources.keySet()) { - DataManager.feedResources.get(resourceType).feedVersionCreated(feedVersion, null); - } - } } diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ValidateFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ValidateFeedJob.java index 7b283a28c..0d69b5e6b 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ValidateFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ValidateFeedJob.java @@ -2,6 +2,7 @@ import com.conveyal.datatools.common.status.MonitorableJob; import com.conveyal.datatools.common.status.StatusEvent; +import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; import com.google.common.eventbus.Subscribe; import org.slf4j.Logger; @@ -15,8 +16,8 @@ public class ValidateFeedJob extends MonitorableJob { public static final Logger LOG = LoggerFactory.getLogger(ValidateFeedJob.class); - private FeedVersion feedVersion; - private Status status; + public FeedVersion feedVersion; + public Status status; public ValidateFeedJob(FeedVersion version, String owner) { super(owner, "Validating Feed for " + version.getFeedSource().name, JobType.VALIDATE_FEED); @@ -33,10 +34,6 @@ public Status getStatus() { } } - public String getFeedVersionId() { - return feedVersion.id; - } - @Override public void run() { LOG.info("Running ValidateFeedJob for {}", feedVersion.id); @@ -45,14 +42,14 @@ public void run() { status.percentComplete = 30; } feedVersion.validate(eventBus); - synchronized (status) { - status.message = "Saving validation..."; - status.percentComplete = 90; - } feedVersion.save(); + if (!status.error) synchronized (status) { - status.message = "Validation complete!"; - status.percentComplete = 100; + if (!status.error) { + status.message = "Validation complete!"; + status.percentComplete = 100; + status.completed = true; + } } jobFinished(); } From fd4d0a5904f482d250b61db052020f53927ccdaf Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 08:58:46 -0400 Subject: [PATCH 080/323] add line --- .../manager/components/validation/GtfsValidationViewer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index d6226db18..4caa2a235 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -101,6 +101,7 @@ class ResultTable extends React.Component { {getMessage(messages, 'problemType')} {getMessage(messages, 'priority')} {getMessage(messages, 'affectedIds')} + {getMessage(messages, 'line')} {getMessage(messages, 'description')} @@ -111,6 +112,7 @@ class ResultTable extends React.Component { {val.errorType} {val.priority} {val.affectedEntityId} + {val.line} {val.message} ) From bd6d692b74a5be7e7ab1adb295b1b316e60f2690 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:01:45 -0400 Subject: [PATCH 081/323] refactor, fix proptypes and warnings --- .../manager/components/FeedSourceTable.js | 5 +--- .../manager/components/FeedSourceViewer.js | 3 +-- .../components/FeedVersionNavigator.js | 1 + .../manager/components/FeedVersionViewer.js | 25 +++++++++++++------ .../manager/components/ProjectSettings.js | 2 +- .../client/manager/components/UserHomePage.js | 1 + 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index 603a1f370..162dbf575 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -41,7 +41,6 @@ export default class FeedSourceTable extends Component { } render () { - console.log(this.state) const messages = getComponentMessages('ProjectViewer') const hover = N/A) const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') @@ -148,7 +146,7 @@ class FeedSourceTableRow extends Component { }} onMouseLeave={() => { if (!this.props.hold) - this.props.onHover(false) + this.props.onHover(null) }} > { - console.log(key) switch (key) { case 'delete': return this.deleteFeed() diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index 22a836221..ed09ae0fb 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -138,7 +138,7 @@ export default class FeedSourceViewer extends Component { const activeTab = ['settings', 'comments', 'snapshots'].indexOf(this.props.activeComponent) === -1 || typeof this.props.routeParams.feedVersionIndex !== 'undefined' ? '' : this.props.activeComponent - console.log(this.props.activeComponent, this.props.routeParams.feedVersionIndex) + // console.log(this.props.activeComponent, this.props.routeParams.feedVersionIndex) const activeSettings = !resourceType ? Settings}> @@ -246,7 +246,6 @@ export default class FeedSourceViewer extends Component { {!this.props.versionSection ? : this.props.versionSection === 'issues' @@ -110,12 +111,7 @@ export class VersionButtonToolbar extends Component { isPublic: PropTypes.bool, hasVersions: PropTypes.bool, - // listView: PropTypes.bool, - // newNotePosted: PropTypes.func, - // notesRequested: PropTypes.func, - // fetchValidationResult: PropTypes.func, - feedVersionRenamed: PropTypes.func, downloadFeedClicked: PropTypes.func, deleteFeedVersionConfirmed: PropTypes.func, loadFeedVersionForEditing: PropTypes.func @@ -184,6 +180,21 @@ class VersionSectionSelector extends Component { feedVersionIndex: PropTypes.number, versionSection: PropTypes.string } + renderIssuesLabel (version) { + const color = version.validationSummary.loadStatus !== 'SUCCESS' + ? 'danger' + : version.validationSummary.errorCount + ? 'warning' + : 'success' + const text = version.validationSummary.loadStatus !== 'SUCCESS' + ? 'critical error' + : version.validationSummary.errorCount + return ( + + ) + } render () { const { version } = this.props return ( @@ -193,7 +204,7 @@ class VersionSectionSelector extends Component { Version summary - Validation issues + Validation issues {this.renderIssuesLabel(version)} {isModuleEnabled('gtfsplus') ? diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index 738f9a1cb..144e3d6db 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -63,7 +63,7 @@ export default class ProjectSettings extends Component { {getMessage(messages, 'title')}}> - + Organization name {activeProject.name} : {this.props.user.profile.nickname} From 604d93e2c9539200b4aa66739ef042bb281acf91 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:03:42 -0400 Subject: [PATCH 082/323] add publishedVersionId, fix status update for validate, and clean up getLatest --- .../datatools/manager/models/FeedSource.java | 18 +++++------------- .../datatools/manager/models/FeedVersion.java | 14 +++++++++++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index aea252165..2bd9df647 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -102,6 +102,8 @@ public void setProject(Project proj) { */ public String snapshotVersion; + public String publishedVersionId; + /** * Create a new feed. */ @@ -292,18 +294,7 @@ public void save (boolean commit) { */ @JsonIgnore public FeedVersion getLatest () { -// DataStore vs = new FeedVersion(this); -// if (vs == null){ -// return null; -// } - FeedVersion v = null; - try { - Class localClass = Class.forName(FeedVersion.class.getName()); - v = FeedVersion.versionStore.findFloor("version", new Fun.Tuple2(this.id, Fun.HI)); - } catch (ClassNotFoundException e) { - LOG.info("Error getting feed version", e); - } - + FeedVersion v = FeedVersion.versionStore.findFloor("version", new Fun.Tuple2(this.id, Fun.HI)); // the ID doesn't necessarily match, because it will fall back to the previous source in the store if there are no versions for this source if (v == null || !v.feedSourceId.equals(this.id)) @@ -337,7 +328,8 @@ public Date getLastUpdated() { @JsonView(JsonViews.UserInterface.class) public FeedValidationResultSummary getLatestValidation () { FeedVersion latest = getLatest(); - return latest != null ? new FeedValidationResultSummary(latest.validationResult) : null; + FeedValidationResult result = latest != null ? latest.validationResult : null; + return result != null ?new FeedValidationResultSummary(result) : null; } @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index cbae46bbf..052185f0c 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -288,18 +288,26 @@ public void validate(EventBus eventBus) { tripsPerDate = stats.getTripCountPerDateOfService(); }catch (Exception e) { e.printStackTrace(); + statusMap.put("message", "Unable to validate feed."); + statusMap.put("percentComplete", 0.0); + statusMap.put("error", true); + eventBus.post(statusMap); + e.printStackTrace(); +// this.validationResult = null; + validationResult.loadStatus = LoadStatus.OTHER_FAILURE; + return; } } catch (Exception e) { - LOG.error("Unable to validate feed {}", this); + LOG.error("Unable to validate feed {}", this.id); // eventBus.post(new StatusEvent("Unable to validate feed.", 0, true)); statusMap.put("message", "Unable to validate feed."); statusMap.put("percentComplete", 0.0); statusMap.put("error", true); eventBus.post(statusMap); e.printStackTrace(); - this.validationResult = null; +// this.validationResult = null; validationResult.loadStatus = LoadStatus.OTHER_FAILURE; - halt(400, "Error validating feed..."); +// halt(400, "Error validating feed..."); return; } From 32d432e700350fcf041c9548117ca1a3fe43f275 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:04:46 -0400 Subject: [PATCH 083/323] add publish func, refactor versionRenamed --- .../manager/containers/ActiveFeedSourceViewer.js | 4 ++-- .../manager/containers/ActiveFeedVersionNavigator.js | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/client/manager/containers/ActiveFeedSourceViewer.js b/src/main/client/manager/containers/ActiveFeedSourceViewer.js index 287fac9cf..ffd7d1a36 100644 --- a/src/main/client/manager/containers/ActiveFeedSourceViewer.js +++ b/src/main/client/manager/containers/ActiveFeedSourceViewer.js @@ -82,8 +82,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { feedSourcePropertyChanged: (feedSource, propName, newValue) => { return dispatch(updateFeedSource(feedSource, { [propName]: newValue })) }, - feedVersionRenamed: (feedSource, feedVersion, name) => { - dispatch(renameFeedVersion(feedSource, feedVersion, name)) + feedVersionRenamed: (feedVersion, name) => { + dispatch(renameFeedVersion(feedVersion, name)) }, gtfsPlusDataRequested: (feedVersion) => { dispatch(downloadGtfsPlusFeed(feedVersion.id)) diff --git a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js index bb41877be..8e2d12b6a 100644 --- a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js +++ b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js @@ -10,7 +10,9 @@ import { fetchValidationResult, postNoteForFeedVersion, renameFeedVersion, - setActiveVersion + setActiveVersion, + publishFeedVersion, + fetchFeedSource } from '../actions/feeds' import { loadFeedVersionForEditing } from '../../editor/actions/snapshots' @@ -76,7 +78,13 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, fetchValidationResult: (feedVersion, isPublic) => { dispatch(fetchValidationResult(feedVersion, isPublic)) - } + }, + publishFeedVersion: (feedVersion) => { + dispatch(publishFeedVersion(feedVersion)) + // .then(() => { + // dispatch(fetchFeedSource(feedVersion.feedSource.id, true, true)) + // }) + }, } } From 9a98471bf124a4972abb4165dd962887859132b7 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:05:05 -0400 Subject: [PATCH 084/323] clean up import --- .../com/conveyal/datatools/manager/persistence/FeedStore.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index 36df94486..d6acfb64d 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -14,9 +14,7 @@ import com.amazonaws.services.s3.transfer.TransferManager; import com.amazonaws.services.s3.transfer.Upload; import com.conveyal.datatools.manager.DataManager; -import com.conveyal.datatools.manager.controllers.api.GtfsApiController; import com.conveyal.datatools.manager.models.FeedSource; -import com.conveyal.gtfs.GTFSFeed; import gnu.trove.list.TLongList; import gnu.trove.list.array.TLongArrayList; import org.apache.commons.io.FileUtils; From 42cfba313e2a3e28bf7c53b118d39f49c24ece82 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:05:44 -0400 Subject: [PATCH 085/323] rm newline --- .../datatools/manager/extensions/mtc/MtcFeedResource.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java b/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java index 36e1dca49..359755e1b 100644 --- a/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java +++ b/src/main/java/com/conveyal/datatools/manager/extensions/mtc/MtcFeedResource.java @@ -218,7 +218,6 @@ public void feedVersionCreated(FeedVersion feedVersion, String authHeader) { AmazonS3 s3client = new AmazonS3Client(creds); s3client.putObject(new PutObjectRequest( s3Bucket, keyName, feedVersion.getGtfsFile())); - } private void writeCarrierToRtd(RtdCarrier carrier, boolean createNew, String authHeader) { From 1311a87fddbdc58eeb17db9c1ae4bbc3433ea38e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:06:34 -0400 Subject: [PATCH 086/323] fix check on upload hash, add publish to external route --- .../api/FeedVersionController.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index 160f52ee0..16e8a6631 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -99,7 +99,7 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { halt(400, "Feed is autofetched! Cannot upload."); } - + FeedVersion latest = s.getLatest(); FeedVersion v = new FeedVersion(s); v.setUser(userProfile); @@ -125,10 +125,9 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc v.hash(); - FeedVersion latest = s.getLatest(); - // Check that hashes don't match (as long as v and latest are not the same entry) if (latest != null && latest.hash.equals(v.hash)) { + LOG.error("Upload matches latest version."); v.getGtfsFile().delete(); // Uploaded feed is same as latest version v.delete(); @@ -154,10 +153,10 @@ public static Boolean createFeedVersionFromSnapshot (Request req, Response res) Auth0UserProfile userProfile = req.attribute("user"); // TODO: should this be edit privelege? FeedSource s = requestFeedSourceById(req, "manage"); - + FeedVersion v = new FeedVersion(s); CreateFeedVersionFromSnapshotJob createFromSnapshotJob = - new CreateFeedVersionFromSnapshotJob(s, req.queryParams("snapshotId"), userProfile.getUser_id()); - + new CreateFeedVersionFromSnapshotJob(v, req.queryParams("snapshotId"), userProfile.getUser_id()); + createFromSnapshotJob.addNextJob(new ProcessSingleFeedJob(v, userProfile.getUser_id())); new Thread(createFromSnapshotJob).start(); return true; @@ -370,8 +369,18 @@ private static FeedDownloadToken getPublicDownloadToken (Request req, Response r token.save(); return token; } + private static FeedVersion publishToExternalResource (Request req, Response res) { + FeedVersion version = requestFeedVersion(req, "manage"); - + // notify any extensions of the change + for(String resourceType : DataManager.feedResources.keySet()) { + DataManager.feedResources.get(resourceType).feedVersionCreated(version, null); + } + FeedSource fs = version.getFeedSource(); + fs.publishedVersionId = version.id; + fs.save(); + return version; + } private static Object downloadFeedVersionWithToken (Request req, Response res) { FeedDownloadToken token = FeedDownloadToken.get(req.params("token")); @@ -396,6 +405,7 @@ public static void register (String apiPrefix) { post(apiPrefix + "secure/feedversion", FeedVersionController::createFeedVersion, json::write); post(apiPrefix + "secure/feedversion/fromsnapshot", FeedVersionController::createFeedVersionFromSnapshot, json::write); put(apiPrefix + "secure/feedversion/:id/rename", FeedVersionController::renameFeedVersion, json::write); + post(apiPrefix + "secure/feedversion/:id/publish", FeedVersionController::publishToExternalResource, json::write); delete(apiPrefix + "secure/feedversion/:id", FeedVersionController::deleteFeedVersion, json::write); get(apiPrefix + "public/feedversion", FeedVersionController::getAllFeedVersions, json::write); From f871110550f1c866a65d2f2e51fac46097354245 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:07:27 -0400 Subject: [PATCH 087/323] add publish reducer, reformat, clear message on receive version --- src/main/client/manager/reducers/projects.js | 26 +++++++++----------- src/main/client/manager/reducers/status.js | 1 + 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index 3fd0cdab6..74b7276bf 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -226,7 +226,15 @@ const projects = (state = { } } ) - + case 'PUBLISHED_FEEDVERSION': + projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) + sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedVersion.feedSource.id) + versionIndex = state.all[projectIndex].feedSources[sourceIndex].feedVersions.findIndex(v => v.id === action.feedVersion.id) + return update(state, + {all: {[projectIndex]: {feedSources: {[sourceIndex]: {publishedVersionId: { + $set: action.feedVersion.id + }}}}}} + ) case 'RECEIVE_VALIDATION_RESULT': projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedVersion.feedSource.id) @@ -239,19 +247,9 @@ const projects = (state = { // result[error.file].push(error) // }) return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {feedVersions: - {[versionIndex]: - {$merge: {validationResult: action.validationResult}} - } - } - } - } - } - } + {all: {[projectIndex]: {feedSources: {[sourceIndex]: {feedVersions: { + [versionIndex]: {$merge: {validationResult: action.validationResult}} + }}}}}} ) case 'RECEIVE_FEEDVERSION_ISOCHRONES': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) diff --git a/src/main/client/manager/reducers/status.js b/src/main/client/manager/reducers/status.js index 0c560fe12..39418a7e8 100644 --- a/src/main/client/manager/reducers/status.js +++ b/src/main/client/manager/reducers/status.js @@ -147,6 +147,7 @@ const config = (state = { case 'RECEIVE_FEEDSOURCES': case 'RECEIVE_FEEDSOURCE': case 'RECEIVE_FEEDVERSIONS': + case 'RECEIVE_FEEDVERSION': case 'RECEIVE_FETCH_FEED_FOR_PROJECT': case 'RECEIVE_PUBLIC_FEEDS': case 'RECEIVE_VALIDATION_RESULT': From 3dcc3990db1c702a227c4665d94756ddc0c2a70b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:08:25 -0400 Subject: [PATCH 088/323] add publish and handlingFinishedJob actions --- src/main/client/manager/actions/feeds.js | 44 ++++++++++++++++++++--- src/main/client/manager/actions/status.js | 12 +++++-- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/main/client/manager/actions/feeds.js b/src/main/client/manager/actions/feeds.js index eba95ed3e..6beda6e0b 100644 --- a/src/main/client/manager/actions/feeds.js +++ b/src/main/client/manager/actions/feeds.js @@ -145,7 +145,7 @@ export function receiveFeedSource (feedSource) { } } -export function fetchFeedSource (feedSourceId, fetchVersions) { +export function fetchFeedSource (feedSourceId, withVersions = false, withSnapshots = false) { return function (dispatch, getState) { console.log('fetchFeedSource', feedSourceId) dispatch(requestingFeedSource()) @@ -166,7 +166,8 @@ export function fetchFeedSource (feedSourceId, fetchVersions) { } console.log('got feedSource', feedSource) dispatch(receiveFeedSource(feedSource)) - if(fetchVersions) dispatch(fetchFeedVersions(feedSource)) + if (withVersions) dispatch(fetchFeedVersions(feedSource)) + if (withSnapshots) dispatch(fetchSnapshots(feedSource)) return feedSource }) } @@ -317,6 +318,31 @@ export function fetchFeedVersion (feedVersionId) { } } +export function publishingFeedVersion (feedVersion) { + return { + type: 'PUBLISHING_FEEDVERSION', + feedVersion + } +} + +export function publishedFeedVersion (feedVersion) { + return { + type: 'PUBLISHED_FEEDVERSION', + feedVersion + } +} + +export function publishFeedVersion (feedVersion) { + return function (dispatch, getState) { + dispatch(publishingFeedVersion(feedVersion)) + const url = `/api/manager/secure/feedversion/${feedVersion.id}/publish` + return secureFetch(url, getState(), 'post') + .then(response => response.json()) + .then(version => { + return dispatch(publishedFeedVersion(version)) + }) + } +} export function fetchPublicFeedVersions (feedSource) { return function (dispatch, getState) { @@ -461,7 +487,15 @@ export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat const params = {fromLat, fromLon, toLat, toLon, date, fromTime, toTime} const url = `/api/manager/secure/feedversion/${feedVersion.id}/isochrones?${qs.stringify(params)}` return secureFetch(url, getState()) - .then(response => response.json()) + .then(res => { + console.log(res.status) + if (res.status === 202) { + // dispatch(setStatus) + console.log("building network") + return [] + } + return res.json() + }) .then(isochrones => { console.log('received isochrones ', isochrones) dispatch(receiveFeedVersionIsochrones(feedVersion.feedSource, feedVersion, isochrones)) @@ -511,13 +545,13 @@ export function renamingFeedVersion () { } } -export function renameFeedVersion (feedSource, feedVersion, name) { +export function renameFeedVersion (feedVersion, name) { return function (dispatch, getState) { dispatch(renamingFeedVersion()) const url = `/api/manager/secure/feedversion/${feedVersion.id}/rename?name=${name}` return secureFetch(url, getState(), 'put') .then((res) => { - dispatch(fetchFeedVersions(feedSource)) + dispatch(fetchFeedVersion(feedVersion.id)) }) } } diff --git a/src/main/client/manager/actions/status.js b/src/main/client/manager/actions/status.js index 90e22d5e4..2a5206021 100644 --- a/src/main/client/manager/actions/status.js +++ b/src/main/client/manager/actions/status.js @@ -1,5 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { fetchFeedVersion } from './feeds' +import { fetchFeedVersion, fetchFeedSource } from './feeds' import { fetchSnapshots } from '../../editor/actions/snapshots' export function setErrorMessage (message) { @@ -121,11 +121,19 @@ export function setJobMonitorVisible (visible) { } } +export function handlingFinishedJob (job) { + return { + type: 'HANDLING_FINISHED_JOB', + job + } +} + export function handleFinishedJob (job) { return function (dispatch, getState) { + dispatch(handlingFinishedJob(job)) switch (job.type) { case 'VALIDATE_FEED': - dispatch(fetchFeedVersion(job.feedVersionId)) + dispatch(fetchFeedSource(job.feedVersion.feedSource.id, true)) break case 'PROCESS_SNAPSHOT': dispatch(fetchSnapshots(job.feedVersion.feedSource)) From 2670a00ccba7a191b2b6729a31afed03d0758105 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:08:54 -0400 Subject: [PATCH 089/323] handle null feedInfo --- src/main/client/editor/reducers/editor.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/client/editor/reducers/editor.js b/src/main/client/editor/reducers/editor.js index d1f10e348..698c0d2dd 100644 --- a/src/main/client/editor/reducers/editor.js +++ b/src/main/client/editor/reducers/editor.js @@ -392,7 +392,22 @@ const editor = (state = defaultState, action) => { feed_publisher_url: action.feedInfo.feedPublisherUrl, feed_version: action.feedInfo.feedVersion } - : null + : { + // datatools props + id: null, + color: null, + defaultLat: null, + defaultLon: null, + defaultRouteType: null, + + // gtfs spec props + feed_end_date: null, + feed_start_date: null, + feed_lang: null, + feed_publisher_name: null, + feed_publisher_url: null, + feed_version: null + } let mapState = {...state.mapState} if (feedInfo && feedInfo.defaultLon && feedInfo.defaultLat) { mapState.bounds = getEntityBounds([feedInfo.defaultLon, feedInfo.defaultLat], 0.5) From de9e74695c65cbe4096f83d2dae62e49e0cd1d23 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 09:10:27 -0400 Subject: [PATCH 090/323] temporary getRouteName for alerts --- src/main/client/editor/util/gtfs.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/client/editor/util/gtfs.js b/src/main/client/editor/util/gtfs.js index 2a73c53cd..4e2615e74 100644 --- a/src/main/client/editor/util/gtfs.js +++ b/src/main/client/editor/util/gtfs.js @@ -165,6 +165,15 @@ export const getControlPoints = (pattern, snapToStops) => { return controlPoints } +export function getRouteNameAlerts (route) { + let routeName = route.route_short_name && route.route_long_name + ? `${route.route_short_name} - ${route.route_long_name}` + : route.route_long_name ? route.route_long_name + : route.route_short_name ? route.route_short_name + : null + return routeName +} + export function getRouteName (route) { let name = '' if(route.route_short_name) { @@ -177,7 +186,9 @@ export function getRouteName (route) { if(route.route_long_name) { name += route.route_long_name } - + if (route.route_id && !route.route_long_name && !route.route_short_name) { + name += route.route_id + } return name } From 8a24b3db7b56c659834ec04632d3d9ff710ff92f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 10:04:27 -0400 Subject: [PATCH 091/323] refactor, add publish button --- .../manager/components/FeedVersionReport.js | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index 912bb63c4..2a507cf2e 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -7,7 +7,7 @@ import Rcslider from 'rc-slider' import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' -import { getComponentMessages, getMessage, getConfigProperty, isModuleEnabled } from '../../common/util/config' +import { getComponentMessages, getMessage, getConfigProperty, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' // import Feed from './reporter/containers/Feed' import Patterns from './reporter/containers/Patterns' @@ -41,6 +41,7 @@ export default class FeedVersionReport extends Component { // fetchValidationResult: PropTypes.func, feedVersionRenamed: PropTypes.func, fetchValidationResult: PropTypes.func, + publishFeedVersion: PropTypes.func, // downloadFeedClicked: PropTypes.func, // loadFeedVersionForEditing: PropTypes.func } @@ -49,7 +50,7 @@ export default class FeedVersionReport extends Component { this.state = { tab: 'feed', mapHeight: MAP_HEIGHTS[0], - isochroneBand: 60 * 60 + isochroneBand: 60 * 60, } } getVersionDateLabel (version) { @@ -63,6 +64,17 @@ export default class FeedVersionReport extends Component { selectTab (tab) { this.setState({tab}) } + renderIsochroneMessage (version) { + if (version.isochrones && version.isochrones.features) { + return 'Move marker or change date/time to recalculate travel shed.' + } + else if (version.isochrones) { + return 'Reading transport network, please try again later.' + } + else { + return 'Click on map above to show travel shed for this feed.' + } + } render () { const version = this.props.version const messages = getComponentMessages('FeedVersionReport') @@ -127,19 +139,13 @@ export default class FeedVersionReport extends Component { @@ -188,6 +194,19 @@ export default class FeedVersionReport extends Component {

{getMessage(messages, 'stopTimesCount')}

+ {isExtensionEnabled('mtc') + ? + : null + } { /* -

Click on map above to show travel shed for this feed.

+ {/* isochrone message */} +

+ {this.renderIsochroneMessage(this.props.version)} +

From 047c2edda77da3d0b3f7023b2e66440b1478fb2f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 12:14:17 -0400 Subject: [PATCH 092/323] add back in legacy fields --- .../datatools/manager/models/FeedValidationResult.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java index de0fb8836..335193723 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java @@ -30,6 +30,13 @@ public class FeedValidationResult implements Serializable { public LocalDate endDate; public Rectangle2D bounds; + // legacy fields included for backwards compatibility (not currently used) + public String feedFileName; + public ValidationResult routes; + public ValidationResult stops; + public ValidationResult trips; + public ValidationResult shapes; + public FeedValidationResult(GTFSFeed feed, FeedStats stats) { this.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); this.agencyCount = stats.getAgencyCount(); From 8006d6c04d59744a5015fbadbf7b8b9e7363e7f2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 12:18:27 -0400 Subject: [PATCH 093/323] data dump constructor --- .../datatools/manager/models/FeedValidationResult.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java index 335193723..a0f55e209 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedValidationResult.java @@ -37,6 +37,9 @@ public class FeedValidationResult implements Serializable { public ValidationResult trips; public ValidationResult shapes; + // constructor for data dump load + public FeedValidationResult() {} + public FeedValidationResult(GTFSFeed feed, FeedStats stats) { this.agencies = stats.getAllAgencies().stream().map(agency -> agency.agency_id).collect(Collectors.toList()); this.agencyCount = stats.getAgencyCount(); From f930add7be6163eca51ef77a2a64b73ac3407638 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 12:48:32 -0400 Subject: [PATCH 094/323] add external props to dump --- .../manager/controllers/DumpController.java | 16 ++++++++++++++++ .../models/ExternalFeedSourceProperty.java | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index 6f177aa94..8a831a494 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -3,6 +3,7 @@ import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.auth.Auth0Users; import com.conveyal.datatools.manager.models.Deployment; +import com.conveyal.datatools.manager.models.ExternalFeedSourceProperty; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedValidationResult; import com.conveyal.datatools.manager.models.FeedVersion; @@ -48,6 +49,7 @@ public static class DatabaseState { public Collection notes; // public Collection users; public Collection deployments; + public Collection externalProperties; } private static JsonManager json = @@ -60,6 +62,7 @@ public static DatabaseState dump (Request req, Response res) throws JsonProcessi db.feedVersions = FeedVersion.getAll(); db.notes = Note.getAll(); db.deployments = Deployment.getAll(); + db.externalProperties = ExternalFeedSourceProperty.getAll(); return db; } @@ -108,6 +111,13 @@ public static boolean load (Request req, Response res) { d.save(false); } Deployment.commit(); + + for (ExternalFeedSourceProperty d : db.externalProperties) { + LOG.info("loading external properties {}", d.id); + d.save(false); + } + ExternalFeedSourceProperty.commit(); + LOG.info("load completed."); return true; } @@ -127,6 +137,12 @@ public static boolean loadLegacy (Request req, Response res) throws Exception { } Project.commit(); break; + case "projects": + for(int i=0; i< entry.getValue().size(); i++) { + loadLegacyProject(entry.getValue().get(i)); + } + Project.commit(); + break; case "feedSources": for(int i=0; i< entry.getValue().size(); i++) { loadLegacyFeedSource(entry.getValue().get(i)); diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 364d65d5b..92cb2c6a6 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -43,6 +43,13 @@ public void save (boolean commit) { propertyStore.saveWithoutCommit(id, this); } + /** + * Commit changes to the datastore + */ + public static void commit () { + propertyStore.commit(); + } + public static ExternalFeedSourceProperty find(FeedSource source, String resourceType, String name) { return propertyStore.getById(source.id + "_" +resourceType + "_" + name); } From faa02a56de5288f8ed4059229dd27491d28e90f8 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 13:02:30 -0400 Subject: [PATCH 095/323] add constructor for data dump --- .../datatools/manager/models/ExternalFeedSourceProperty.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 92cb2c6a6..3367e27ed 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -14,6 +14,9 @@ public class ExternalFeedSourceProperty extends Model { private FeedSource feedSource; + // constructor for data dump load + public ExternalFeedSourceProperty() {} + public ExternalFeedSourceProperty(FeedSource feedSource, String resourceType, String name, String value) { this.id = feedSource.id + "_" + resourceType + "_" + name; this.feedSource = feedSource; From add10cb26b133c870186a7c507e267bfdb739479 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 13:06:53 -0400 Subject: [PATCH 096/323] mark feedSourceId as jsonview --- .../datatools/manager/models/ExternalFeedSourceProperty.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 3367e27ed..0d66c5fc0 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.models; import com.conveyal.datatools.manager.persistence.DataStore; +import com.fasterxml.jackson.annotation.JsonView; import java.util.Collection; @@ -25,6 +26,7 @@ public ExternalFeedSourceProperty(FeedSource feedSource, String resourceType, St this.value = value; } + @JsonView(JsonViews.UserInterface.class) public String getFeedSourceId() { return feedSource.id; } From 9e91131de7644866473572a11297f5725ee34bc8 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 13:13:26 -0400 Subject: [PATCH 097/323] mark feedSourceId as jsonprop --- .../datatools/manager/models/ExternalFeedSourceProperty.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 0d66c5fc0..4f35f4b49 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.models; import com.conveyal.datatools.manager.persistence.DataStore; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonView; import java.util.Collection; @@ -26,7 +27,7 @@ public ExternalFeedSourceProperty(FeedSource feedSource, String resourceType, St this.value = value; } - @JsonView(JsonViews.UserInterface.class) + @JsonProperty public String getFeedSourceId() { return feedSource.id; } From d12fc7600ee6553c06dc1536fdfbb3e07eec3059 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 13:16:04 -0400 Subject: [PATCH 098/323] ignore unknown --- .../datatools/manager/models/ExternalFeedSourceProperty.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 4f35f4b49..21d3eada5 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.models; import com.conveyal.datatools.manager.persistence.DataStore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonView; @@ -9,6 +10,7 @@ /** * Created by demory on 3/30/16. */ +@JsonIgnoreProperties(ignoreUnknown = true) public class ExternalFeedSourceProperty extends Model { private static final long serialVersionUID = 1L; From 354321e92c4b9631c38e5131b3e6e22c246552c0 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 13:21:31 -0400 Subject: [PATCH 099/323] fix feedsourceId --- .../manager/models/ExternalFeedSourceProperty.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java index 21d3eada5..a19310873 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java +++ b/src/main/java/com/conveyal/datatools/manager/models/ExternalFeedSourceProperty.java @@ -23,7 +23,7 @@ public ExternalFeedSourceProperty() {} public ExternalFeedSourceProperty(FeedSource feedSource, String resourceType, String name, String value) { this.id = feedSource.id + "_" + resourceType + "_" + name; - this.feedSource = feedSource; + this.feedSourceId = feedSource.id; this.resourceType = resourceType; this.name = name; this.value = value; @@ -31,11 +31,13 @@ public ExternalFeedSourceProperty(FeedSource feedSource, String resourceType, St @JsonProperty public String getFeedSourceId() { - return feedSource.id; + return feedSource != null ? feedSource.id : feedSourceId; } public String resourceType; + private String feedSourceId; + public String name; public String value; From 849b5aa53c2710ff3891719447c61f3c6e9e9147 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 15:14:50 -0400 Subject: [PATCH 100/323] fix units for project scheduler --- .../controllers/api/ProjectController.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 3dd8fdeab..9ba1d5e84 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -625,8 +626,8 @@ public static Project thirdPartySync(Request req, Response res) throws Exception halt(404); return null; } - public static ScheduledFuture scheduleAutoFeedFetch (String id, int hour, int minute, int interval, String timezoneId){ - TimeUnit unit = TimeUnit.DAYS; + public static ScheduledFuture scheduleAutoFeedFetch (String id, int hour, int minute, int intervalInDays, String timezoneId){ + TimeUnit minutes = TimeUnit.MINUTES; try { // First cancel any already scheduled auto fetch task for this project id. cancelAutoFetch(id); @@ -645,30 +646,31 @@ public static ScheduledFuture scheduleAutoFeedFetch (String id, int hour, int mi } LOG.info("Scheduling autofetch for projectID: {}", p.id); - long initialDelay = 0; + long delayInMinutes = 0; // NOW in default timezone - ZonedDateTime now = LocalDateTime.now().atZone(timezone); + ZonedDateTime now = ZonedDateTime.ofInstant(Instant.now(), timezone); // SCHEDULED START TIME ZonedDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(hour, minute)).atZone(timezone); + LOG.info("Now: {}", now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); LOG.info("Scheduled start time: {}", startTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)); // Get diff between start time and current time long diffInMinutes = (startTime.toEpochSecond() - now.toEpochSecond()) / 60; if ( diffInMinutes >= 0 ){ - initialDelay = diffInMinutes; // delay in minutes + delayInMinutes = diffInMinutes; // delay in minutes } else{ - initialDelay = 24 * 60 + diffInMinutes; // wait for one day plus difference (which is negative) + delayInMinutes = 24 * 60 + diffInMinutes; // wait for one day plus difference (which is negative) } - LOG.info("Auto fetch begins in {} hours and runs every {} hours", String.valueOf(initialDelay / 60.0), unit.toHours(interval)); + LOG.info("Auto fetch begins in {} hours and runs every {} hours", String.valueOf(delayInMinutes / 60.0), TimeUnit.DAYS.toHours(intervalInDays)); FetchProjectFeedsJob fetchProjectFeedsJob = new FetchProjectFeedsJob(p, null); - return DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, initialDelay, interval, unit); + return DataManager.scheduler.scheduleAtFixedRate(fetchProjectFeedsJob, delayInMinutes, TimeUnit.DAYS.toMinutes(intervalInDays), minutes); } catch (Exception e) { e.printStackTrace(); return null; From ba6614b0327686611d18abfc75c11d0650652450 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 15:37:47 -0400 Subject: [PATCH 101/323] fix enterprise hidden features --- src/main/client/public/components/UserAccount.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 9fbd9333e..214b12b92 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -62,7 +62,7 @@ export default class UserAccount extends Component { }, { id: 'account', - hidden: getConfigProperty('modules.enterprise.enabled') + hidden: !getConfigProperty('modules.enterprise.enabled') }, { id: 'organizations' @@ -145,7 +145,7 @@ export default class UserAccount extends Component { }, { id: 'billing', - hidden: getConfigProperty('modules.enterprise.enabled') + hidden: !getConfigProperty('modules.enterprise.enabled') } ] const activeSection = accountSections.find(section => section.id === this.props.activeComponent) From 6095b495bd3a897fc75d2e754d5e107e0724c8dc Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 15:38:48 -0400 Subject: [PATCH 102/323] hide org tab --- src/main/client/public/components/UserAccount.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 214b12b92..26396d58d 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -65,7 +65,8 @@ export default class UserAccount extends Component { hidden: !getConfigProperty('modules.enterprise.enabled') }, { - id: 'organizations' + id: 'organizations', + hidden: !getConfigProperty('modules.enterprise.enabled') }, { id: 'notifications', From 0ca3f1c3790b61bd36d7b32d18f3b5186f772bb2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 31 Oct 2016 15:41:53 -0400 Subject: [PATCH 103/323] hide org/regions tabs for now --- src/main/client/admin/components/UserAdmin.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index b595d417a..031158b06 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -11,7 +11,7 @@ import { getComponentMessages, getMessage } from '../../common/util/config' export default class UserAdmin extends Component { static propTypes = { user: PropTypes.user, - + onComponentMount: PropTypes.func, setUserPermission: PropTypes.func, createUser: PropTypes.func, @@ -64,8 +64,8 @@ export default class UserAdmin extends Component { User management Application logs - Organizations - Regions + {/*Organizations + Regions*/} From ce4d2bf1e733e29f37d2fb358960e848333798ab Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 1 Nov 2016 10:43:51 -0400 Subject: [PATCH 104/323] rename agencyExists -> feedExists, refactor editor_mapdb config --- .../editor/controllers/api/CalendarController.java | 4 ++-- .../editor/controllers/api/FareController.java | 11 ++--------- .../controllers/api/ScheduleExceptionController.java | 4 ++-- .../editor/controllers/api/StopController.java | 8 ++------ .../editor/controllers/api/TripController.java | 4 ++-- .../editor/controllers/api/TripPatternController.java | 4 ++-- .../editor/datastore/VersionedDataStore.java | 6 +++--- 7 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java index f2ef24c93..be8f7eb12 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java @@ -107,7 +107,7 @@ public static Object createCalendar(Request req, Response res) { try { cal = Base.mapper.readValue(req.body(), ServiceCalendar.class); - if (!VersionedDataStore.agencyExists(cal.feedId)) { + if (!VersionedDataStore.feedExists(cal.feedId)) { halt(400); } @@ -147,7 +147,7 @@ public static Object updateCalendar(Request req, Response res) { try { cal = Base.mapper.readValue(req.body(), ServiceCalendar.class); - if (!VersionedDataStore.agencyExists(cal.feedId)) { + if (!VersionedDataStore.feedExists(cal.feedId)) { halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java index 939d0762a..a9db61d13 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java @@ -4,20 +4,13 @@ import com.conveyal.datatools.editor.datastore.FeedTx; import com.conveyal.datatools.editor.datastore.VersionedDataStore; import com.conveyal.datatools.editor.models.transit.Fare; -import com.conveyal.datatools.editor.models.transit.ScheduleException; -import com.conveyal.datatools.editor.models.transit.Trip; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.collect.Sets; -import org.mapdb.Fun; import spark.Request; import spark.Response; import java.util.Calendar; import java.util.Collection; -import java.util.Set; import static spark.Spark.*; import static spark.Spark.delete; @@ -79,7 +72,7 @@ public static Object createFare(Request req, Response res) { try { fare = Base.mapper.readValue(req.body(), Fare.class); - if (!VersionedDataStore.agencyExists(fare.feedId)) { + if (!VersionedDataStore.feedExists(fare.feedId)) { halt(400); } @@ -119,7 +112,7 @@ public static Object updateFare(Request req, Response res) { try { fare = Base.mapper.readValue(req.body(), Fare.class); - if (!VersionedDataStore.agencyExists(fare.feedId)) { + if (!VersionedDataStore.feedExists(fare.feedId)) { halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java index 7f2f59706..d918b5df5 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java @@ -57,7 +57,7 @@ public static Object createScheduleException (Request req, Response res) { try { ScheduleException ex = Base.mapper.readValue(req.body(), ScheduleException.class); - if (!VersionedDataStore.agencyExists(ex.feedId)) { + if (!VersionedDataStore.feedExists(ex.feedId)) { halt(400); } @@ -125,7 +125,7 @@ public static Object updateScheduleException (Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(ex.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(ex.feedId)) { + if (!VersionedDataStore.feedExists(ex.feedId)) { halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java index 7ba2490fe..b651986ce 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java @@ -3,9 +3,6 @@ import com.conveyal.datatools.editor.datastore.FeedTx; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.core.JsonParseException; -import com.fasterxml.jackson.databind.JsonMappingException; import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.conveyal.datatools.editor.controllers.Base; @@ -19,7 +16,6 @@ import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.stream.Collector; import java.util.stream.Collectors; import org.mapdb.BTreeMap; @@ -142,7 +138,7 @@ public static Object createStop(Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(stop.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(stop.feedId)) { + if (!VersionedDataStore.feedExists(stop.feedId)) { halt(400, "Stop must reference feed source ID"); } @@ -177,7 +173,7 @@ public static Object updateStop(Request req, Response res) throws IOException { halt(400, "Must provide feed ID"); } - if (!VersionedDataStore.agencyExists(feedId)) { + if (!VersionedDataStore.feedExists(feedId)) { halt(400, "Feed ID ("+feedId+") does not exist"); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java index 710191ae1..e8f151daf 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java @@ -83,7 +83,7 @@ public static Object createTrip(Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(trip.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(trip.feedId)) { + if (!VersionedDataStore.feedExists(trip.feedId)) { halt(400); } @@ -120,7 +120,7 @@ public static Object updateTrip(Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(trip.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(trip.feedId)) { + if (!VersionedDataStore.feedExists(trip.feedId)) { halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 42f713830..3f4493b46 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -92,7 +92,7 @@ public static Object createTripPattern(Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(tripPattern.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(tripPattern.feedId)) { + if (!VersionedDataStore.feedExists(tripPattern.feedId)) { halt(400); } @@ -132,7 +132,7 @@ public static Object updateTripPattern(Request req, Response res) { if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(tripPattern.feedId)) halt(400); - if (!VersionedDataStore.agencyExists(tripPattern.feedId)) { + if (!VersionedDataStore.feedExists(tripPattern.feedId)) { halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java index ec6b4217c..e40dd10dd 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java @@ -26,7 +26,7 @@ */ public class VersionedDataStore { public static final Logger LOG = LoggerFactory.getLogger(VersionedDataStore.class); - private static File dataDirectory = new File((String) DataManager.config.get("application").get("data").get("editor_mapdb").asText()); + private static File dataDirectory = new File(DataManager.getConfigPropertyAsText("application.data.editor_mapdb")); private static TxMaker globalTxMaker; private static Map feedTxMakers = Maps.newConcurrentMap(); @@ -183,8 +183,8 @@ public static File getSnapshotDir (String feedId, int version) { return new File(snapshotsDir, "" + version); } - /** Convenience function to check if an agency exists */ - public static boolean agencyExists (String feedId) { + /** Convenience function to check if a feed exists */ + public static boolean feedExists(String feedId) { GlobalTx tx = getGlobalTx(); boolean exists = tx.feeds.containsKey(feedId); tx.rollback(); From 4f10ddbdb5950c05f9cc6dafcd509c9ee3cbf5dd Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 1 Nov 2016 10:54:21 -0400 Subject: [PATCH 105/323] throw halt exceptions --- .../editor/controllers/api/AgencyController.java | 9 +++++++++ .../controllers/api/CalendarController.java | 7 +++++++ .../editor/controllers/api/FareController.java | 8 +++++++- .../controllers/api/FeedInfoController.java | 5 +++++ .../editor/controllers/api/RouteController.java | 16 ++++++++++++++-- .../api/ScheduleExceptionController.java | 7 +++++++ .../editor/controllers/api/StopController.java | 9 ++++++++- .../editor/controllers/api/TripController.java | 7 +++++++ .../controllers/api/TripPatternController.java | 7 +++++++ 9 files changed, 71 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java index 10049a367..3b1f7ce05 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java @@ -8,6 +8,7 @@ import com.conveyal.datatools.editor.utils.S3Utils; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import spark.HaltException; import spark.Request; import spark.Response; @@ -35,6 +36,8 @@ public static Object getAgency(Request req, Response res) { } tx.rollback(); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -61,6 +64,8 @@ public static Object createAgency(Request req, Response res) { tx.commit(); return agency; + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -87,6 +92,8 @@ public static Object updateAgency(Request req, Response res) { tx.commit(); return Base.toJson(agency, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -121,6 +128,8 @@ public static Object uploadAgencyBranding(Request req, Response res) { tx.commit(); return agency; + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java index be8f7eb12..64b2d0e68 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java @@ -13,6 +13,7 @@ import com.conveyal.datatools.editor.models.transit.ServiceCalendar.ServiceCalendarForPattern; import com.conveyal.datatools.editor.models.transit.Trip; import org.mapdb.Fun; +import spark.HaltException; import spark.Request; import spark.Response; @@ -92,6 +93,8 @@ public ServiceCalendarForPattern apply(String input) { } tx.rollback(); + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollback(); e.printStackTrace(); @@ -132,6 +135,8 @@ public static Object createCalendar(Request req, Response res) { tx.commit(); return cal; + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); @@ -175,6 +180,8 @@ public static Object updateCalendar(Request req, Response res) { tx.commit(); return json; + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java index a9db61d13..0cdf84973 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java @@ -6,6 +6,7 @@ import com.conveyal.datatools.editor.models.transit.Fare; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import spark.HaltException; import spark.Request; import spark.Response; @@ -57,6 +58,8 @@ public static Object getFare(Request req, Response res) { } tx.rollback(); + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollback(); e.printStackTrace(); @@ -97,10 +100,11 @@ public static Object createFare(Request req, Response res) { tx.commit(); return fare; + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); - halt(400); } return null; } @@ -140,6 +144,8 @@ public static Object updateFare(Request req, Response res) { tx.commit(); return json; + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java index 278535845..7ed93b773 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java @@ -11,6 +11,7 @@ import com.conveyal.gtfs.model.FeedInfo; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import spark.HaltException; import spark.Request; import spark.Response; @@ -67,6 +68,8 @@ public static Object createFeedInfo(Request req, Response res) { gtx.commit(); return Base.toJson(fs, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { gtx.rollbackIfOpen(); e.printStackTrace(); @@ -95,6 +98,8 @@ public static Object updateFeedInfo(Request req, Response res) throws IOExceptio gtx.commit(); return Base.toJson(feed, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java index eccac3398..7456e492a 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java @@ -20,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spark.HaltException; import spark.Request; import spark.Response; @@ -69,6 +70,8 @@ public static Object getRoute(Request req, Response res) { tx.rollback(); // return json; } + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollbackIfOpen(); e.printStackTrace(); @@ -112,6 +115,8 @@ public static Object createRoute(Request req, Response res) { tx.commit(); return route; + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -147,6 +152,8 @@ public static Object updateRoute(Request req, Response res) { tx.commit(); return route; + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -181,6 +188,8 @@ public static Object uploadRouteBranding(Request req, Response res) { tx.commit(); return route; + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -226,6 +235,8 @@ public static Object deleteRoute(Request req, Response res) { tx.routes.remove(id); tx.commit(); return true; // ok(); + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollback(); e.printStackTrace(); @@ -302,8 +313,9 @@ public Trip apply(Tuple2 input) { tx.commit(); return true; // ok(); - } - catch (Exception e) { + } catch (HaltException e) { + throw e; + } catch (Exception e) { e.printStackTrace(); tx.rollback(); throw e; diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java index d918b5df5..27dac7c57 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java @@ -8,6 +8,7 @@ import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import spark.HaltException; import spark.Request; import spark.Response; @@ -44,6 +45,8 @@ public static Object getScheduleException (Request req, Response res) { json = Base.toJson(tx.exceptions.values(), false); } tx.rollback(); + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); @@ -109,6 +112,8 @@ public static Object createScheduleException (Request req, Response res) { tx.commit(); return Base.toJson(ex, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); @@ -166,6 +171,8 @@ public static Object updateScheduleException (Request req, Response res) { tx.commit(); return ex; + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java index b651986ce..9cd0a465c 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java @@ -19,6 +19,7 @@ import java.util.stream.Collectors; import org.mapdb.BTreeMap; +import spark.HaltException; import spark.Request; import spark.Response; @@ -121,6 +122,8 @@ public Stop apply(TripPatternStop input) { } + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -154,6 +157,8 @@ public static Object createStop(Request req, Response res) { } catch (IOException e) { e.printStackTrace(); halt(400); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); throw e; @@ -276,7 +281,9 @@ public static Object findDuplicateStops(Request req, Response res) { } json = Base.toJson(ret, false); - } catch (Exception e) { + } catch (HaltException e) { + throw e; + } catch (Exception e) { e.printStackTrace(); halt(400); } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java index e8f151daf..a4555437b 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java @@ -14,6 +14,7 @@ import java.io.IOException; +import spark.HaltException; import spark.Request; import spark.Response; @@ -66,6 +67,8 @@ else if(patternId != null) { return Base.toJson(tx.trips.values(), false); } + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollback(); e.printStackTrace(); @@ -103,6 +106,8 @@ public static Object createTrip(Request req, Response res) { tx.commit(); return Base.toJson(trip, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); if (tx != null) tx.rollbackIfOpen(); @@ -159,6 +164,8 @@ public static Object updateTrip(Request req, Response res) { tx.commit(); return Base.toJson(trip, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollbackIfOpen(); e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 3f4493b46..c6d63f747 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -17,6 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spark.HaltException; import spark.Request; import spark.Response; @@ -75,6 +76,8 @@ public TripPattern apply(Tuple2 input) { tx.rollback(); + } catch (HaltException e) { + throw e; } catch (Exception e) { tx.rollback(); e.printStackTrace(); @@ -109,6 +112,8 @@ public static Object createTripPattern(Request req, Response res) { tx.commit(); return Base.toJson(tripPattern, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { e.printStackTrace(); halt(400); @@ -164,6 +169,8 @@ public static Object updateTripPattern(Request req, Response res) { tx.commit(); return Base.toJson(tripPattern, false); + } catch (HaltException e) { + throw e; } catch (Exception e) { if (tx != null) tx.rollback(); e.printStackTrace(); From f85790d732df9d18f7d26c4dcd3b5cbc2c31a1ab Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 1 Nov 2016 11:36:35 -0400 Subject: [PATCH 106/323] log halts --- .../editor/controllers/api/AgencyController.java | 7 +++++++ .../editor/controllers/api/CalendarController.java | 6 ++++++ .../editor/controllers/api/FareController.java | 14 ++++++-------- .../editor/controllers/api/FeedInfoController.java | 6 +++++- .../editor/controllers/api/RouteController.java | 8 +++++++- .../api/ScheduleExceptionController.java | 7 +++++++ .../editor/controllers/api/SnapshotController.java | 2 -- .../editor/controllers/api/StopController.java | 8 ++++++-- .../editor/controllers/api/TripController.java | 7 ++++--- .../controllers/api/TripPatternController.java | 3 +++ 10 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java index 3b1f7ce05..462b6210d 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/AgencyController.java @@ -8,6 +8,8 @@ import com.conveyal.datatools.editor.utils.S3Utils; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -17,6 +19,7 @@ public class AgencyController { public static JsonManager json = new JsonManager<>(Agency.class, JsonViews.UserInterface.class); + private static Logger LOG = LoggerFactory.getLogger(AgencyController.class); public static Object getAgency(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); @@ -37,6 +40,7 @@ public static Object getAgency(Request req, Response res) { tx.rollback(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -65,6 +69,7 @@ public static Object createAgency(Request req, Response res) { return agency; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -93,6 +98,7 @@ public static Object updateAgency(Request req, Response res) { return Base.toJson(agency, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -129,6 +135,7 @@ public static Object uploadAgencyBranding(Request req, Response res) { return agency; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java index 64b2d0e68..c594c9267 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java @@ -13,6 +13,8 @@ import com.conveyal.datatools.editor.models.transit.ServiceCalendar.ServiceCalendarForPattern; import com.conveyal.datatools.editor.models.transit.Trip; import org.mapdb.Fun; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -27,6 +29,7 @@ public class CalendarController { public static JsonManager json = new JsonManager<>(Calendar.class, JsonViews.UserInterface.class); + private static final Logger LOG = LoggerFactory.getLogger(CalendarController.class); public static Object getCalendar(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); @@ -94,6 +97,7 @@ public ServiceCalendarForPattern apply(String input) { tx.rollback(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollback(); @@ -136,6 +140,7 @@ public static Object createCalendar(Request req, Response res) { return cal; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); @@ -181,6 +186,7 @@ public static Object updateCalendar(Request req, Response res) { return json; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java index 0cdf84973..20ae7a8f1 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FareController.java @@ -6,6 +6,8 @@ import com.conveyal.datatools.editor.models.transit.Fare; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -22,6 +24,7 @@ public class FareController { public static JsonManager json = new JsonManager<>(Calendar.class, JsonViews.UserInterface.class); + private static final Logger LOG = LoggerFactory.getLogger(FareController.class); public static Object getFare(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); @@ -59,6 +62,7 @@ public static Object getFare(Request req, Response res) { tx.rollback(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollback(); @@ -101,6 +105,7 @@ public static Object createFare(Request req, Response res) { return fare; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); @@ -145,6 +150,7 @@ public static Object updateFare(Request req, Response res) { return json; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); @@ -165,14 +171,6 @@ public static Object deleteFare(Request req, Response res) { halt(404); } -// // we just don't let you delete calendars unless there are no trips on them -// Long count = tx.tripCountByCalendar.get(id); -// if (count != null && count > 0) { -// tx.rollback(); -// halt(400); -// } - - tx.fares.remove(id); tx.commit(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java index 7ed93b773..fdda2be5a 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java @@ -11,6 +11,8 @@ import com.conveyal.gtfs.model.FeedInfo; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -32,7 +34,7 @@ public class FeedInfoController { public static JsonManager json = new JsonManager<>(EditorFeed.class, JsonViews.UserInterface.class); - + private static final Logger LOG = LoggerFactory.getLogger(FeedInfoController.class); public static Object getFeedInfo(Request req, Response res) { String id = req.params("id"); @@ -69,6 +71,7 @@ public static Object createFeedInfo(Request req, Response res) { return Base.toJson(fs, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { gtx.rollbackIfOpen(); @@ -99,6 +102,7 @@ public static Object updateFeedInfo(Request req, Response res) throws IOExceptio return Base.toJson(feed, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java index 7456e492a..9c19e55d3 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java @@ -30,7 +30,7 @@ public class RouteController { public static JsonManager json = new JsonManager<>(Route.class, JsonViews.UserInterface.class); - private static Logger LOG = LoggerFactory.getLogger(Route.class); + private static final Logger LOG = LoggerFactory.getLogger(RouteController.class); public static Object getRoute(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); @@ -71,6 +71,7 @@ public static Object getRoute(Request req, Response res) { // return json; } } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollbackIfOpen(); @@ -116,6 +117,7 @@ public static Object createRoute(Request req, Response res) { return route; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -153,6 +155,7 @@ public static Object updateRoute(Request req, Response res) { return route; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -189,6 +192,7 @@ public static Object uploadRouteBranding(Request req, Response res) { return route; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -236,6 +240,7 @@ public static Object deleteRoute(Request req, Response res) { tx.commit(); return true; // ok(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollback(); @@ -314,6 +319,7 @@ public Trip apply(Tuple2 input) { tx.commit(); return true; // ok(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java index 27dac7c57..1e7b242e1 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/ScheduleExceptionController.java @@ -8,6 +8,8 @@ import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -18,6 +20,8 @@ public class ScheduleExceptionController { public static JsonManager json = new JsonManager<>(ScheduleException.class, JsonViews.UserInterface.class); + private static final Logger LOG = LoggerFactory.getLogger(ScheduleExceptionController.class); + /** Get all of the schedule exceptions for an agency */ public static Object getScheduleException (Request req, Response res) { String exceptionId = req.params("exceptionId"); @@ -46,6 +50,7 @@ public static Object getScheduleException (Request req, Response res) { } tx.rollback(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); @@ -113,6 +118,7 @@ public static Object createScheduleException (Request req, Response res) { return Base.toJson(ex, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); @@ -172,6 +178,7 @@ public static Object updateScheduleException (Request req, Response res) { return ex; } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java index 27c1211f2..51175abcc 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java @@ -36,8 +36,6 @@ public class SnapshotController { public static JsonManager json = new JsonManager<>(Snapshot.class, JsonViews.UserInterface.class); - - public static Object getSnapshot(Request req, Response res) throws IOException { String id = req.params("id"); String feedId= req.queryParams("feedId"); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java index 9cd0a465c..1e8334fb9 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/StopController.java @@ -19,6 +19,8 @@ import java.util.stream.Collectors; import org.mapdb.BTreeMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import spark.HaltException; import spark.Request; import spark.Response; @@ -30,7 +32,7 @@ public class StopController { public static JsonManager json = new JsonManager<>(Stop.class, JsonViews.UserInterface.class); - + private static final Logger LOG = LoggerFactory.getLogger(StopController.class); public static Object getStop(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); @@ -123,6 +125,7 @@ public Stop apply(TripPatternStop input) { } } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -158,10 +161,10 @@ public static Object createStop(Request req, Response res) { e.printStackTrace(); halt(400); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); - throw e; } finally { if (tx != null) tx.rollbackIfOpen(); } @@ -282,6 +285,7 @@ public static Object findDuplicateStops(Request req, Response res) { json = Base.toJson(ret, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java index a4555437b..6a79dfb1e 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java @@ -27,9 +27,7 @@ public class TripController { public static JsonManager json = new JsonManager<>(Trip.class, JsonViews.UserInterface.class); - public static Object getTrip(Request req, Response res -// String id, String patternId, String calendarId, String feedId - ) { + public static Object getTrip(Request req, Response res) { String id = req.params("id"); String feedId = req.queryParams("feedId"); String patternId = req.queryParams("patternId"); @@ -68,6 +66,7 @@ else if(patternId != null) { } } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollback(); @@ -107,6 +106,7 @@ public static Object createTrip(Request req, Response res) { return Base.toJson(trip, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -165,6 +165,7 @@ public static Object updateTrip(Request req, Response res) { return Base.toJson(trip, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollbackIfOpen(); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index c6d63f747..3282135ea 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -77,6 +77,7 @@ public TripPattern apply(Tuple2 input) { tx.rollback(); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { tx.rollback(); @@ -113,6 +114,7 @@ public static Object createTripPattern(Request req, Response res) { return Base.toJson(tripPattern, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { e.printStackTrace(); @@ -170,6 +172,7 @@ public static Object updateTripPattern(Request req, Response res) { return Base.toJson(tripPattern, false); } catch (HaltException e) { + LOG.error("Halt encountered", e); throw e; } catch (Exception e) { if (tx != null) tx.rollback(); From 6a5a125f36e498b23432090a2e73015236a01520 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 2 Nov 2016 12:35:09 +0700 Subject: [PATCH 107/323] build(yarn): Add yarn, fix install warnings, and remove npm-shrinkwrap. --- npm-shrinkwrap.json | 14 - package.json | 4 +- yarn.lock | 5669 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 5670 insertions(+), 17 deletions(-) delete mode 100644 npm-shrinkwrap.json create mode 100644 yarn.lock diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json deleted file mode 100644 index c3cb8ec64..000000000 --- a/npm-shrinkwrap.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "dependencies": { - "react-color": { - "version": "^2.2.2", - "from": "react-color@^2.2.2", - "dependencies": { - "react": { - "version": "^15.2.1", - "from": "react@^0.14.0" - } - } - } - } -} diff --git a/package.json b/package.json index 59dea609b..2ad981781 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,6 @@ }, "dependencies": { "array-unique": "^0.2.1", - "babel-loader": "^6.2.4", "babel-plugin-transform-regenerator": "^6.9.0", "body-parser": "^1.14.2", "bootstrap": "^3.3.7", @@ -54,7 +53,7 @@ "react-color": "^2.3.4", "react-dnd": "^2.1.4", "react-dnd-html5-backend": "^2.1.2", - "react-dom": "^0.14.8", + "react-dom": "^15.2.1", "react-dropzone": "^3.5.3", "react-fa": "^4.1.2", "react-helmet": "^3.1.0", @@ -76,7 +75,6 @@ "request": "^2.67.0", "selectn": "^1.0.20", "shpjs": "^3.3.2", - "style-loader": "^0.13.1", "truncate": "^2.0.0", "turf-along": "^3.0.10", "turf-bearing": "^3.0.10", diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..dc88a8b32 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,5669 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 +abbrev@1: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +accepts@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" + dependencies: + mime-types "~2.1.11" + negotiator "0.6.1" + +acorn-globals@^1.0.4: + version "1.0.9" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" + dependencies: + acorn "^2.1.0" + +acorn-to-esprima@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/acorn-to-esprima/-/acorn-to-esprima-1.0.7.tgz#9436259760098f9ead9b9da2242fab2f4850281b" + +acorn@^2.1.0, acorn@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" + +acorn@^3.0.0, acorn@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +add-dom-event-listener@1.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.1.tgz#70e74d0692b27108f9740554e0fa80a4683c2eb2" + dependencies: + object-assign "4.x" + +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" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +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" + +alter@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" + dependencies: + stable "~0.1.3" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-html@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.6.tgz#bda8e33dd2ee1c20f54c08eb405713cbfc0ed80e" + +ansi-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +array.filter@^0.1.0: + version "0.1.5" + resolved "https://registry.yarnpkg.com/array.filter/-/array.filter-0.1.5.tgz#7ef07c37f472223f3e5c4b73be637face765bcd7" + dependencies: + curry2 "^1.0.1" + selectn "^1.0.5" + +arraymap@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/arraymap/-/arraymap-0.1.2.tgz#2bb1d4f5706d1ccf4fd83896225e92ef5fe41a51" + dependencies: + curry2 "^0.1.0" + selectn "^0.10.0" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asap@^2.0.3, asap@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assertion-error@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" + +ast-traverse@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" + +ast-types@0.8.12: + version "0.8.12" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" + +ast-types@0.8.15: + version "0.8.15" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@^0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + +async@^1.2.1, async@^1.3.0, async@^1.5.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +attr-accept@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.0.tgz#b5cd35227f163935a8f1de10ed3eba16941f6be6" + +autoprefixer@^6.0.3, autoprefixer@^6.3.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.5.1.tgz#ae759a5221e709f3da17c2d656230e67c43cbb75" + dependencies: + browserslist "~1.4.0" + caniuse-db "^1.0.30000554" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.4" + postcss-value-parser "^3.2.3" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" + +babel-cli@^6.4.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.18.0.tgz#92117f341add9dead90f6fa7d0a97c0cc08ec186" + dependencies: + babel-core "^6.18.0" + babel-polyfill "^6.16.0" + babel-register "^6.18.0" + babel-runtime "^6.9.0" + commander "^2.8.1" + convert-source-map "^1.1.0" + fs-readdir-recursive "^1.0.0" + glob "^5.0.5" + lodash "^4.2.0" + output-file-sync "^1.1.0" + path-is-absolute "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + v8flags "^2.0.10" + optionalDependencies: + chokidar "^1.0.0" + +babel-code-frame@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^2.0.0" + +babel-core@^5.8.33: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558" + dependencies: + babel-plugin-constant-folding "^1.0.1" + babel-plugin-dead-code-elimination "^1.0.2" + babel-plugin-eval "^1.0.1" + babel-plugin-inline-environment-variables "^1.0.1" + babel-plugin-jscript "^1.0.4" + babel-plugin-member-expression-literals "^1.0.1" + babel-plugin-property-literals "^1.0.1" + babel-plugin-proto-to-assign "^1.0.3" + babel-plugin-react-constant-elements "^1.0.3" + babel-plugin-react-display-name "^1.0.3" + babel-plugin-remove-console "^1.0.1" + babel-plugin-remove-debugger "^1.0.1" + babel-plugin-runtime "^1.0.7" + babel-plugin-undeclared-variables-check "^1.0.2" + babel-plugin-undefined-to-void "^1.1.6" + babylon "^5.8.38" + bluebird "^2.9.33" + chalk "^1.0.0" + convert-source-map "^1.1.0" + core-js "^1.0.0" + debug "^2.1.1" + detect-indent "^3.0.0" + esutils "^2.0.0" + fs-readdir-recursive "^0.1.0" + globals "^6.4.0" + home-or-tmp "^1.0.0" + is-integer "^1.0.4" + js-tokens "1.0.1" + json5 "^0.4.0" + lodash "^3.10.0" + minimatch "^2.0.3" + output-file-sync "^1.1.0" + path-exists "^1.0.0" + path-is-absolute "^1.0.0" + private "^0.1.6" + regenerator "0.8.40" + regexpu "^1.3.0" + repeating "^1.1.2" + resolve "^1.1.6" + shebang-regex "^1.0.0" + slash "^1.0.0" + source-map "^0.5.0" + source-map-support "^0.2.10" + to-fast-properties "^1.0.0" + trim-right "^1.0.0" + try-resolve "^1.0.0" + +babel-core@^6.0.0, babel-core@^6.18.0, babel-core@^6.3.26: + version "6.18.2" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.18.2.tgz#d8bb14dd6986fa4f3566a26ceda3964fa0e04e5b" + dependencies: + babel-code-frame "^6.16.0" + babel-generator "^6.18.0" + babel-helpers "^6.16.0" + babel-messages "^6.8.0" + babel-register "^6.18.0" + babel-runtime "^6.9.1" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.11.0" + convert-source-map "^1.1.0" + debug "^2.1.1" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" + path-is-absolute "^1.0.0" + private "^0.1.6" + slash "^1.0.0" + source-map "^0.5.0" + +babel-eslint@^4.1.6: + version "4.1.8" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-4.1.8.tgz#4f79e7a4f5879ecf03f48cb16f552a355fcc31b2" + dependencies: + acorn-to-esprima "^1.0.5" + babel-core "^5.8.33" + lodash.assign "^3.2.0" + lodash.pick "^3.1.0" + +babel-generator@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.18.0.tgz#e4f104cb3063996d9850556a45aae4a022060a07" + dependencies: + babel-messages "^6.8.0" + babel-runtime "^6.9.0" + babel-types "^6.18.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + +babel-helper-bindify-decorators@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.18.0.tgz#fc00c573676a6e702fffa00019580892ec8780a5" + dependencies: + babel-runtime "^6.0.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-builder-binary-assignment-operator-visitor@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.18.0.tgz#8ae814989f7a53682152e3401a04fabd0bb333a6" + dependencies: + babel-helper-explode-assignable-expression "^6.18.0" + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-helper-builder-react-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.18.0.tgz#ab02f19a2eb7ace936dd87fa55896d02be59bf71" + dependencies: + babel-runtime "^6.9.0" + babel-types "^6.18.0" + esutils "^2.0.0" + lodash "^4.2.0" + +babel-helper-call-delegate@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.18.0.tgz#05b14aafa430884b034097ef29e9f067ea4133bd" + dependencies: + babel-helper-hoist-variables "^6.18.0" + babel-runtime "^6.0.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-define-map@^6.18.0, babel-helper-define-map@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.18.0.tgz#8d6c85dc7fbb4c19be3de40474d18e97c3676ec2" + dependencies: + babel-helper-function-name "^6.18.0" + babel-runtime "^6.9.0" + babel-types "^6.18.0" + lodash "^4.2.0" + +babel-helper-explode-assignable-expression@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.18.0.tgz#14b8e8c2d03ad735d4b20f1840b24cd1f65239fe" + dependencies: + babel-runtime "^6.0.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-explode-class@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.18.0.tgz#c44f76f4fa23b9c5d607cbac5d4115e7a76f62cb" + dependencies: + babel-helper-bindify-decorators "^6.18.0" + babel-runtime "^6.0.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-function-name@^6.18.0, babel-helper-function-name@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz#68ec71aeba1f3e28b2a6f0730190b754a9bf30e6" + dependencies: + babel-helper-get-function-arity "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-get-function-arity@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz#a5b19695fd3f9cdfc328398b47dafcd7094f9f24" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-helper-hoist-variables@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.18.0.tgz#a835b5ab8b46d6de9babefae4d98ea41e866b82a" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-helper-optimise-call-expression@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.18.0.tgz#9261d0299ee1a4f08a6dd28b7b7c777348fd8f0f" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-helper-regex@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.18.0.tgz#ae0ebfd77de86cb2f1af258e2cc20b5fe893ecc6" + dependencies: + babel-runtime "^6.9.0" + babel-types "^6.18.0" + lodash "^4.2.0" + +babel-helper-remap-async-to-generator@^6.16.0, babel-helper-remap-async-to-generator@^6.16.2: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.18.0.tgz#336cdf3cab650bb191b02fc16a3708e7be7f9ce5" + dependencies: + babel-helper-function-name "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helper-replace-supers@^6.18.0, babel-helper-replace-supers@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.18.0.tgz#28ec69877be4144dbd64f4cc3a337e89f29a924e" + dependencies: + babel-helper-optimise-call-expression "^6.18.0" + babel-messages "^6.8.0" + babel-runtime "^6.0.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-helpers@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.16.0.tgz#1095ec10d99279460553e67eb3eee9973d3867e3" + dependencies: + babel-runtime "^6.0.0" + babel-template "^6.16.0" + +babel-jest@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-16.0.0.tgz#348729aea6d624a4774b8a934d07a40dd2cfd640" + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^2.0.0" + babel-preset-jest "^16.0.0" + +babel-loader@^6.2.1: + version "6.2.7" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.2.7.tgz#16fdbf64328030dc5a606827d389c8b92a2a8032" + dependencies: + find-cache-dir "^0.1.1" + loader-utils "^0.2.11" + mkdirp "^0.5.1" + object-assign "^4.0.1" + +babel-messages@^6.8.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-check-es2015-constants@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz#dbf024c32ed37bfda8dee1e76da02386a8d26fe7" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-constant-folding@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz#8361d364c98e449c3692bdba51eff0844290aa8e" + +babel-plugin-dead-code-elimination@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65" + +babel-plugin-eval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da" + +babel-plugin-inline-environment-variables@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz#1f58ce91207ad6a826a8bf645fafe68ff5fe3ffe" + +babel-plugin-istanbul@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-2.0.3.tgz#266b304b9109607d60748474394676982f660df4" + dependencies: + find-up "^1.1.2" + istanbul-lib-instrument "^1.1.4" + object-assign "^4.1.0" + test-exclude "^2.1.1" + +babel-plugin-jest-hoist@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-16.0.0.tgz#b58ca3f770982a7e7c25b5614b2e57e9dafc6e76" + +babel-plugin-jscript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz#8f342c38276e87a47d5fa0a8bd3d5eb6ccad8fcc" + +babel-plugin-member-expression-literals@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz#cc5edb0faa8dc927170e74d6d1c02440021624d3" + +babel-plugin-property-literals@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz#0252301900192980b1c118efea48ce93aab83336" + +babel-plugin-proto-to-assign@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz#c49e7afd02f577bc4da05ea2df002250cf7cd123" + dependencies: + lodash "^3.9.3" + +babel-plugin-react-constant-elements@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz#946736e8378429cbc349dcff62f51c143b34e35a" + +babel-plugin-react-display-name@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz#754fe38926e8424a4e7b15ab6ea6139dee0514fc" + +babel-plugin-react-transform@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109" + dependencies: + lodash "^4.6.1" + +babel-plugin-remove-console@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz#d8f24556c3a05005d42aaaafd27787f53ff013a7" + +babel-plugin-remove-debugger@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz#fd2ea3cd61a428ad1f3b9c89882ff4293e8c14c7" + +babel-plugin-runtime@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz#bf7c7d966dd56ecd5c17fa1cb253c9acb7e54aaf" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-async-generators@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + +babel-plugin-syntax-class-constructor-call@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.1.18, babel-plugin-syntax-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + +babel-plugin-syntax-do-expressions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz#5747756139aa26d390d09410b03744ba07e4796d" + +babel-plugin-syntax-dynamic-import@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-export-extensions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" + +babel-plugin-syntax-flow@^6.18.0, babel-plugin-syntax-flow@^6.3.13: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-syntax-function-bind@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46" + +babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.3.13: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz#2b84b7d53dd744f94ff1fad7669406274b23f541" + +babel-plugin-transform-async-generator-functions@^6.17.0: + version "6.17.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.17.0.tgz#d0b5a2b2f0940f2b245fa20a00519ed7bc6cae54" + dependencies: + babel-helper-remap-async-to-generator "^6.16.2" + babel-plugin-syntax-async-generators "^6.5.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-async-to-generator@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.16.0.tgz#19ec36cb1486b59f9f468adfa42ce13908ca2999" + dependencies: + babel-helper-remap-async-to-generator "^6.16.0" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-class-constructor-call@^6.3.13: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.18.0.tgz#80855e38a1ab47b8c6c647f8ea1bcd2c00ca3aae" + dependencies: + babel-plugin-syntax-class-constructor-call "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + +babel-plugin-transform-class-properties@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.18.0.tgz#bc1266a39d4c8726e0bd7b15c56235177e6ede57" + dependencies: + babel-helper-function-name "^6.18.0" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.9.1" + +babel-plugin-transform-decorators-legacy@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925" + dependencies: + babel-plugin-syntax-decorators "^6.1.18" + babel-runtime "^6.2.0" + babel-template "^6.3.0" + +babel-plugin-transform-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.13.0.tgz#82d65c1470ae83e2d13eebecb0a1c2476d62da9d" + dependencies: + babel-helper-define-map "^6.8.0" + babel-helper-explode-class "^6.8.0" + babel-plugin-syntax-decorators "^6.13.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + babel-types "^6.13.0" + +babel-plugin-transform-do-expressions@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.8.0.tgz#fda692af339835cc255bb7544efb8f7c1306c273" + dependencies: + babel-plugin-syntax-do-expressions "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-arrow-functions@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.8.0.tgz#5b63afc3181bdc9a8c4d481b5a4f3f7d7fef3d9d" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.8.0.tgz#ed95d629c4b5a71ae29682b998f70d9833eb366d" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-block-scoping@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.18.0.tgz#3bfdcfec318d46df22525cdea88f1978813653af" + dependencies: + babel-runtime "^6.9.0" + babel-template "^6.15.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + lodash "^4.2.0" + +babel-plugin-transform-es2015-classes@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.18.0.tgz#ffe7a17321bf83e494dcda0ae3fc72df48ffd1d9" + dependencies: + babel-helper-define-map "^6.18.0" + babel-helper-function-name "^6.18.0" + babel-helper-optimise-call-expression "^6.18.0" + babel-helper-replace-supers "^6.18.0" + babel-messages "^6.8.0" + babel-runtime "^6.9.0" + babel-template "^6.14.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-plugin-transform-es2015-computed-properties@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.8.0.tgz#f51010fd61b3bd7b6b60a5fdfd307bb7a5279870" + dependencies: + babel-helper-define-map "^6.8.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + +babel-plugin-transform-es2015-destructuring@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.18.0.tgz#a08fb89415ab82058649558bedb7bf8dafa76ba5" + dependencies: + babel-runtime "^6.9.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.6.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.8.0.tgz#fd8f7f7171fc108cc1c70c3164b9f15a81c25f7d" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.8.0" + +babel-plugin-transform-es2015-for-of@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.18.0.tgz#4c517504db64bf8cfc119a6b8f177211f2028a70" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-function-name@^6.9.0: + version "6.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.9.0.tgz#8c135b17dbd064e5bba56ec511baaee2fca82719" + dependencies: + babel-helper-function-name "^6.8.0" + babel-runtime "^6.9.0" + babel-types "^6.9.0" + +babel-plugin-transform-es2015-literals@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.8.0.tgz#50aa2e5c7958fc2ab25d74ec117e0cc98f046468" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-modules-amd@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.18.0.tgz#49a054cbb762bdf9ae2d8a807076cfade6141e40" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + +babel-plugin-transform-es2015-modules-commonjs@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz#c15ae5bb11b32a0abdcc98a5837baa4ee8d67bcc" + dependencies: + babel-plugin-transform-strict-mode "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.16.0" + babel-types "^6.18.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.18.0.tgz#f09294707163edae4d3b3e8bfacecd01d920b7ad" + dependencies: + babel-helper-hoist-variables "^6.18.0" + babel-runtime "^6.11.6" + babel-template "^6.14.0" + +babel-plugin-transform-es2015-modules-umd@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.18.0.tgz#23351770ece5c1f8e83ed67cb1d7992884491e50" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.18.0" + babel-runtime "^6.0.0" + babel-template "^6.8.0" + +babel-plugin-transform-es2015-object-super@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.8.0.tgz#1b858740a5a4400887c23dcff6f4d56eea4a24c5" + dependencies: + babel-helper-replace-supers "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-parameters@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.18.0.tgz#9b2cfe238c549f1635ba27fc1daa858be70608b1" + dependencies: + babel-helper-call-delegate "^6.18.0" + babel-helper-get-function-arity "^6.18.0" + babel-runtime "^6.9.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + +babel-plugin-transform-es2015-shorthand-properties@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.18.0.tgz#e2ede3b7df47bf980151926534d1dd0cbea58f43" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-plugin-transform-es2015-spread@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.8.0.tgz#0217f737e3b821fa5a669f187c6ed59205f05e9c" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-sticky-regex@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.8.0.tgz#e73d300a440a35d5c64f5c2a344dc236e3df47be" + dependencies: + babel-helper-regex "^6.8.0" + babel-runtime "^6.0.0" + babel-types "^6.8.0" + +babel-plugin-transform-es2015-template-literals@^6.6.0: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.8.0.tgz#86eb876d0a2c635da4ec048b4f7de9dfc897e66b" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.18.0.tgz#0b14c48629c90ff47a0650077f6aa699bee35798" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-es2015-unicode-regex@^6.3.13: + version "6.11.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.11.0.tgz#6298ceabaad88d50a3f4f392d8de997260f6ef2c" + dependencies: + babel-helper-regex "^6.8.0" + babel-runtime "^6.0.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.8.0.tgz#db25742e9339eade676ca9acec46f955599a68a4" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.8.0" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-export-extensions@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.8.0.tgz#fa80ff655b636549431bfd38f6b817bd82e47f5b" + dependencies: + babel-plugin-syntax-export-extensions "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-flow-strip-types@^6.3.13: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.18.0.tgz#4d3e642158661e9b40db457c004a30817fa32592" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-function-bind@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.8.0.tgz#e7f334ce69f50d28fe850a822eaaab9fa4f4d821" + dependencies: + babel-plugin-syntax-function-bind "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-object-rest-spread@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.16.0.tgz#db441d56fffc1999052fdebe2e2f25ebd28e36a9" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-react-display-name@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.8.0.tgz#f7a084977383d728bdbdc2835bba0159577f660e" + dependencies: + babel-runtime "^6.0.0" + +babel-plugin-transform-react-jsx-self@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.11.0.tgz#605c9450c1429f97a930f7e1dfe3f0d9d0dbd0f4" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.9.0" + +babel-plugin-transform-react-jsx-source@^6.3.13: + version "6.9.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.9.0.tgz#af684a05c2067a86e0957d4f343295ccf5dccf00" + dependencies: + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.9.0" + +babel-plugin-transform-react-jsx@^6.3.13: + version "6.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.8.0.tgz#94759942f70af18c617189aa7f3593f1644a71ab" + dependencies: + babel-helper-builder-react-jsx "^6.8.0" + babel-plugin-syntax-jsx "^6.8.0" + babel-runtime "^6.0.0" + +babel-plugin-transform-regenerator@^6.16.0, babel-plugin-transform-regenerator@^6.9.0: + version "6.16.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz#a75de6b048a14154aae14b0122756c5bed392f59" + dependencies: + babel-runtime "^6.9.0" + babel-types "^6.16.0" + private "~0.1.5" + +babel-plugin-transform-runtime@^6.9.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.15.0.tgz#3d75b4d949ad81af157570273846fb59aeb0d57c" + dependencies: + babel-runtime "^6.9.0" + +babel-plugin-transform-strict-mode@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.18.0.tgz#df7cf2991fe046f44163dcd110d5ca43bc652b9d" + dependencies: + babel-runtime "^6.0.0" + babel-types "^6.18.0" + +babel-plugin-undeclared-variables-check@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz#5cf1aa539d813ff64e99641290af620965f65dee" + dependencies: + leven "^1.0.2" + +babel-plugin-undefined-to-void@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" + +babel-polyfill@^6.16.0, babel-polyfill@^6.9.1: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.16.0.tgz#2d45021df87e26a374b6d4d1a9c65964d17f2422" + dependencies: + babel-runtime "^6.9.1" + core-js "^2.4.0" + regenerator-runtime "^0.9.5" + +babel-preset-es2015@^6.3.13: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.18.0.tgz#b8c70df84ec948c43dcf2bf770e988eb7da88312" + dependencies: + babel-plugin-check-es2015-constants "^6.3.13" + babel-plugin-transform-es2015-arrow-functions "^6.3.13" + babel-plugin-transform-es2015-block-scoped-functions "^6.3.13" + babel-plugin-transform-es2015-block-scoping "^6.18.0" + babel-plugin-transform-es2015-classes "^6.18.0" + babel-plugin-transform-es2015-computed-properties "^6.3.13" + babel-plugin-transform-es2015-destructuring "^6.18.0" + babel-plugin-transform-es2015-duplicate-keys "^6.6.0" + babel-plugin-transform-es2015-for-of "^6.18.0" + babel-plugin-transform-es2015-function-name "^6.9.0" + babel-plugin-transform-es2015-literals "^6.3.13" + babel-plugin-transform-es2015-modules-amd "^6.18.0" + babel-plugin-transform-es2015-modules-commonjs "^6.18.0" + babel-plugin-transform-es2015-modules-systemjs "^6.18.0" + babel-plugin-transform-es2015-modules-umd "^6.18.0" + babel-plugin-transform-es2015-object-super "^6.3.13" + babel-plugin-transform-es2015-parameters "^6.18.0" + babel-plugin-transform-es2015-shorthand-properties "^6.18.0" + babel-plugin-transform-es2015-spread "^6.3.13" + babel-plugin-transform-es2015-sticky-regex "^6.3.13" + babel-plugin-transform-es2015-template-literals "^6.6.0" + babel-plugin-transform-es2015-typeof-symbol "^6.18.0" + babel-plugin-transform-es2015-unicode-regex "^6.3.13" + babel-plugin-transform-regenerator "^6.16.0" + +babel-preset-jest@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-16.0.0.tgz#417aabc2d7d93170f43c20ef1ea0145e8f7f2db5" + dependencies: + babel-plugin-jest-hoist "^16.0.0" + +babel-preset-react@^6.3.13: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.16.0.tgz#aa117d60de0928607e343c4828906e4661824316" + dependencies: + babel-plugin-syntax-flow "^6.3.13" + babel-plugin-syntax-jsx "^6.3.13" + babel-plugin-transform-flow-strip-types "^6.3.13" + babel-plugin-transform-react-display-name "^6.3.13" + babel-plugin-transform-react-jsx "^6.3.13" + babel-plugin-transform-react-jsx-self "^6.11.0" + babel-plugin-transform-react-jsx-source "^6.3.13" + +babel-preset-stage-0@^6.5.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.16.0.tgz#f5a263c420532fd57491f1a7315b3036e428f823" + dependencies: + babel-plugin-transform-do-expressions "^6.3.13" + babel-plugin-transform-function-bind "^6.3.13" + babel-preset-stage-1 "^6.16.0" + +babel-preset-stage-1@^6.16.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.16.0.tgz#9d31fbbdae7b17c549fd3ac93e3cf6902695e479" + dependencies: + babel-plugin-transform-class-constructor-call "^6.3.13" + babel-plugin-transform-export-extensions "^6.3.13" + babel-preset-stage-2 "^6.16.0" + +babel-preset-stage-2@^6.16.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.18.0.tgz#9eb7bf9a8e91c68260d5ba7500493caaada4b5b5" + dependencies: + babel-plugin-syntax-dynamic-import "^6.18.0" + babel-plugin-transform-class-properties "^6.18.0" + babel-plugin-transform-decorators "^6.13.0" + babel-preset-stage-3 "^6.17.0" + +babel-preset-stage-3@^6.17.0: + version "6.17.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.17.0.tgz#b6638e46db6e91e3f889013d8ce143917c685e39" + dependencies: + babel-plugin-syntax-trailing-function-commas "^6.3.13" + babel-plugin-transform-async-generator-functions "^6.17.0" + babel-plugin-transform-async-to-generator "^6.16.0" + babel-plugin-transform-exponentiation-operator "^6.3.13" + babel-plugin-transform-object-rest-spread "^6.16.0" + +babel-register@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.18.0.tgz#892e2e03865078dd90ad2c715111ec4449b32a68" + dependencies: + babel-core "^6.18.0" + babel-runtime "^6.11.6" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^5.6.18: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" + dependencies: + core-js "^1.0.0" + +babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.2.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@6.x: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.9.5" + +babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.3.0, babel-template@^6.8.0: + version "6.16.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" + dependencies: + babel-runtime "^6.9.0" + babel-traverse "^6.16.0" + babel-types "^6.16.0" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.16.0, babel-traverse@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.18.0.tgz#5aeaa980baed2a07c8c47329cd90c3b90c80f05e" + dependencies: + babel-code-frame "^6.16.0" + babel-messages "^6.8.0" + babel-runtime "^6.9.0" + babel-types "^6.18.0" + babylon "^6.11.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.13.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.8.0, babel-types@^6.9.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.18.0.tgz#1f7d5a73474c59eb9151b2417bbff4e4fce7c3f8" + dependencies: + babel-runtime "^6.9.1" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^5.8.38: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" + +babylon@^6.11.0, babylon@^6.13.0: + version "6.13.1" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.13.1.tgz#adca350e088f0467647157652bafead6ddb8dfdb" + +balanced-match@^0.4.1, balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +balanced-match@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" + +base62@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/base62/-/base62-1.1.1.tgz#974e82c11bd5e00816b508a7ed9c7b9086c9db6b" + +base64-js@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" + +base64-url@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f" + +Base64@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" + +base64url@~1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-1.0.6.tgz#d64d375d68a7c640d912e2358d170dca5bb54681" + dependencies: + concat-stream "~1.4.7" + meow "~2.0.0" + +batch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + +bcrypt-pbkdf@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" + dependencies: + tweetnacl "^0.14.3" + +big.js@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" + +binary-extensions@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.7.0.tgz#6c1610db163abfb34edfe42fa423343a1e01185d" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^2.9.33: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" + +bluebird@^3.0.5: + version "3.4.6" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" + +blueimp-tmpl@^2.5.5: + version "2.5.7" + resolved "https://registry.yarnpkg.com/blueimp-tmpl/-/blueimp-tmpl-2.5.7.tgz#33fb12c139d65512ae40afbd8e2def8d9db96490" + +body-parser@^1.14.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.15.2.tgz#d7578cf4f1d11d5f6ea804cef35dc7a7ff6dae67" + dependencies: + bytes "2.4.0" + content-type "~1.0.2" + debug "~2.2.0" + depd "~1.1.0" + http-errors "~1.5.0" + iconv-lite "0.4.13" + on-finished "~2.3.0" + qs "6.2.0" + raw-body "~2.1.7" + type-is "~1.6.13" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +bootstrap: + version "3.3.7" + resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +brackets2dots@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brackets2dots/-/brackets2dots-1.1.0.tgz#3f3d40375fc660ce0fd004fa27d67b34f9469ac3" + +breakable@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" + +"browser-request@>= 0.3.1 < 0.4.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" + +browserify-zlib@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + dependencies: + pako "~0.2.0" + +browserslist@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.4.0.tgz#9cfdcf5384d9158f5b70da2aa00b30e8ff019049" + dependencies: + caniuse-db "^1.0.30000539" + +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +buffer@^4.9.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +bytes@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" + +bytes@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + +camel-case@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-1.2.2.tgz#1aca7c4d195359a2ce9955793433c6e5542511f2" + dependencies: + sentence-case "^1.1.1" + upper-case "^1.1.1" + +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + +camelcase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-1.0.0.tgz#bd1a11bf9b31a1ce493493a930de1a0baf4ad7ec" + dependencies: + camelcase "^1.0.1" + map-obj "^1.0.0" + +camelcase@^1.0.1, camelcase@^1.0.2, camelcase@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +caniuse-db@^1.0.30000539, caniuse-db@^1.0.30000554: + version "1.0.30000572" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000572.tgz#81d0aa6b7de2d785c8dcab135502983276cc707d" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chai@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" + dependencies: + assertion-error "^1.0.1" + deep-eql "^0.1.3" + type-detect "^1.0.0" + +chalk@^1.0.0, chalk@^1.1.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" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +change-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.0.tgz#6c9c8e35f8790870a82b6b0745be8c3cbef9b081" + dependencies: + camel-case "^3.0.0" + constant-case "^2.0.0" + dot-case "^2.1.0" + header-case "^1.0.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + no-case "^2.2.0" + param-case "^2.1.0" + pascal-case "^2.0.0" + path-case "^2.1.0" + sentence-case "^2.1.0" + snake-case "^2.1.0" + swap-case "^1.1.0" + title-case "^2.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +change-case@2.3.x: + version "2.3.1" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-2.3.1.tgz#2c4fde3f063bb41d00cd68e0d5a09db61cbe894f" + dependencies: + camel-case "^1.1.1" + constant-case "^1.1.0" + dot-case "^1.1.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + param-case "^1.1.0" + pascal-case "^1.1.0" + path-case "^1.1.0" + sentence-case "^1.1.1" + snake-case "^1.1.0" + swap-case "^1.1.0" + title-case "^1.1.0" + upper-case "^1.1.1" + upper-case-first "^1.1.0" + +chokidar@^1.0.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +clap@^1.0.9: + version "1.1.1" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.1.tgz#a8a93e0bfb7581ac199c4f001a5525a724ce696d" + dependencies: + chalk "^1.1.3" + +classnames@^2.1.2, classnames@^2.2.0, classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5, classnames@2.x: + version "2.2.5" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" + +clean-css@3.4.x: + version "3.4.20" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.20.tgz#c0d8963b5448e030f0bcd3ddd0dac4dfe3dea501" + dependencies: + commander "2.8.x" + source-map "0.4.x" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +clone@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" + +coa@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.0.1.tgz#1104cd34f9b5b45d3eba88f1babc1924e1ce35fb" + dependencies: + number-is-nan "^1.0.0" + +color-convert@^1.3.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.6.0.tgz#7592755faf53938a05b1ea8e5374cab77d6dd190" + dependencies: + color-name "^1.1.1" + +color-name@^1.0.0, color-name@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +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" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.5.0, commander@^2.8.1, commander@^2.9.0, commander@2.9.x: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commander@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" + +commander@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" + +commander@2.8.x: + version "2.8.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +commoner@^0.10.1, commoner@~0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.4.tgz#98f3333dd3ad399596bb2d384a783bb7213d68f8" + dependencies: + commander "^2.5.0" + detective "^4.3.1" + glob "^5.0.15" + graceful-fs "^4.1.2" + iconv-lite "^0.4.5" + mkdirp "^0.5.0" + private "^0.1.6" + q "^1.1.2" + recast "^0.10.0" + +component-classes@^1.2.5: + version "1.2.6" + resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" + dependencies: + component-indexof "0.0.3" + +component-indexof@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" + +compressible@~2.0.8: + version "2.0.9" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.9.tgz#6daab4e2b599c2770dd9e21e7a891b1c5a755425" + dependencies: + mime-db ">= 1.24.0 < 2" + +compression@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" + dependencies: + accepts "~1.3.3" + bytes "2.3.0" + compressible "~2.0.8" + debug "~2.2.0" + on-headers "~1.0.1" + vary "~1.1.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@~1.4.7: + version "1.4.10" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" + dependencies: + inherits "~2.0.1" + readable-stream "~1.1.9" + typedarray "~0.0.5" + +concat-stream@1.5.x: + version "1.5.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + +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" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + 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" + +constant-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-1.1.2.tgz#8ec2ca5ba343e00aa38dbf4e200fd5ac907efd63" + dependencies: + snake-case "^1.1.0" + upper-case "^1.1.1" + +constant-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" + dependencies: + snake-case "^2.1.0" + upper-case "^1.1.1" + +constants-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-0.0.1.tgz#92577db527ba6c4cf0a4568d84bc031f441e21f2" + +content-disposition@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" + +content-type@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" + +convert-source-map@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-js@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" + +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" + +cors@^2.3.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.1.tgz#6181aa56abb45a2825be3304703747ae4e9d2383" + dependencies: + vary "^1" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +crypto-browserify@~3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.2.8.tgz#b9b11dbe6d9651dd882a01e6cc467df718ecf189" + dependencies: + pbkdf2-compat "2.0.1" + ripemd160 "0.2.0" + sha.js "2.2.6" + +css-animation@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.3.0.tgz#315d9742bbe282c23b9951ceac2aa53cee05b708" + dependencies: + component-classes "^1.2.5" + +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" + +css-loader@^0.23.1: + version "0.23.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.23.1.tgz#9fa23f2b5c0965235910ad5ecef3b8a36390fe50" + dependencies: + css-selector-tokenizer "^0.5.1" + cssnano ">=2.6.1 <4" + loader-utils "~0.2.2" + lodash.camelcase "^3.0.1" + object-assign "^4.0.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.0.0" + postcss-modules-local-by-default "^1.0.1" + postcss-modules-scope "^1.0.0" + postcss-modules-values "^1.1.0" + source-list-map "^0.1.4" + +css-selector-tokenizer@^0.5.1: + version "0.5.4" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.5.4.tgz#139bafd34a35fd0c1428487049e0699e6f6a2c21" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + +css-selector-tokenizer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +"cssnano@>=2.6.1 <4": + version "3.8.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.8.0.tgz#bb90ac5292f42b679d9a05f6da0e9697556bb80d" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.2.1.tgz#51fbb5347e50e81e6ed51668a48490ae6fe2afe2" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +"cssom@>= 0.3.0 < 0.4.0", cssom@0.3.x: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" + +"cssstyle@>= 0.2.29 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + +curry2@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/curry2/-/curry2-0.1.0.tgz#cc9837ec9148a6eaea47f6cedf254d103031a233" + +curry2@^1.0.0, curry2@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/curry2/-/curry2-1.0.3.tgz#38191d55f1060bfea47ca08009385bb878f6612f" + dependencies: + fast-bind "^1.0.0" + +d3@^3.5.6: + version "3.5.17" + resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" + +dashdash@^1.12.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.0.tgz#29e486c5418bf0f356034a993d51686a33e84141" + dependencies: + assert-plus "^1.0.0" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@^2.1.1, debug@^2.2.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" + dependencies: + ms "0.7.1" + +decamelize@^1.0.0, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-diff@0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.4.tgz#aac5c39952236abe5f037a2349060ba01b00ae48" + +deep-eql@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" + dependencies: + type-detect "0.1.1" + +deep-equal@^1.0.0, deep-equal@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +defs@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" + dependencies: + alter "~0.2.0" + ast-traverse "~0.1.1" + breakable "~1.0.0" + esprima-fb "~15001.1001.0-dev-harmony-fb" + simple-fmt "~0.1.0" + simple-is "~0.2.0" + stringmap "~0.2.2" + stringset "~0.2.1" + tryor "~0.1.2" + yargs "~3.27.0" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" + dependencies: + get-stdin "^4.0.1" + minimist "^1.1.0" + repeating "^1.1.0" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detective@^4.3.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" + dependencies: + acorn "^3.1.0" + defined "^1.0.0" + +diff@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" + +disposables@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.1.tgz#064727a25b54f502bd82b89aa2dfb8df9f1b39e3" + +dnd-core@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-2.0.2.tgz#4528da4fbeb1abb6c308414b2d2e8389f3514646" + dependencies: + asap "^2.0.3" + invariant "^2.0.0" + lodash "^4.2.0" + redux "^3.2.0" + +dom-align@1.x: + version "1.5.2" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.5.2.tgz#bceb6c109a3442ebc001c04c48cb37a9d5e96df2" + +dom-helpers@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-2.4.0.tgz#9bb4b245f637367b1fa670274272aa28fe06c367" + +dom-serializer@0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +dom-walk@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" + +domain-browser@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" + +domelementtype@^1.3.0, domelementtype@1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + dependencies: + domelementtype "1" + +domutils@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-1.1.2.tgz#1e73826900de28d6de5480bc1de31d0842b06bec" + dependencies: + sentence-case "^1.1.2" + +dot-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.0.tgz#4b43dd0d7403c34cb645424add397e80bfe85ca6" + dependencies: + no-case "^2.2.0" + +dotsplit.js@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/dotsplit.js/-/dotsplit.js-1.0.3.tgz#b71938c07364668d16ef4309273ca210ba00691d" + dependencies: + array.filter "^0.1.0" + arraymap "^0.1.2" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +ecdsa-sig-formatter@^1.0.0: + version "1.0.7" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.7.tgz#3137e976a1d6232517e2513e04e32f79bcbdf126" + dependencies: + base64-url "^1.2.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +element-class@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +encodeurl@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +enhanced-resolve@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.2.0" + tapable "^0.1.8" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +envify@^3.0.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" + dependencies: + jstransform "^11.0.3" + through "~2.3.4" + +errno@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" + dependencies: + prr "~0.0.0" + +error-ex@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.0.tgz#e67b43f3e82c96ea3a584ffee0b9fc3325d802d9" + dependencies: + is-arrayish "^0.2.1" + +es6-promise@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escape-string-regexp@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" + +escodegen@^1.6.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +esprima-fb@^15001.1.0-dev-harmony-fb: + version "15001.1.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901" + +esprima-fb@~15001.1001.0-dev-harmony-fb: + version "15001.1001.0-dev-harmony-fb" + resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" + +esprima@^2.6.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +esutils@^2.0.0, esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" + +eventemitter3@1.x.x: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +eventsource@~0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + dependencies: + original ">=0.0.5" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^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" + dependencies: + fill-range "^2.1.0" + +express-jwt@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-3.4.0.tgz#e6fe8730e61da81a8deab64680154ae46c0028cc" + dependencies: + async "^1.5.0" + express-unless "^0.3.0" + jsonwebtoken "^5.0.0" + lodash.set "^4.0.0" + +express-unless@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.0.tgz#5c795e7392571512dd28f520b3857a52b21261a2" + +express@^4.13.3: + version "4.14.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" + dependencies: + accepts "~1.3.3" + array-flatten "1.1.1" + content-disposition "0.5.1" + content-type "~1.0.2" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "~2.2.0" + depd "~1.1.0" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + finalhandler "0.5.0" + fresh "0.3.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.1" + path-to-regexp "0.1.7" + proxy-addr "~1.1.2" + qs "6.2.0" + range-parser "~1.2.0" + send "0.14.1" + serve-static "~1.11.1" + type-is "~1.6.13" + utils-merge "1.0.0" + vary "~1.1.0" + +extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extract-text-webpack-plugin@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-0.8.2.tgz#49309df325f53affc53972f711afba2360c1914c" + dependencies: + async "^1.2.1" + loader-utils "~0.2.3" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fast-bind@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-bind/-/fast-bind-1.0.0.tgz#7fa9652cb3325f5cd1e252d6cb4f160de1a76e75" + +fast-levenshtein@~2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.5.tgz#bd33145744519ab1c36c3ee9f31f08e9079b67f2" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.0.tgz#d9ccf0e789e7db725d74bc4877d23aa42972ac50" + dependencies: + websocket-driver ">=0.5.1" + +fbjs@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7" + dependencies: + core-js "^1.0.0" + loose-envify "^1.0.0" + promise "^7.0.3" + ua-parser-js "^0.7.9" + whatwg-fetch "^0.9.0" + +fbjs@^0.8.4: + version "0.8.5" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.5.tgz#f69ba8a876096cb1b9bffe4d7c1e71c19d39d008" + dependencies: + core-js "^1.0.0" + immutable "^3.7.6" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + ua-parser-js "^0.7.9" + +fbjs@0.1.0-alpha.10: + version "0.1.0-alpha.10" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.1.0-alpha.10.tgz#46e457c09cbefb51fc752a3e030e7b67fcc384c8" + dependencies: + core-js "^1.0.0" + promise "^7.0.3" + whatwg-fetch "^0.9.0" + +file-loader@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.8.5.tgz#9275d031fe780f27d47f5f4af02bd43713cc151b" + dependencies: + loader-utils "~0.2.5" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +finalhandler@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" + dependencies: + debug "~2.2.0" + escape-html "~1.0.3" + on-finished "~2.3.0" + statuses "~1.3.0" + unpipe "~1.0.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-up@^1.0.0, find-up@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +font-awesome@^4.3.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" + +for-in@^0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" + +for-own@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072" + dependencies: + for-in "^0.1.5" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.1.tgz#4adf0342e1a79afa1e84c8c320a9ffc82392a1f3" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +formatio@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" + dependencies: + samsam "~1.1" + +forwarded@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" + +fresh@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" + +fs-readdir-recursive@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" + +fs-readdir-recursive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.0.14" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.14.tgz#558e8cc38643d8ef40fe45158486d0d25758eee4" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" + +gauge@~2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-color "^0.1.7" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +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" + dependencies: + is-property "^1.0.0" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + 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" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^5.0.15, glob@^5.0.5: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.5: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@3.2.11: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + dependencies: + inherits "2" + minimatch "0.3" + +global@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.1.tgz#5f757908c7cbabce54f386ae440e11e26b7916df" + dependencies: + min-document "^2.19.0" + process "~0.5.1" + +globals@^6.4.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" + +globals@^9.0.0: + version "9.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.12.0.tgz#992ce90828c3a55fa8f16fada177adb64664cf9d" + +graceful-fs@^4.1.2, graceful-fs@^4.1.4: + version "4.1.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.9.tgz#baacba37d19d11f9d146d3578bc99958c3787e29" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +gravatar@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.5.2.tgz#25b4c96790b82d1b3c714e3963999253bd2ba1cd" + +growl@1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-color@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +he@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.0.0.tgz#6da5b265d7f2c3b5e480749168e0e159d05728da" + +header-case@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.0.tgz#d9e335909505d56051ec16a0106821889e910781" + dependencies: + no-case "^2.2.0" + upper-case "^1.1.3" + +history@^2.0.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/history/-/history-2.1.2.tgz#4aa2de897a0e4867e4539843be6ecdb2986bfdec" + dependencies: + deep-equal "^1.0.0" + invariant "^2.0.0" + query-string "^3.0.0" + warning "^2.0.0" + +history@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/history/-/history-3.2.1.tgz#71c7497f4e6090363d19a6713bb52a1bfcdd99aa" + dependencies: + invariant "^2.2.1" + loose-envify "^1.2.0" + query-string "^4.2.2" + warning "^3.0.0" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" + +home-or-tmp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" + dependencies: + os-tmpdir "^1.0.1" + user-home "^1.1.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" + +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" + +html-entities@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" + +html-minifier@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-1.5.0.tgz#beb05fd9cc340945865c10f40aedf469af4b1534" + dependencies: + change-case "2.3.x" + clean-css "3.4.x" + commander "2.9.x" + concat-stream "1.5.x" + he "1.0.x" + ncname "1.0.x" + relateurl "0.2.x" + uglify-js "2.6.x" + +html-webpack-plugin@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-1.7.0.tgz#cd0c73c791bd0c8c45b24e3001be334a6b74297b" + dependencies: + bluebird "^3.0.5" + blueimp-tmpl "^2.5.5" + html-minifier "^1.0.0" + lodash "^3.10.1" + +"htmlparser2@>= 3.7.3 < 4.0.0": + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +http-browserify@^1.3.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/http-browserify/-/http-browserify-1.7.0.tgz#33795ade72df88acfbfd36773cefeda764735b20" + dependencies: + Base64 "~0.2.0" + inherits "~2.0.1" + +http-errors@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.0.tgz#b1cb3d8260fd8e2386cad3189045943372d48211" + dependencies: + inherits "2.0.1" + setprototypeof "1.0.1" + statuses ">= 1.3.0 < 2" + +http-proxy-middleware@~0.17.1: + version "0.17.2" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.2.tgz#572d517a6d2fb1063a469de294eed96066352007" + dependencies: + http-proxy "^1.15.1" + is-glob "^3.0.0" + lodash "^4.16.2" + micromatch "^2.3.11" + +http-proxy@^1.15.1: + version "1.15.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31" + dependencies: + eventemitter3 "1.x.x" + requires-port "1.x.x" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.0.tgz#b3ffdfe734b2a3d4a9efd58e8654c91fce86eafd" + +iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" + +icss-replace-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5" + +ieee754@^1.1.4: + version "1.1.8" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + +immutable@^3.7.6: + version "3.8.1" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" + +indent-string@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-1.2.2.tgz#db99bcc583eb6abbb1e48dcbb1999a986041cb6b" + dependencies: + get-stdin "^4.0.1" + minimist "^1.1.0" + repeating "^1.1.0" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +interpret@^0.6.4: + version "0.6.6" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" + +invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.1.tgz#b097010547668c7e337028ebe816ebe36c8a8d54" + 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" + +ipaddr.js@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" + +is-absolute-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.0.0.tgz#9c4b20b0e5c0cbef9a479a367ede6f991679f359" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +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" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" + +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" + dependencies: + builtin-modules "^1.0.0" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +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" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.0.tgz#33411a482b046bf95e6b0cb27ee2711af4cf15ad" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + 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" + dependencies: + number-is-nan "^1.0.0" + +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" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-integer@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.6.tgz#5273819fada880d123e1ac00a938e7172dd8d95e" + dependencies: + is-finite "^1.0.0" + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + dependencies: + lower-case "^1.1.0" + +is-my-json-valid@^2.12.4: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +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" + +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" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-svg@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.0.1.tgz#f93ab3bf1d6bbca30e9753cd3485b1300eebc013" + dependencies: + html-comment-regex "^1.1.0" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + dependencies: + upper-case "^1.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +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" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isomorphic-fetch@^2.1.1, isomorphic-fetch@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul-lib-coverage@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.0.tgz#c3f9b6d226da12424064cce87fce0fb57fdfa7a2" + +istanbul-lib-instrument@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.2.0.tgz#73d5d108ab7568c373fdcb7d01c1d42d565bc8c4" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.13.0" + istanbul-lib-coverage "^1.0.0" + semver "^5.3.0" + +jade@0.26.3: + version "0.26.3" + resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" + dependencies: + commander "0.6.1" + mkdirp "0.3.0" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +js-base64@^2.1.9: + version "2.1.9" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce" + +js-tokens@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" + +js-tokens@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae" + +js-yaml@^3.0.2, js-yaml@~3.6.1: + version "3.6.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" + +jsdom@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-6.5.1.tgz#b6064d6a7651081af41d576edc56bc51e00122c0" + dependencies: + acorn "^2.4.0" + acorn-globals "^1.0.4" + browser-request ">= 0.3.1 < 0.4.0" + cssom ">= 0.3.0 < 0.4.0" + cssstyle ">= 0.2.29 < 0.3.0" + escodegen "^1.6.1" + htmlparser2 ">= 3.7.3 < 4.0.0" + nwmatcher ">= 1.3.6 < 2.0.0" + parse5 "^1.4.2" + request "^2.55.0" + symbol-tree ">= 3.1.0 < 4.0.0" + tough-cookie "^2.0.0" + whatwg-url-compat "~0.6.5" + xml-name-validator ">= 2.0.1 < 3.0.0" + xmlhttprequest ">= 1.6.0 < 2.0.0" + xtend "^4.0.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-loader@^0.5.3: + version "0.5.4" + resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +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" + +json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + +json5@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" + +json5@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.0.tgz#9b20715b026cbe3778fd769edccd822d8332a5b2" + +jsonpointer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" + +jsonwebtoken@^5.0.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz#1c90f9a86ce5b748f5f979c12b70402b4afcddb4" + dependencies: + jws "^3.0.0" + ms "^0.7.1" + xtend "^4.0.1" + +jsprim@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + dependencies: + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +jstransform@^11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/jstransform/-/jstransform-11.0.3.tgz#09a78993e0ae4d4ef4487f6155a91f6190cb4223" + dependencies: + base62 "^1.1.0" + commoner "^0.10.1" + esprima-fb "^15001.1.0-dev-harmony-fb" + object-assign "^2.0.0" + source-map "^0.4.2" + +jszip@^2.4.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.1.tgz#b88f3a7b2e67a2a048152982c7a3756d9c4828f0" + dependencies: + pako "~1.0.2" + +jszip@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.3.tgz#8a920403b2b1651c0fc126be90192d9080957c37" + dependencies: + core-js "~2.3.0" + es6-promise "~3.0.2" + lie "~3.1.0" + pako "~1.0.2" + readable-stream "~2.0.6" + +jwa@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.3.tgz#fa9f2f005ff0c630e7c41526a31f37f79733cd6d" + dependencies: + base64url "~1.0.4" + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "^1.0.0" + +jws@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.3.tgz#b88f1b4581a2c5ee8813c06b3fdf90ea9b5c7e6c" + dependencies: + base64url "~1.0.4" + jwa "^1.1.2" + +jwt-decode@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.1.0.tgz#d3079cef1689d82d56bbb7aedcfea28b12f0e36a" + +keycode@^2.1.2: + version "2.1.7" + resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.7.tgz#7b9255919f6cff562b09a064d222dca70b020f5c" + +kind-of@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74" + dependencies: + is-buffer "^1.0.2" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +leaflet-draw@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/leaflet-draw/-/leaflet-draw-0.4.1.tgz#e46d21e42c82364ae871707373abad9723983ff6" + +leaflet-editable@^1.0.0-rc.1: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/leaflet-editable/-/leaflet-editable-1.0.0-rc.2.tgz#27d7e99acc9df4df8887e520c7d87079fae69176" + +leaflet@^0.7.7: + version "0.7.7" + resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-0.7.7.tgz#1e352ba54e63d076451fa363c900890cb2cf75ee" + +leven@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lie@^3.0.1, lie@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.0.tgz#65e0139eaef9ae791a1f5c8c53692c8d3b4718f4" + 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" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-utils@^0.2.11, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5, loader-utils@0.2.x: + version "0.2.16" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + object-assign "^4.0.1" + +lodash-es@^4.2.1: + version "4.16.6" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.16.6.tgz#c8552faedaa4d1d591de8da9b3980ef1c52efa08" + +lodash._baseassign@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" + dependencies: + lodash._basecopy "^3.0.0" + lodash.keys "^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" + +lodash._baseflatten@^3.0.0: + version "3.1.4" + resolved "https://registry.yarnpkg.com/lodash._baseflatten/-/lodash._baseflatten-3.1.4.tgz#0770ff80131af6e34f3b511796a7ba5214e65ff7" + dependencies: + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash._basefor@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" + +lodash._bindcallback@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" + +lodash._createassigner@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" + dependencies: + lodash._bindcallback "^3.0.0" + lodash._isiterateecall "^3.0.0" + lodash.restparam "^3.0.0" + +lodash._createcompounder@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz#5dd2cb55372d6e70e0e2392fb2304d6631091075" + dependencies: + lodash.deburr "^3.0.0" + lodash.words "^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" + +lodash._isiterateecall@^3.0.0: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" + +lodash._pickbyarray@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash._pickbyarray/-/lodash._pickbyarray-3.0.2.tgz#1f898d9607eb560b0e167384b77c7c6d108aa4c5" + +lodash._pickbycallback@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._pickbycallback/-/lodash._pickbycallback-3.0.0.tgz#ff61b9a017a7b3af7d30e6c53de28afa19b8750a" + dependencies: + lodash._basefor "^3.0.0" + lodash.keysin "^3.0.0" + +lodash._root@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + +lodash.assign@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" + dependencies: + lodash._baseassign "^3.0.0" + lodash._createassigner "^3.0.0" + lodash.keys "^3.0.0" + +lodash.camelcase@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz#932c8b87f8a4377897c67197533282f97aeac298" + dependencies: + lodash._createcompounder "^3.0.0" + +lodash.deburr@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5" + dependencies: + lodash._root "^3.0.0" + +lodash.indexof@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/lodash.indexof/-/lodash.indexof-4.0.5.tgz#53714adc2cddd6ed87638f893aa9b6c24e31ef3c" + +lodash.isarguments@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + +lodash.isarray@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" + +lodash.keys@^3.0.0, lodash.keys@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.keysin@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" + dependencies: + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + +lodash.pick@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-3.1.0.tgz#f252a855b2046b61bcd3904b26f76bd2efc65550" + dependencies: + lodash._baseflatten "^3.0.0" + lodash._bindcallback "^3.0.0" + lodash._pickbyarray "^3.0.0" + lodash._pickbycallback "^3.0.0" + lodash.restparam "^3.0.0" + +lodash.restparam@^3.0.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" + +lodash.set@^4.0.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" + +lodash.words@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz#4e2a8649bc08745b17c695b1a3ce8fee596623b3" + dependencies: + lodash._root "^3.0.0" + +lodash@^3.10.0, lodash@^3.10.1, lodash@^3.9.3: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.16.1, lodash@^4.16.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.6.1: + version "4.16.6" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" + +lolex@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +lonlng@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/lonlng/-/lonlng-0.0.2.tgz#46d85e634a9e0c8d8e468eb61b375fd479b7973a" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.0.tgz#6b26248c42f6d4fa4b0d8542f78edfcde35642a8" + dependencies: + js-tokens "^2.0.0" + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb" + +lru-cache@^2.7.0, lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +macaddress@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +material-colors@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.0.tgz#956f0c87660882333cff7b7557efb7b03592820e" + +math-expression-evaluator@^1.2.14: + version "1.2.14" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab" + dependencies: + lodash.indexof "^4.0.5" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memory-fs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" + +memory-fs@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-2.0.0.tgz#8f530a8ecf5d40d3f4b4df93c3472900fba2a8f1" + dependencies: + camelcase-keys "^1.0.0" + indent-string "^1.1.0" + minimist "^1.1.0" + object-assign "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + +merge@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + +mgrs@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mgrs/-/mgrs-0.0.3.tgz#3058d38ae92e1abfbf74b32a8f6cb5225a6eaa05" + +micromatch@^2.1.5, micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +"mime-db@>= 1.24.0 < 2", mime-db@~1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.24.0.tgz#e2d13f939f0016c6e4e9ad25a8652f126c467f0c" + +mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7: + version "2.1.12" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.12.tgz#152ba256777020dd4663f54c2e7bc26381e71729" + dependencies: + mime-db "~1.24.0" + +mime@^1.3.4, mime@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +mime@1.2.x: + version "1.2.11" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + dependencies: + dom-walk "^0.1.0" + +minimatch@^2.0.3: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimatch@^3.0.0, minimatch@^3.0.2, "minimatch@2 || 3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimist@^1.1.0, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mkdirp@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" + +mocha-jsdom@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mocha-jsdom/-/mocha-jsdom-1.1.0.tgz#e1576fbd0601cc89d358a213a0e5585d1b7c7a01" + +mocha@^2.3.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" + dependencies: + commander "2.3.0" + debug "2.2.0" + diff "1.4.0" + escape-string-regexp "1.0.2" + glob "3.2.11" + growl "1.9.2" + jade "0.26.3" + mkdirp "0.5.1" + supports-color "1.2.0" + to-iso-string "0.0.2" + +moment-timezone@^0.5.3: + version "0.5.7" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.7.tgz#1305bcada16f046dbbc7ac89abf66effff886cb5" + dependencies: + moment ">= 2.6.0" + +moment@^2.11.2, moment@^2.8.2, "moment@>= 2.6.0": + version "2.15.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.2.tgz#1bfdedf6a6e345f322fe956d5df5bd08a8ce84dc" + +ms@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +nan@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" + +ncname@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" + 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" + +no-case@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.0.tgz#ca2825ccb76b18e6f79d573dcfbf1eace33dd164" + dependencies: + lower-case "^1.1.1" + +node-fetch@^1.0.1: + version "1.6.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +node-libs-browser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.6.0.tgz#244806d44d319e048bc8607b5cc4eaf9a29d2e3c" + dependencies: + assert "^1.1.1" + browserify-zlib "~0.1.4" + buffer "^4.9.0" + console-browserify "^1.1.0" + constants-browserify "0.0.1" + crypto-browserify "~3.2.6" + domain-browser "^1.1.1" + events "^1.0.0" + http-browserify "^1.3.2" + https-browserify "0.0.0" + os-browserify "~0.1.2" + path-browserify "0.0.0" + process "^0.11.0" + punycode "^1.2.4" + querystring-es3 "~0.2.0" + readable-stream "^1.1.13" + stream-browserify "^1.0.0" + string_decoder "~0.10.25" + timers-browserify "^1.0.1" + tty-browserify "0.0.0" + url "~0.10.1" + util "~0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.6.29: + version "0.6.31" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.31.tgz#d8a00ddaa301a940615dbcc8caad4024d58f6017" + dependencies: + mkdirp "~0.5.1" + nopt "~3.0.6" + npmlog "^4.0.0" + rc "~1.1.6" + request "^2.75.0" + rimraf "~2.5.4" + semver "~5.3.0" + tar "~2.2.1" + tar-pack "~3.3.0" + +node-uuid@~1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" + +nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2: + version "2.3.5" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-url@^1.4.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.7.0.tgz#d82452d98d38821cffddab4d77a5f8d20ce66db0" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +npmlog@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.0.tgz#e094503961c70c1774eb76692080e8d578a9f88f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.6.0" + set-blocking "~2.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +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" + +numeral@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62" + +"nwmatcher@>= 1.3.6 < 2.0.0": + version "1.3.9" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.3.9.tgz#8bab486ff7fa3dfd086656bbe8b17116d3692d2a" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-1.0.0.tgz#e65dc8766d3b47b4b8307465c8311da030b070a6" + +object-assign@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@4.x: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + +object-path@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.2.tgz#74bf3b3c5a7f2024d75e333f12021353fa9d485e" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + 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" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +open@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" + +optimist@~0.6.0, optimist@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +original@>=0.0.5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" + dependencies: + url-parse "1.0.x" + +os-browserify@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +output-file-sync@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + +pako@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf" + +param-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-1.1.2.tgz#dcb091a43c259b9228f1c341e7b6a44ea0bf9743" + dependencies: + sentence-case "^1.1.2" + +param-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.0.tgz#2619f90fd6c829ed0b958f1c84ed03a745a6d70a" + dependencies: + no-case "^2.2.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse5@^1.4.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + +parsedbf@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/parsedbf/-/parsedbf-0.1.2.tgz#7f9aaeff89961df9bea0d60957856dbc92f8a8ce" + +parseurl@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" + +pascal-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-1.1.2.tgz#3e5d64a20043830a7c49344c2d74b41be0c9c99b" + dependencies: + camel-case "^1.1.1" + upper-case-first "^1.1.0" + +pascal-case@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.0.tgz#39c248bde5a8dc02d5160696bdb01e044d016ee1" + dependencies: + camel-case "^3.0.0" + upper-case-first "^1.1.0" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-1.1.2.tgz#50ce6ba0d3bed3dd0b5c2a9c4553697434409514" + dependencies: + sentence-case "^1.1.2" + +path-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.0.tgz#5ac491de642936e5dfe0e18d16c461b8be8cf073" + dependencies: + no-case "^2.2.0" + +path-exists@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +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" + +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" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +pbkdf2-compat@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288" + +performance-now@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + 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" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +polyline@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/polyline/-/polyline-0.2.0.tgz#4f2b716ca81134a6cbaa488975d236ecb1cc2840" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-colormin@^2.1.8: + version "2.2.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.1.tgz#dc5421b6ae6f779ef6bfd47352b94abe59d0316b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.4.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.4.1.tgz#45dce4d4e33b7d967b97a4d937f270ea98d2fe7a" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +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" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.0.1.tgz#5fae3f1a71df3e19cffb37309d1a7dba56c4589c" + 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" + 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" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.2.tgz#5d72f7d05d11de0a9589e001958067ccae1b4931" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +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" + dependencies: + postcss "^5.0.4" + uniqid "^4.0.0" + +postcss-loader@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-0.6.0.tgz#984f9c9add2f8b153d754909dad04fbbb6b02f39" + dependencies: + loader-utils "^0.2.11" + postcss "^5.0.0" + +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" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.1.tgz#ff59b5dec6d586ce2cea183138f55c5876fa9cdc" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.0.10" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.0.10.tgz#54b360be804e7e69a5c7222635247b92a3569e9b" + dependencies: + postcss "^5.0.4" + vendors "^1.0.0" + +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" + +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" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.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" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.0.5.tgz#82d602643b8616a61fb3634d7ede0289836d67f9" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.0.5.tgz#4e1f966fb49c95266804016ba9a3c6645bb601e0" + dependencies: + alphanum-sort "^1.0.2" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" + dependencies: + postcss "^5.0.4" + +postcss-modules-local-by-default@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce" + dependencies: + css-selector-tokenizer "^0.6.0" + postcss "^5.0.4" + +postcss-modules-scope@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29" + dependencies: + css-selector-tokenizer "^0.6.0" + postcss "^5.0.4" + +postcss-modules-values@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1" + dependencies: + icss-replace-symbols "^1.0.2" + postcss "^5.0.14" + +postcss-normalize-charset@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.0.tgz#2fbd30e12248c442981d31ea2484d46fd0628970" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.7.tgz#6bd90d0a4bc5a1df22c26ea65c53257dc3829f4e" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.2.tgz#be8b511741fab2dac8e614a2302e9d10267b0771" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-reduce-idents@^2.2.2: + version "2.3.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.3.1.tgz#024e8e219f52773313408573db9645ba62d2d2fe" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.0.tgz#8f739b938289ef2e48936d7101783e4741ca9bbb" + 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" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-selector-parser@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.1.tgz#fdbf696103b12b0a64060e5610507f410491f7c8" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-svgo@^2.1.1: + version "2.1.5" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.5.tgz#46fc0363f01bab6a36a9abb01c229fcc45363094" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +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" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +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" + +postcss-zindex@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.1.1.tgz#ea3fbe656c9738aa8729e2ee96ec2a46089b720f" + dependencies: + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@^5.0.0, 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.4: + version "5.2.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.5.tgz#ec428c27dffc7fac65961340a9b022fa4af5f056" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.1.2" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +prettysize@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-0.0.3.tgz#14afff6a645e591a4ddf1c72919c23b4146181a1" + +private@^0.1.6, private@~0.1.5: + version "0.1.6" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.6.tgz#55c6a976d0f9bafb9924851350fe47b9b5fbb7c1" + +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" + +process@^0.11.0, process@~0.11.0: + version "0.11.9" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" + +process@~0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" + +proj4@^2.1.4: + version "2.3.15" + resolved "https://registry.yarnpkg.com/proj4/-/proj4-2.3.15.tgz#5ad06e8bca30be0ffa389a49e4565f51f06d089e" + dependencies: + mgrs "~0.0.2" + +promise@^7.0.3, promise@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + dependencies: + asap "~2.0.3" + +proxy-addr@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" + dependencies: + forwarded "~0.1.0" + ipaddr.js "1.1.1" + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + +punycode@^1.2.4, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +q@^1.1.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + +qs@^6.2.1, qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + +qs@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" + +query-string@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638" + dependencies: + strict-uri-encode "^1.0.0" + +query-string@^4.1.0, query-string@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.2.3.tgz#9f27273d207a25a8ee4c7b8c74dcd45d556db822" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@^0.2.0, querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +querystringify@0.0.x: + version "0.0.4" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" + +quickselect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-1.0.0.tgz#02630818f9aae4ecab26f0103f98d061c17c58f3" + +raf@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.3.0.tgz#93845eeffc773f8129039f677f80a36044eee2c3" + dependencies: + performance-now "~0.2.0" + +randomatic@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + +raw-body@~2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" + dependencies: + bytes "2.4.0" + iconv-lite "0.4.13" + unpipe "1.0.0" + +rbush@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/rbush/-/rbush-2.0.1.tgz#4cfaca28c3064bc0ee75431a1b79990e875eefa9" + dependencies: + quickselect "^1.0.0" + +rc-align@2.x: + version "2.3.2" + resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.3.2.tgz#23bfc2a403fd21c567ef4dae3410ad6502e3fba0" + dependencies: + dom-align "1.x" + rc-util "3.x" + +rc-animate@2.x: + version "2.3.1" + resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.3.1.tgz#eacda46bf589d857f640c53953d92e07cb688636" + dependencies: + css-animation "^1.3.0" + +rc-slider@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-5.2.1.tgz#58e4526a5296cf40c2a1515fe8fda3736fb611af" + dependencies: + babel-runtime "6.x" + classnames "^2.2.5" + rc-tooltip "^3.4.2" + rc-util "^3.2.1" + warning "^3.0.0" + +rc-tooltip@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.4.2.tgz#c5c89c347ed790f7f290ef825d5e24e8fb599166" + dependencies: + rc-trigger "1.x" + +rc-trigger@1.x: + version "1.7.3" + resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-1.7.3.tgz#0f34afbc975b3ccd6c2bbb46a8b000749ed4cbf7" + dependencies: + babel-runtime "6.x" + rc-align "2.x" + rc-animate "2.x" + rc-util "^3.3.x" + +rc-util@^3.2.1, rc-util@^3.3.x, rc-util@3.x: + version "3.4.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-3.4.1.tgz#4b7e0b0c7593bdbcff8ed045d88fbbc773a7b061" + dependencies: + add-dom-event-listener "1.x" + classnames "2.x" + shallowequal "0.2.x" + +rc@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~1.0.4" + +react-addons-perf@^15.2.1: + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.3.2.tgz#bbdbebe8649f936f9636a5750ac145bf5c620213" + +"react-addons-shallow-compare@^0.14.0 || ^15.0.0", react-addons-shallow-compare@^15.1.0: + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.3.2.tgz#c9edba49b9eab44d0c59024d289beb1ab97318b5" + +react-addons-test-utils@^0.14.3: + version "0.14.8" + resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-0.14.8.tgz#dcddc039e71fc3c81d80338e53a3714f14d41e1f" + +"react-addons-update@^0.14.0 || ^15.0.0": + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.3.2.tgz#b6385c4db1e5df371825e0615b04360ed94430fe" + +react-addons-update@^0.14.7: + version "0.14.8" + resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-0.14.8.tgz#486f1fb71e09ef8ad09c0e95fbe59d173782fe1a" + +react-bootstrap-datetimepicker@^0.0.22: + version "0.0.22" + resolved "https://registry.yarnpkg.com/react-bootstrap-datetimepicker/-/react-bootstrap-datetimepicker-0.0.22.tgz#07e448d993157d049ad0876d0f9a3c9c5029d9c5" + dependencies: + babel-runtime "^5.6.18" + classnames "^2.1.2" + moment "^2.8.2" + +react-bootstrap-table@^2.5.2: + version "2.5.6" + resolved "https://registry.yarnpkg.com/react-bootstrap-table/-/react-bootstrap-table-2.5.6.tgz#4a7fe78d2ab9d4b7ff86e460f13cc02637f66457" + dependencies: + classnames "^2.1.2" + react-toastr "^2.8.0" + +react-bootstrap@^0.30.0-rc.2: + version "0.30.6" + resolved "https://registry.yarnpkg.com/react-bootstrap/-/react-bootstrap-0.30.6.tgz#288662a245f9dbb79f7740ee595e4ec931d6a4a9" + dependencies: + babel-runtime "^6.11.6" + classnames "^2.2.5" + dom-helpers "^2.4.0" + invariant "^2.2.1" + keycode "^2.1.2" + react-overlays "^0.6.10" + react-prop-types "^0.4.0" + uncontrollable "^4.0.1" + warning "^3.0.0" + +react-color@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.4.0.tgz#58005b9093bb8b51b43bc6b91b4410c8c0a2d236" + dependencies: + babel-jest "^16.0.0" + lodash "^4.0.1" + material-colors "^1.0.0" + merge "^1.2.0" + react-addons-shallow-compare "^0.14.0 || ^15.0.0" + reactcss "^1.0.6" + tinycolor2 "^1.1.2" + +react-deep-force-update@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.0.1.tgz#f911b5be1d2a6fe387507dd6e9a767aa2924b4c7" + +react-dnd-html5-backend@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.1.2.tgz#bcff5866629c335b310b1062fe6537af35073c66" + dependencies: + lodash "^4.2.0" + +react-dnd@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-2.1.4.tgz#dd2afeddddd5ff4507d795a5bd44361c84374c0f" + dependencies: + disposables "^1.0.1" + dnd-core "^2.0.1" + invariant "^2.1.0" + lodash "^4.2.0" + +react-dom@^0.14.0: + version "0.14.8" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.14.8.tgz#0f1c547514263f771bd31814a739e5306575069e" + +"react-dom@^0.14.0 || ^15.0.0", react-dom@^15.2.1: + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f" + +react-dropzone@^3.5.3: + version "3.7.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-3.7.2.tgz#e6e17e39eb0f2741beff200a4f9cdb796a9b0125" + dependencies: + attr-accept "^1.0.3" + +react-fa@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/react-fa/-/react-fa-4.1.2.tgz#5d828916da05efc1ed58c97531f36120146f1f16" + dependencies: + font-awesome "^4.3.0" + +react-helmet@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-3.1.0.tgz#63486194682f33004826f3687dc49a138b557050" + dependencies: + deep-equal "1.0.1" + object-assign "^4.0.1" + react-side-effect "1.0.2" + shallowequal "0.2.2" + warning "2.1.0" + +react-input-autosize@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.1.0.tgz#3fe1ac832387d8abab85f6051ceab1c9e5570853" + +"react-leaflet-draw@github:alex3165/react-leaflet-draw": + version "0.10.1" + resolved "https://codeload.github.com/alex3165/react-leaflet-draw/tar.gz/c6422e0205782d84bec913cd9fe5014c92178f8c" + dependencies: + react-leaflet "^0.12.1" + +react-leaflet@^0.12.1: + version "0.12.3" + resolved "https://registry.yarnpkg.com/react-leaflet/-/react-leaflet-0.12.3.tgz#db37372e2f9bca7c45cc6d8c2e720bee7788845b" + dependencies: + lodash "^4.0.0" + warning "^3.0.0" + +react-overlays@^0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.6.10.tgz#e7e52dad47f00a0fc784eb044428c3a9e874bfa3" + dependencies: + classnames "^2.2.5" + dom-helpers "^2.4.0" + react-prop-types "^0.4.0" + warning "^3.0.0" + +react-prop-types@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/react-prop-types/-/react-prop-types-0.4.0.tgz#f99b0bfb4006929c9af2051e7c1414a5c75b93d0" + dependencies: + warning "^3.0.0" + +react-proxy@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a" + dependencies: + lodash "^4.6.1" + react-deep-force-update "^1.0.0" + +react-pure-render@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-pure-render/-/react-pure-render-1.0.2.tgz#9d8a928c7f2c37513c2d064e57b3e3c356e9fabb" + +react-redux@^4.4.0: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-4.4.5.tgz#f509a2981be2252d10c629ef7c559347a4aec457" + dependencies: + hoist-non-react-statics "^1.0.3" + invariant "^2.0.0" + lodash "^4.2.0" + loose-envify "^1.1.0" + +react-router-bootstrap@^0.20.1: + version "0.20.1" + resolved "https://registry.yarnpkg.com/react-router-bootstrap/-/react-router-bootstrap-0.20.1.tgz#fc2059660e268b022b05abc69db5281ca32b63b3" + +react-router-redux@^4.0.0: + version "4.0.6" + resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.6.tgz#10cf98dce911d7dd912a05bdb07fee4d3c563dee" + +react-router@^3.0.0-alpha.1: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.0.0.tgz#3f313e4dbaf57048c48dd0a8c3cac24d93667dff" + dependencies: + history "^3.0.0" + hoist-non-react-statics "^1.2.0" + invariant "^2.2.1" + loose-envify "^1.2.0" + warning "^3.0.0" + +react-select@^1.0.0-beta14: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-1.0.0-rc.2.tgz#9fc11b149a3dc1ac831289d21b40a59742f82f8d" + dependencies: + classnames "^2.2.4" + react-input-autosize "^1.1.0" + +react-side-effect@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.0.2.tgz#98e354decdbf0281e4223d87852d33e345eda561" + dependencies: + fbjs "0.1.0-alpha.10" + +react-sidebar@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/react-sidebar/-/react-sidebar-2.2.1.tgz#a8faf6a3c62ddc562c70680d5d016fe9741b585f" + +react-toastr@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/react-toastr/-/react-toastr-2.8.1.tgz#17dfbccf37caec55e165b712e70fb35e6940205b" + dependencies: + classnames "^2.2.5" + element-class "^0.2.2" + lodash "^4.16.1" + react-addons-update "^0.14.0 || ^15.0.0" + react-dom "^0.14.0 || ^15.0.0" + +react-transform-hmr@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb" + dependencies: + global "^4.3.0" + react-proxy "^1.1.7" + +react-virtualized-select@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-1.4.0.tgz#6a7adededbcbf0421280f0a106a920dbff2d5f38" + dependencies: + react-select "^1.0.0-beta14" + react-virtualized "^7.0.0" + +react-virtualized@^7.0.0, react-virtualized@^7.11.2: + version "7.24.3" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-7.24.3.tgz#b9b1b20568339e66e7b6908e92ff6b55309253b7" + dependencies: + babel-runtime "^6.11.6" + classnames "^2.2.3" + dom-helpers "^2.4.0" + raf "^3.1.0" + +react@^0.14.0: + version "0.14.8" + resolved "https://registry.yarnpkg.com/react/-/react-0.14.8.tgz#078dfa454d4745bcc54a9726311c2bf272c23684" + dependencies: + envify "^3.0.0" + fbjs "^0.6.1" + +react@^15.2.1: + version "15.3.2" + resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e" + dependencies: + fbjs "^0.8.4" + loose-envify "^1.1.0" + object-assign "^4.1.0" + +reactcss@^1.0.4, reactcss@^1.0.6: + version "1.0.9" + resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.0.9.tgz#adb38e1d75640433ac99ef4933b8116d8a369fd4" + dependencies: + classnames "^2.2.0" + lodash "^4.0.1" + merge "^1.2.0" + object-assign "^4.1.0" + +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" + dependencies: + find-up "^1.0.0" + read-pkg "^1.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" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~2.0.0, readable-stream@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +recast@^0.10.0, recast@^0.10.10: + version "0.10.43" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" + dependencies: + ast-types "0.8.15" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + source-map "~0.5.0" + +recast@0.10.33: + version "0.10.33" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" + dependencies: + ast-types "0.8.12" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + source-map "~0.5.0" + +reduce-css-calc@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.1.tgz#fa02e126e695824263cab91d3a5b0fdc1dd27a9a" + dependencies: + balanced-match "~0.1.0" + +redux-logger@^2.6.1: + version "2.7.4" + resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.7.4.tgz#891e5d29e7f111d08b5781a237b9965b5858c7f8" + dependencies: + deep-diff "0.3.4" + +redux-thunk@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.1.0.tgz#c724bfee75dbe352da2e3ba9bc14302badd89a98" + +redux@^3.2.0, redux@^3.3.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" + dependencies: + lodash "^4.2.1" + lodash-es "^4.2.1" + loose-envify "^1.1.0" + symbol-observable "^1.0.2" + +regenerate@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.1.tgz#0300203a5d2fdcf89116dce84275d011f5903f33" + +regenerator-runtime@^0.9.5: + version "0.9.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz#403d6d40a4bdff9c330dd9392dcbb2d9a8bba1fc" + +regenerator@0.8.40: + version "0.8.40" + resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.40.tgz#a0e457c58ebdbae575c9f8cd75127e93756435d8" + dependencies: + commoner "~0.10.3" + defs "~1.1.0" + esprima-fb "~15001.1001.0-dev-harmony-fb" + private "~0.1.5" + recast "0.10.33" + through "~2.3.8" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexpu/-/regexpu-1.3.0.tgz#e534dc991a9e5846050c98de6d7dd4a55c9ea16d" + dependencies: + esprima "^2.6.0" + recast "^0.10.10" + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + 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" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^1.1.0, repeating@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" + dependencies: + is-finite "^1.0.0" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@^2.55.0, request@^2.67.0, request@^2.75.0: + version "2.76.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.76.0.tgz#be44505afef70360a0436955106be3945d95560e" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + +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" + +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" + +resolve@^1.1.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@^2.4.3, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +ripemd160@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" + +samsam@~1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" + +samsam@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" + +sax@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + +selectn@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/selectn/-/selectn-0.10.0.tgz#8ad4fabdd6338677f519a28711792c45a3a14c39" + +selectn@^1.0.20, selectn@^1.0.5: + version "1.1.1" + resolved "https://registry.yarnpkg.com/selectn/-/selectn-1.1.1.tgz#94271b1cc10b82801a032f7276eb076f70c8b7f8" + dependencies: + brackets2dots "^1.1.0" + curry2 "^1.0.0" + debug "^2.2.0" + dotsplit.js "^1.0.3" + +semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +send@0.14.1: + version "0.14.1" + resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" + dependencies: + debug "~2.2.0" + depd "~1.1.0" + destroy "~1.0.4" + encodeurl "~1.0.1" + escape-html "~1.0.3" + etag "~1.7.0" + fresh "0.3.0" + http-errors "~1.5.0" + mime "1.3.4" + ms "0.7.1" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.3.0" + +sentence-case@^1.1.1, sentence-case@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-1.1.3.tgz#8034aafc2145772d3abe1509aa42c9e1042dc139" + dependencies: + lower-case "^1.1.1" + +sentence-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.0.tgz#d592fbed457fd1a59e3af0ee17e99f6fd70d7efd" + dependencies: + no-case "^2.2.0" + upper-case-first "^1.1.2" + +serve-index@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" + dependencies: + accepts "~1.3.3" + batch "0.5.3" + debug "~2.2.0" + escape-html "~1.0.3" + http-errors "~1.5.0" + mime-types "~2.1.11" + parseurl "~1.3.1" + +serve-static@~1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" + dependencies: + encodeurl "~1.0.1" + escape-html "~1.0.3" + parseurl "~1.3.1" + send "0.14.1" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +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" + +setprototypeof@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" + +sha.js@2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" + +shallowequal@0.2.2, shallowequal@0.2.x: + version "0.2.2" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e" + dependencies: + lodash.keys "^3.1.2" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shpjs@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/shpjs/-/shpjs-3.3.2.tgz#f8ca857a4c70ee9526965a280d229a46775fb553" + dependencies: + jszip "^2.4.0" + lie "^3.0.1" + lru-cache "^2.7.0" + parsedbf "~0.1.2" + proj4 "^2.1.4" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" + +simple-fmt@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" + +simple-is@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" + +sinon-chai@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.8.0.tgz#432a9bbfd51a6fc00798f4d2526a829c060687ac" + +sinon@^1.16.1: + version "1.17.6" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.6.tgz#a43116db59577c8296356afee13fafc2332e58e1" + dependencies: + formatio "1.1.1" + lolex "1.3.2" + samsam "1.1.2" + util ">=0.10.3 <1" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +snake-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-1.1.2.tgz#0c2f25e305158d9a18d3d977066187fef8a5a66a" + dependencies: + sentence-case "^1.1.2" + +snake-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f" + dependencies: + no-case "^2.2.0" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sockjs-client@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0" + dependencies: + debug "^2.2.0" + eventsource "~0.1.6" + faye-websocket "~0.11.0" + inherits "^2.0.1" + json3 "^3.3.2" + url-parse "^1.1.1" + +sockjs@^0.3.15: + version "0.3.18" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" + dependencies: + faye-websocket "^0.10.0" + uuid "^2.0.2" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^0.1.4, source-list-map@~0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.6.tgz#e1e6f94f0b40c4d28dcf8f5b8766e0e45636877f" + +source-map-support@^0.2.10: + version "0.2.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" + dependencies: + source-map "0.1.32" + +source-map-support@^0.4.2: + version "0.4.6" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.6.tgz#32552aa64b458392a85eab3b0b5ee61527167aeb" + dependencies: + source-map "^0.5.3" + +source-map@^0.4.2, source-map@~0.4.1, source-map@0.4.x: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +source-map@0.1.32: + version "0.1.32" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" + dependencies: + amdefine ">=0.0.4" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + 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" + +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" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.10.1.tgz#30e1a5d329244974a1af61511339d595af6638b0" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stable@~0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" + +stats-webpack-plugin@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stats-webpack-plugin/-/stats-webpack-plugin-0.2.2.tgz#7d6a839669ca67909a9db801d502bc395cc66695" + +"statuses@>= 1.3.0 < 2", statuses@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" + +stream-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-1.0.0.tgz#bf9b4abfb42b274d751479e44e0ff2656b6f1193" + dependencies: + inherits "~2.0.1" + readable-stream "^1.0.27-1" + +stream-cache@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/stream-cache/-/stream-cache-0.0.2.tgz#1ac5ad6832428ca55667dbdee395dad4e6db118f" + +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" + +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" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +stringmap@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" + +stringset@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +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" + dependencies: + ansi-regex "^2.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" + dependencies: + is-utf8 "^0.2.0" + +strip-json-comments@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + +style-loader@^0.13.0: + version "0.13.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.1.tgz#468280efbc0473023cd3a6cd56e33b5a1d7fc3a9" + dependencies: + loader-utils "^0.2.7" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" + dependencies: + has-flag "^1.0.0" + +supports-color@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" + +svgo@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.1.tgz#287320fed972cb097e72c2bb1685f96fe08f8034" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.2.1" + js-yaml "~3.6.1" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +symbol-observable@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" + +"symbol-tree@>= 3.1.0 < 4.0.0": + version "3.1.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.1.4.tgz#02b279348d337debc39694c5c95f882d448a312a" + +tapable@^0.1.8, tapable@~0.1.8: + version "0.1.10" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" + +tar-pack@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" + dependencies: + debug "~2.2.0" + fstream "~1.0.10" + fstream-ignore "~1.0.5" + once "~1.3.3" + readable-stream "~2.1.4" + rimraf "~2.5.1" + tar "~2.2.1" + uid-number "~0.0.6" + +tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +test-exclude@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-2.1.3.tgz#a8d8968e1da83266f9864f2852c55e220f06434a" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + +through@~2.3.4, through@~2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timers-browserify@^1.0.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" + dependencies: + process "~0.11.0" + +tinycolor2@^1.1.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" + +title-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-1.1.2.tgz#fae4a6ae546bfa22d083a0eea910a40d12ed4f5a" + dependencies: + sentence-case "^1.1.1" + upper-case "^1.0.3" + +title-case@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.0.tgz#c68ccb4232079ded64f94b91b4941ade91391979" + dependencies: + no-case "^2.2.0" + upper-case "^1.0.3" + +to-fast-properties@^1.0.0, to-fast-properties@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + +to-iso-string@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" + +tough-cookie@^2.0.0, tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tr46@~0.0.1: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + +trim-right@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +truncate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/truncate/-/truncate-2.0.0.tgz#09d5bc4163f3e257cd687351241071c24f14112f" + +try-resolve@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" + +tryor@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +turf-along@^3.0.10: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-along/-/turf-along-3.0.12.tgz#e622bde7a4bd138c09647d4b14aa0ea700485de6" + dependencies: + turf-bearing "^3.0.12" + turf-destination "^3.0.12" + turf-distance "^3.0.12" + turf-helpers "^3.0.12" + +turf-bearing@^3.0.10, turf-bearing@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-bearing/-/turf-bearing-3.0.12.tgz#65f609dd850e7364c7771aa6ded87b0e1917fd20" + dependencies: + turf-invariant "^3.0.12" + +turf-destination@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-destination/-/turf-destination-3.0.12.tgz#7dd6fbf97e86f831a26c83ef2d5a2f8d1d8a6de2" + dependencies: + turf-helpers "^3.0.12" + turf-invariant "^3.0.12" + +turf-distance@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-distance/-/turf-distance-3.0.12.tgz#fb97b8705facd993b145e014b41862610eeca449" + dependencies: + turf-helpers "^3.0.12" + turf-invariant "^3.0.12" + +turf-extent@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/turf-extent/-/turf-extent-1.0.4.tgz#9f28ce11af916ada88862a062074658ba3fe0f01" + dependencies: + turf-meta "^1.0.2" + +turf-helpers@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-helpers/-/turf-helpers-3.0.12.tgz#dd4272e74b3ad7c96eecb7ae5c57fe8eca544b7b" + +turf-invariant@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-invariant/-/turf-invariant-3.0.12.tgz#3b95253953991ebd962dd35d4f6704c287de8ebe" + +turf-line-distance@^3.0.10: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-line-distance/-/turf-line-distance-3.0.12.tgz#7108f5b26907f7b8c2dd1b3997866dd3a60e8f5f" + dependencies: + turf-distance "^3.0.12" + turf-helpers "^3.0.12" + +turf-line-slice@^3.0.11: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-line-slice/-/turf-line-slice-3.0.12.tgz#f5f1accc92adae69ea1ac0b29f07529a28dde916" + dependencies: + turf-bearing "^3.0.12" + turf-destination "^3.0.12" + turf-distance "^3.0.12" + turf-helpers "^3.0.12" + turf-point-on-line "^3.0.12" + +turf-linestring@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/turf-linestring/-/turf-linestring-1.0.2.tgz#604a63a7c54c9346ab9f39f273bbb7ece05a3096" + +turf-meta@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/turf-meta/-/turf-meta-1.0.2.tgz#74d4c495938eb24772a70b31a157ee3d4fd6ba1b" + +turf-point-on-line@^3.0.11, turf-point-on-line@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-point-on-line/-/turf-point-on-line-3.0.12.tgz#1d8663354e70372db1863e6253e9040c47127b0f" + dependencies: + turf-bearing "^3.0.12" + turf-destination "^3.0.12" + turf-distance "^3.0.12" + turf-helpers "^3.0.12" + +turf-point@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/turf-point/-/turf-point-2.0.1.tgz#a2dcc30a2d20f44cf5c6271df7bae2c0e2146069" + dependencies: + minimist "^1.1.0" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.3.tgz#3da382f670f25ded78d7b3d1792119bca0b7132d" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-detect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" + +type-detect@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" + +type-is@~1.6.13: + version "1.6.13" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.11" + +typedarray@~0.0.5: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +ua-parser-js@^0.7.9: + version "0.7.10" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f" + +uglify-js@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.4.tgz#a295a0de12b6a650c031c40deb0dc40b14568bd2" + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + +uglify-js@2.6.x: + version "2.6.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.6.4.tgz#65ea2fb3059c9394692f15fed87c2b36c16b9adf" + dependencies: + async "~0.2.6" + source-map "~0.5.1" + uglify-to-browserify "~1.0.0" + yargs "~3.10.0" + +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" + +uid-number@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +uncontrollable@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.0.3.tgz#06ec76cb9e02914756085d9cea0354fc746b09b4" + dependencies: + invariant "^2.1.0" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqid@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.0.tgz#33d9679f65022f48988a03fd24e7dcaf8f109eca" + 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" + +unpipe@~1.0.0, unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +upper-case-first@^1.1.0, upper-case-first@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + +url-loader@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.7.tgz#67e8779759f8000da74994906680c943a9b0925d" + dependencies: + loader-utils "0.2.x" + mime "1.2.x" + +url-parse@^1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url-parse@1.0.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" + dependencies: + querystringify "0.0.x" + requires-port "1.0.x" + +url@~0.10.1: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +"util@>=0.10.3 <1", util@~0.10.3, util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +utils-merge@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" + +uuid@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" + +v8flags@^2.0.10: + version "2.0.11" + resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" + dependencies: + user-home "^1.1.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" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +validator@^5.5.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-5.7.0.tgz#7a87a58146b695ac486071141c0c49d67da05e5c" + +vary@^1, vary@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" + +vendors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +warning@^2.0.0, warning@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" + dependencies: + loose-envify "^1.0.0" + +warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" + dependencies: + loose-envify "^1.0.0" + +watchpack@^0.2.1: + version "0.2.9" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b" + dependencies: + async "^0.9.0" + chokidar "^1.0.0" + graceful-fs "^4.1.2" + +webpack-core@~0.6.0: + version "0.6.8" + resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.8.tgz#edf9135de00a6a3c26dd0f14b208af0aa4af8d0a" + dependencies: + source-list-map "~0.1.0" + source-map "~0.4.1" + +webpack-dev-middleware@^1.2.0, webpack-dev-middleware@^1.4.0: + version "1.8.4" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.8.4.tgz#e8765c9122887ce9e3abd4cc9c3eb31b61e0948d" + dependencies: + memory-fs "~0.3.0" + mime "^1.3.4" + path-is-absolute "^1.0.0" + range-parser "^1.0.3" + +webpack-dev-server@^1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-1.16.2.tgz#8bebc2c4ce1c45a15c72dd769d9ba08db306a793" + dependencies: + compression "^1.5.2" + connect-history-api-fallback "^1.3.0" + express "^4.13.3" + http-proxy-middleware "~0.17.1" + open "0.0.5" + optimist "~0.6.1" + serve-index "^1.7.2" + sockjs "^0.3.15" + sockjs-client "^1.0.3" + stream-cache "~0.0.1" + strip-ansi "^3.0.0" + supports-color "^3.1.1" + webpack-dev-middleware "^1.4.0" + +webpack-hot-middleware@^2.2.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.13.1.tgz#104350e044be58ba3b7ef1c39513d69562841975" + dependencies: + ansi-html "0.0.6" + html-entities "^1.2.0" + querystring "^0.2.0" + strip-ansi "^3.0.0" + +webpack-visualizer-plugin@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/webpack-visualizer-plugin/-/webpack-visualizer-plugin-0.1.5.tgz#88acdba3a212f7c845f37a086ed23188ab476ea6" + dependencies: + d3 "^3.5.6" + prettysize "0.0.3" + react "^0.14.0" + react-dom "^0.14.0" + +webpack@^1.13.0: + version "1.13.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.13.3.tgz#e79c46fe5a37c5ca70084ba0894c595cdcb42815" + dependencies: + acorn "^3.0.0" + async "^1.3.0" + clone "^1.0.2" + enhanced-resolve "~0.9.0" + interpret "^0.6.4" + loader-utils "^0.2.11" + memory-fs "~0.3.0" + mkdirp "~0.5.0" + node-libs-browser "^0.6.0" + optimist "~0.6.0" + supports-color "^3.1.0" + tapable "~0.1.8" + uglify-js "~2.7.3" + watchpack "^0.2.1" + webpack-core "~0.6.0" + +websocket-driver@>=0.5.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + dependencies: + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" + +whatwg-fetch@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" + +whatwg-fetch@>=0.10.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.0.0.tgz#01c2ac4df40e236aaa18480e3be74bd5c8eb798e" + +whatwg-url-compat@~0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" + dependencies: + tr46 "~0.0.1" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +window-size@^0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +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" + +"xml-name-validator@>= 2.0.1 < 3.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + +"xmlhttprequest@>= 1.6.0 < 2.0.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + +xtend@^4.0.0, xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yaml-loader@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.1.0.tgz#33cd4e6404c5b441005810f1537775d73cb28a66" + dependencies: + js-yaml "^3.0.2" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" + +yargs@~3.27.0: + version "3.27.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" + dependencies: + camelcase "^1.2.1" + cliui "^2.1.0" + decamelize "^1.0.0" + os-locale "^1.4.0" + window-size "^0.1.2" + y18n "^3.2.0" + From 5627ebf36bbc8af36148d7489fece560665e31c3 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 2 Nov 2016 12:45:02 +0700 Subject: [PATCH 108/323] chore(yarn): Add yarn as a prestart script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ad981781..8ea24ac7a 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ "build": "webpack", "build-min": "WEBPACK_MINIFY=true npm run build", "dev-server": "WEBPACK_DEV_SERVER=true webpack-dev-server", - "dev-server-min": "WEBPACK_MINIFY=true npm run dev-server" + "dev-server-min": "WEBPACK_MINIFY=true npm run dev-server", + "prestart": "yarn" }, "dependencies": { "array-unique": "^0.2.1", From 518c1b18aefe7d6a33dd831b185090178f36d928 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 2 Nov 2016 15:31:12 +0700 Subject: [PATCH 109/323] refactor(build): Refactor code to build with mastarm --- .babelrc | 18 - .gitignore | 1 + configurations/default/settings.yml | 3 + gtfsplus.yml | 80 +- index.html | 15 + package.json | 46 +- src/main/client/admin/components/UserAdmin.js | 2 +- src/main/client/admin/components/UserList.js | 2 +- src/main/client/admin/reducers/index.js | 4 +- .../alerts/components/AffectedEntity.js | 2 +- .../client/alerts/components/AlertsList.js | 2 +- .../client/alerts/components/AlertsViewer.js | 2 +- .../alerts/containers/ActiveAlertEditor.js | 2 - .../alerts/containers/MainAlertsViewer.js | 2 - src/main/client/alerts/reducers/index.js | 6 +- src/main/client/alerts/style.css | 2 - .../common/components/DatatoolsNavbar.js | 2 +- .../client/common/components/JobMonitor.js | 2 +- src/main/client/common/components/Loading.js | 2 +- src/main/client/common/components/Sidebar.js | 7 +- .../common/components/SidebarNavItem.js | 2 +- src/main/client/common/containers/App.js | 8 - .../client/common/containers/StarButton.js | 2 +- src/main/client/common/user/Auth0Manager.js | 5 +- .../client/editor/components/CalendarList.js | 2 +- .../components/EditorFeedSourcePanel.js | 2 +- .../client/editor/components/EditorMap.js | 2 +- .../client/editor/components/EditorSidebar.js | 2 +- .../client/editor/components/EntityDetails.js | 2 +- .../client/editor/components/EntityList.js | 3 +- .../client/editor/components/FeedInfoPanel.js | 2 +- .../editor/components/PatternStopCard.js | 16 +- .../editor/components/PatternStopContainer.js | 10 +- .../client/editor/components/RouteEditor.js | 2 +- .../editor/components/TimetableEditor.js | 2 +- .../editor/components/TripPatternList.js | 4 +- .../components/VirtualizedEntitySelect.js | 3 - src/main/client/editor/reducers/index.js | 4 +- src/main/client/gtfs/components/GtfsMap.js | 2 +- src/main/client/gtfs/reducers/index.js | 2 - src/main/client/gtfsplus/reducers/index.js | 4 +- src/main/client/index.css | 15 + src/main/client/index.tpl.html | 23 - src/main/client/main.js | 17 +- .../manager/components/FeedSourceTable.js | 2 +- .../manager/components/FeedSourceViewer.js | 2 +- .../components/FeedVersionNavigator.js | 2 +- .../manager/components/FeedVersionReport.js | 2 +- .../manager/components/FeedVersionViewer.js | 2 +- .../manager/components/ProjectSettings.js | 2 +- .../manager/components/ProjectViewer.js | 2 +- .../client/manager/components/UserHomePage.js | 2 +- .../validation/GtfsValidationSummary.js | 2 +- .../validation/GtfsValidationViewer.js | 2 +- src/main/client/manager/reducers/index.js | 12 +- .../client/public/components/PublicHeader.js | 2 +- .../client/public/components/UserAccount.js | 2 +- src/main/client/signs/components/SignsList.js | 2 +- .../signs/containers/ActiveSignEditor.js | 2 - .../signs/containers/MainSignsViewer.js | 2 - src/main/client/signs/reducers/index.js | 6 +- src/main/client/signs/style.css | 8 - webpack.config.js | 62 - yarn.lock | 4958 +++++++++++------ 64 files changed, 3527 insertions(+), 1887 deletions(-) delete mode 100644 .babelrc create mode 100644 configurations/default/settings.yml create mode 100644 index.html create mode 100644 src/main/client/index.css delete mode 100644 src/main/client/index.tpl.html delete mode 100644 webpack.config.js diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 14ddaa388..000000000 --- a/.babelrc +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": ["es2015", "stage-0", "react"], - "env": { - "development": { - "plugins": ["transform-decorators-legacy", ["react-transform", { - "transforms": [{ - "transform": "react-transform-hmr", - // if you use React Native, pass "react-native" instead: - "imports": ["react"], - // this is important for Webpack HMR: - "locals": ["module"] - }] - // note: you can put more transforms into array - // this is just one of them! - }]] - } - } -} diff --git a/.gitignore b/.gitignore index e776b729a..f77e66980 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ src/main/client/config.js datatools-manager.iml config.yml config_server.yml +assets diff --git a/configurations/default/settings.yml b/configurations/default/settings.yml new file mode 100644 index 000000000..4537638a0 --- /dev/null +++ b/configurations/default/settings.yml @@ -0,0 +1,3 @@ +entries: + - src/main/client/main.js:assets/index.js + - src/main/client/index.css:assets/index.css diff --git a/gtfsplus.yml b/gtfsplus.yml index 9f15f0264..6d961ff66 100644 --- a/gtfsplus.yml +++ b/gtfsplus.yml @@ -7,8 +7,7 @@ required: true inputType: GTFS_ROUTE columnWidth: 4 - helpContent: From GTFS routes.txt file. This is necessary to maintain relationship - between routes in this file and routes in the standard GTFS routes.txt file. + helpContent: From GTFS routes.txt file. This is necessary to maintain relationship between routes in this file and routes in the standard GTFS routes.txt file. - name: realtime_enabled required: true inputType: DROPDOWN @@ -18,29 +17,21 @@ - value: '1' text: Enabled columnWidth: 2 - helpContent: Standard GTFS does not include a field to indicate real-time status - of a route. This allows the flexibility for agencies to identify routes that - may not have real-time predictions. + helpContent: Standard GTFS does not include a field to indicate real-time status of a route. This allows the flexibility for agencies to identify routes that may not have real-time predictions. - name: realtime_routename required: false inputType: TEXT columnWidth: 3 - helpContent: This field may be empty if route_long_name in GTFS routes.txt file - is provided. Otherwise, it should contain the data defined for route_long_name - in GTFS routes.txt file. + helpContent: This field may be empty if route_long_name in GTFS routes.txt file is provided. Otherwise, it should contain the data defined for route_long_name in GTFS routes.txt file. - name: realtime_routecode required: false inputType: TEXT columnWidth: 3 - helpContent: This field can be empty if real-time route code in MTC’s real-time - feed is the same as route_short_name in GTFS routes.txt file + helpContent: This field can be empty if real-time route code in MTC’s real-time feed is the same as route_short_name in GTFS routes.txt file - id: realtime_stops name: realtime_stops.txt - helpContent: This file helps map regional real-time stop identifiers with the - stop_id in stops.txt file in GTFS. This file can be omitted if GTFS file - stops.txt contains the regional real-time stop id in the optional stop_code - field. + helpContent: This file helps map regional real-time stop identifiers with the stop_id in stops.txt file in GTFS. This file can be omitted if GTFS file stops.txt contains the regional real-time stop id in the optional stop_code field. fields: - name: trip_id required: true @@ -51,8 +42,7 @@ required: true inputType: GTFS_STOP columnWidth: 5 - helpContent: From GTFS stop_times.txt file when the stop_code in standard GTFS - is different from the stop_code used in real-time feed for MTC. + helpContent: From GTFS stop_times.txt file when the stop_code in standard GTFS is different from the stop_code used in real-time feed for MTC. - name: realtime_stop_id required: true inputType: TEXT @@ -61,21 +51,7 @@ - id: directions name: directions.txt - helpContent:

Users of version 1.7 or later must use this file and ignore - realtime_directions.txt file.

-

In the standard GTFS, trip direction can be specified as ‘0’ or ‘1’ only. It - does not allow for indication of specific directions such as North, South, etc. - This file captures the direction text for each of the directions in a route.

-

Route direction information can be provided through either this directions.txt - OR realtime_directions.txt file, but not both. Users of GTFS+ version 1.7 and - above are required to provide directions.txt file and ignore realtime_directions.txt - file. Users of version 1.6 or earlier may continue to use realtime_directions.txt, - unless they voluntarily choose to upgrade to directions.txt. Only one of - directions.txt or realtime_directions.txt should be included. Also, tripheadsign - field in GTFS trips.txt file should be populated in order to distinguish patterns - within a direction and to provide destination information to passengers.

-

When directions.txt file is used, optional direction_id field in GTFS trips.txt - must be filled in.

+ helpContent:

Users of version 1.7 or later must use this file and ignore realtime_directions.txt file.

In the standard GTFS, trip direction can be specified as ‘0’ or ‘1’ only. It does not allow for indication of specific directions such as North, South, etc. This file captures the direction text for each of the directions in a route.

Route direction information can be provided through either this directions.txt OR realtime_directions.txt file, but not both. Users of GTFS+ version 1.7 and above are required to provide directions.txt file and ignore realtime_directions.txt file. Users of version 1.6 or earlier may continue to use realtime_directions.txt, unless they voluntarily choose to upgrade to directions.txt. Only one of directions.txt or realtime_directions.txt should be included. Also, tripheadsign field in GTFS trips.txt file should be populated in order to distinguish patterns within a direction and to provide destination information to passengers.

When directions.txt file is used, optional direction_id field in GTFS trips.txt must be filled in.

fields: - name: route_id required: true @@ -89,8 +65,7 @@ - value: '0' - value: '1' columnWidth: 3 - helpContent: Binary direction_id from GTFS trips.txt file. Each (route_id, - direction_id) pair can only appear once in directions.txt. + helpContent: Binary direction_id from GTFS trips.txt file. Each (route_id, direction_id) pair can only appear once in directions.txt. - name: direction required: true inputType: DROPDOWN @@ -115,9 +90,7 @@ - id: realtime_trips name: realtime_trips.txt - helpContent: This file contains real-time identifiers for trips. This file can be - omitted if the GTFS trips.txt file contains the same Trip IDs included in - real-time feed for MTC within the trip_id field. + helpContent: This file contains real-time identifiers for trips. This file can be omitted if the GTFS trips.txt file contains the same Trip IDs included in real-time feed for MTC within the trip_id field. fields: - name: trip_id required: true @@ -133,8 +106,7 @@ - id: stop_attributes name: stop_attributes.txt - helpContent: This file contains additional attributes for a stop. This file is needed - because standard GTFS does not include these fields. + helpContent: This file contains additional attributes for a stop. This file is needed because standard GTFS does not include these fields. fields: - name: stop_id required: true @@ -214,9 +186,7 @@ - id: timepoints name: timepoints.txt - helpContent: This file can be omitted if the GTFS file stop_times.txt has blank - values for arrival_time and departure_time for all the stops that are NOT time - points. This file is needed because standard GTFS does not include these fields. + helpContent: This file can be omitted if the GTFS file stop_times.txt has blank values for arrival_time and departure_time for all the stops that are NOT time points. This file is needed because standard GTFS does not include these fields. fields: - name: trip_id required: true @@ -231,8 +201,7 @@ - id: rider_categories name: rider_categories.txt - helpContent: This file lists the rider categories for fares other than the Regular - category. This file is needed because standard GTFS does not include these fields. + helpContent: This file lists the rider categories for fares other than the Regular category. This file is needed because standard GTFS does not include these fields. fields: - name: rider_category_id required: true @@ -277,24 +246,17 @@ - value: '25' text: Custom (25) columnWidth: 6 - helpContent: Unique rider category ID (agency can assign categories that do not - fall under standard categories) + helpContent: Unique rider category ID (agency can assign categories that do not fall under standard categories) - name: rider_category_description required: true inputType: TEXT maxLength: 256 columnWidth: 6 - helpContent: Rider category as it should appear on 511.org, such as Child (ages 5- - 11), Seniors (Ages 62 & Up) + helpContent: Rider category as it should appear on 511.org, such as Child (ages 5-11), Seniors (Ages 62 & Up) - id: fare_rider_categories name: fare_rider_categories.txt - helpContent: This file specifies attributes for the fares for rider categories. GTFS - file fare_attributes.txt contains the fares for the Regular rider category. Fares - for other rider categories such as Child, Senior, etc will be provided in this plus - file fare_rider_categories.txt. The combination of fare_id and rider_category_id - should be unique in this file. This file is needed because standard GTFS does not - include these fields. + helpContent: This file specifies attributes for the fares for rider categories. GTFS file fare_attributes.txt contains the fares for the Regular rider category. Fares for other rider categories such as Child, Senior, etc will be provided in this plus file fare_rider_categories.txt. The combination of fare_id and rider_category_id should be unique in this file. This file is needed because standard GTFS does not include these fields. fields: - name: fare_id required: true @@ -351,8 +313,7 @@ - id: calendar_attributes name: calendar_attributes.txt - helpContent: This file contains calendar attributes. This file is needed because - standard GTFS does not include these fields. + helpContent: This file contains calendar attributes. This file is needed because standard GTFS does not include these fields. fields: - name: service_id required: true @@ -362,13 +323,11 @@ required: true inputType: TEXT maxLength: 30 - helpContent: Description of the service, as it should appear on 511.org such as - Weekdays, Sunday/Holiday + helpContent: Description of the service, as it should appear on 511.org such as Weekdays, Sunday/Holiday - id: farezone_attributes name: farezone_attributes.txt - helpContent: This file contains fare zone attributes. This file is needed because - standard GTFS does not include these fields. + helpContent: This file contains fare zone attributes. This file is needed because standard GTFS does not include these fields. fields: - name: zone_id required: true @@ -378,5 +337,4 @@ required: true inputType: TEXT maxLength: 35 - helpContent: Public name of the fare zone, as it should appear on 511.org such as - EastBay, WestBay, etc + helpContent: Public name of the fare zone, as it should appear on 511.org such as EastBay, WestBay, etc diff --git a/index.html b/index.html new file mode 100644 index 000000000..7398f9bdf --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Catalogue + + + + +
+ + + diff --git a/package.json b/package.json index 8ea24ac7a..80428a97d 100644 --- a/package.json +++ b/package.json @@ -10,24 +10,21 @@ "author": "Conveyal LLC", "license": "MIT", "scripts": { - "build": "webpack", - "build-min": "WEBPACK_MINIFY=true npm run build", - "dev-server": "WEBPACK_DEV_SERVER=true webpack-dev-server", - "dev-server-min": "WEBPACK_MINIFY=true npm run dev-server", - "prestart": "yarn" + "build": "mastarm build --env production", + "prestart": "yarn", + "start": "mastarm build --serve", + "test": "mastarm lint \"src/main/client/**/*.js\"" }, "dependencies": { + "@conveyal/woonerf": "^0.2.0", "array-unique": "^0.2.1", - "babel-plugin-transform-regenerator": "^6.9.0", "body-parser": "^1.14.2", "bootstrap": "^3.3.7", "change-case": "^3.0.0", "clone": "^1.0.2", "cors": "^2.3.1", - "css-loader": "^0.23.1", "express": "^4.13.3", "express-jwt": "^3.3.0", - "file-loader": "^0.8.5", "gravatar": "^1.5.2", "history": "^2.0.1", "isomorphic-fetch": "^2.2.1", @@ -56,7 +53,6 @@ "react-dnd-html5-backend": "^2.1.2", "react-dom": "^15.2.1", "react-dropzone": "^3.5.3", - "react-fa": "^4.1.2", "react-helmet": "^3.1.0", "react-leaflet": "^0.12.1", "react-leaflet-draw": "github:alex3165/react-leaflet-draw", @@ -85,46 +81,20 @@ "turf-linestring": "^1.0.2", "turf-point": "^2.0.1", "turf-point-on-line": "^3.0.11", - "url-loader": "^0.5.7", - "validator": "^5.5.0", - "yaml-loader": "^0.1.0" + "validator": "^5.5.0" }, "devDependencies": { "autoprefixer": "^6.0.3", - "babel-cli": "^6.4.0", - "babel-core": "^6.3.26", - "babel-eslint": "^4.1.6", - "babel-loader": "^6.2.1", - "babel-plugin-react-transform": "^2.0.0", - "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-plugin-transform-regenerator": "^6.9.0", - "babel-plugin-transform-runtime": "^6.9.0", - "babel-polyfill": "^6.9.1", - "babel-preset-es2015": "^6.3.13", - "babel-preset-react": "^6.3.13", - "babel-preset-stage-0": "^6.5.0", "chai": "^3.2.0", - "css-loader": "^0.23.1", - "extract-text-webpack-plugin": "^0.8.2", - "html-webpack-plugin": "^1.6.1", "jsdom": "^6.5.1", - "json-loader": "^0.5.3", + "mastarm": "^2.0.0", "mocha": "^2.3.3", "mocha-jsdom": "^1.0.0", - "postcss-loader": "^0.6.0", "react-addons-perf": "^15.2.1", "react-addons-test-utils": "^0.14.3", - "react-transform-hmr": "^1.0.0", "rimraf": "^2.4.3", "sinon": "^1.16.1", - "sinon-chai": "^2.8.0", - "stats-webpack-plugin": "^0.2.1", - "style-loader": "^0.13.0", - "webpack": "^1.13.0", - "webpack-dev-middleware": "^1.2.0", - "webpack-dev-server": "^1.16.2", - "webpack-hot-middleware": "^2.2.0", - "webpack-visualizer-plugin": "^0.1.5" + "sinon-chai": "^2.8.0" }, "standard": { "parser": "babel-eslint" diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index 031158b06..814fb7053 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react' import { Grid, Row, Col, Panel, ListGroup, ListGroupItem, Button } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' import Helmet from 'react-helmet' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import ManagerPage from '../../common/components/ManagerPage' import UserList from './UserList' diff --git a/src/main/client/admin/components/UserList.js b/src/main/client/admin/components/UserList.js index 47e41ad0b..7e944ba5d 100644 --- a/src/main/client/admin/components/UserList.js +++ b/src/main/client/admin/components/UserList.js @@ -1,7 +1,7 @@ import React, { PropTypes, Component} from 'react' import ReactDOM from 'react-dom' import { Panel, Row, Col, Button, Label, ButtonGroup, InputGroup, FormControl, Glyphicon, Image, ListGroup, ListGroupItem } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import CreateUser from './CreateUser' import UserSettings from './UserSettings' diff --git a/src/main/client/admin/reducers/index.js b/src/main/client/admin/reducers/index.js index 8022ef2d5..cf7d65a1a 100644 --- a/src/main/client/admin/reducers/index.js +++ b/src/main/client/admin/reducers/index.js @@ -1 +1,3 @@ -export admin from './admin' +module.exports = { + admin: require('./admin') +} diff --git a/src/main/client/alerts/components/AffectedEntity.js b/src/main/client/alerts/components/AffectedEntity.js index 30d7b4c05..df2be80cf 100644 --- a/src/main/client/alerts/components/AffectedEntity.js +++ b/src/main/client/alerts/components/AffectedEntity.js @@ -1,7 +1,7 @@ import React from 'react' import { ListGroupItem, Row, Col, ButtonGroup, Button, Glyphicon, FormControl, Label, Collapse } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import GtfsSearch from '../../gtfs/components/gtfssearch' import modes from '../modes' diff --git a/src/main/client/alerts/components/AlertsList.js b/src/main/client/alerts/components/AlertsList.js index 6cfeb4485..c69f2e35f 100644 --- a/src/main/client/alerts/components/AlertsList.js +++ b/src/main/client/alerts/components/AlertsList.js @@ -1,6 +1,6 @@ import React, { PropTypes, Component } from 'react' import { Row, Col, ButtonGroup, Button, FormControl, FormGroup, Badge, ControlLabel } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { sentence as toSentenceCase } from 'change-case' import AlertPreview from './AlertPreview' diff --git a/src/main/client/alerts/components/AlertsViewer.js b/src/main/client/alerts/components/AlertsViewer.js index c3cf69aaa..059bcff5b 100644 --- a/src/main/client/alerts/components/AlertsViewer.js +++ b/src/main/client/alerts/components/AlertsViewer.js @@ -1,6 +1,6 @@ import React from 'react' import Helmet from 'react-helmet' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { Grid, Row, Col, Button } from 'react-bootstrap' import ManagerPage from '../../common/components/ManagerPage' diff --git a/src/main/client/alerts/containers/ActiveAlertEditor.js b/src/main/client/alerts/containers/ActiveAlertEditor.js index 857009f11..504b17551 100644 --- a/src/main/client/alerts/containers/ActiveAlertEditor.js +++ b/src/main/client/alerts/containers/ActiveAlertEditor.js @@ -11,8 +11,6 @@ import AlertEditor from '../components/AlertEditor' import { browserHistory } from 'react-router' import { getFeedsForPermission } from '../../common/util/permissions' -import '../style.css' - const agencyCompare = function(a, b) { if (a.name < b.name) return -1; diff --git a/src/main/client/alerts/containers/MainAlertsViewer.js b/src/main/client/alerts/containers/MainAlertsViewer.js index 7a0a71962..0697c3743 100644 --- a/src/main/client/alerts/containers/MainAlertsViewer.js +++ b/src/main/client/alerts/containers/MainAlertsViewer.js @@ -6,8 +6,6 @@ import AlertsViewer from '../components/AlertsViewer' import { createAlert } from '../actions/alerts' import { fetchProjects } from '../actions/projects' -import '../style.css' - const mapStateToProps = (state, ownProps) => { return { activeFeeds: state.gtfs.filter.activeFeeds, diff --git a/src/main/client/alerts/reducers/index.js b/src/main/client/alerts/reducers/index.js index 80af04fff..c330302c6 100644 --- a/src/main/client/alerts/reducers/index.js +++ b/src/main/client/alerts/reducers/index.js @@ -1,2 +1,4 @@ -export activeAlert from './activeAlert' -export alerts from './alerts' +module.exports = { + activeAlert: require('./activeAlert'), + alerts: require('./alerts') +} diff --git a/src/main/client/alerts/style.css b/src/main/client/alerts/style.css index dd2423904..71143ed64 100644 --- a/src/main/client/alerts/style.css +++ b/src/main/client/alerts/style.css @@ -1,5 +1,3 @@ -@import url('~react-select/dist/react-select.css'); -@import url('~react-bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css'); .leaflet-top, .leaflet-bottom { diff --git a/src/main/client/common/components/DatatoolsNavbar.js b/src/main/client/common/components/DatatoolsNavbar.js index c9855977c..d24c5add9 100644 --- a/src/main/client/common/components/DatatoolsNavbar.js +++ b/src/main/client/common/components/DatatoolsNavbar.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import { Navbar, Nav, NavItem, NavDropdown, MenuItem, Glyphicon, OverlayTrigger, Popover, ProgressBar, Button, Badge } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' import { browserHistory, Link } from 'react-router' -import { Icon } from 'react-fa' +import { Icon } from '@conveyal/woonerf' import Breadcrumbs from './Breadcrumbs' import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../util/config' diff --git a/src/main/client/common/components/JobMonitor.js b/src/main/client/common/components/JobMonitor.js index e231390cd..eaa36efbf 100644 --- a/src/main/client/common/components/JobMonitor.js +++ b/src/main/client/common/components/JobMonitor.js @@ -1,6 +1,6 @@ import React, { PropTypes, Component } from 'react' import { ProgressBar, Button } from 'react-bootstrap' -import { Icon } from 'react-fa' +import { Icon } from '@conveyal/woonerf' import truncate from 'truncate' import SidebarPopover from './SidebarPopover' diff --git a/src/main/client/common/components/Loading.js b/src/main/client/common/components/Loading.js index a8f122270..b054515a6 100644 --- a/src/main/client/common/components/Loading.js +++ b/src/main/client/common/components/Loading.js @@ -1,6 +1,6 @@ import React from 'react' import { Row, Col } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' export default class Loading extends React.Component { diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 55856c5b0..ae3926181 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -2,15 +2,16 @@ import React, { Component, PropTypes } from 'react' import { Navbar, Button, ButtonToolbar, Checkbox } from 'react-bootstrap' import { browserHistory } from 'react-router' import { Link } from 'react-router' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import SidebarNavItem from './SidebarNavItem' import SidebarPopover from './SidebarPopover' import JobMonitor from './JobMonitor' import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' -import icon from '../../assets/application_icon.png' -import longIcon from '../../assets/application_logo.png' +// TODO: Fix PNG import +const icon = "" // import icon from '../../assets/application_icon.png' +const longIcon = "" // import longIcon from '../../assets/application_logo.png' export default class Sidebar extends Component { diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index c9a7a8948..c1b35c81b 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -1,5 +1,5 @@ import React, { Component, PropTypes } from 'react' -import { Icon } from 'react-fa' +import { Icon } from '@conveyal/woonerf' import { Link } from 'react-router' import { Tooltip, OverlayTrigger } from 'react-bootstrap' diff --git a/src/main/client/common/containers/App.js b/src/main/client/common/containers/App.js index 676b46b4e..a8f3f6e58 100644 --- a/src/main/client/common/containers/App.js +++ b/src/main/client/common/containers/App.js @@ -6,7 +6,6 @@ import { checkExistingLogin, login } from '../../manager/actions/user' import { checkJobStatus } from '../../manager/actions/status' import { isModuleEnabled } from '../util/config' -// import NoAccessScreen from '../components/NoAccessScreen' import ActiveFeedSourceViewer from '../../manager/containers/ActiveFeedSourceViewer' import ActiveProjectViewer from '../../manager/containers/ActiveProjectViewer' import ActiveProjectsList from '../../manager/containers/ActiveProjectsList' @@ -26,13 +25,6 @@ import ActiveGtfsPlusEditor from '../../gtfsplus/containers/ActiveGtfsPlusEditor import ActiveGtfsEditor from '../../editor/containers/ActiveGtfsEditor' import ActiveGtfsTableEditor from '../../editor/containers/ActiveGtfsTableEditor' -// import ActiveGtfsValidationMap from '../../manager/containers/validation/ActiveGtfsValidationMap' -// import ActiveGtfsValidationExplorer from '../../manager/containers/validation/ActiveGtfsValidationExplorer' - -require('../style.css') - -// import { UserIsAuthenticated, UserIsAdmin } from '../util/util' - class App extends Component { static propTypes = { checkExistingLogin: PropTypes.func, diff --git a/src/main/client/common/containers/StarButton.js b/src/main/client/common/containers/StarButton.js index 0d03d239f..67d373a7b 100644 --- a/src/main/client/common/containers/StarButton.js +++ b/src/main/client/common/containers/StarButton.js @@ -2,7 +2,7 @@ import React from 'react' import { Button } from 'react-bootstrap' import { updateStar } from '../../manager/actions/user' import { connect } from 'react-redux' -import { Icon } from 'react-fa' +import { Icon } from '@conveyal/woonerf' import { getComponentMessages, getMessage } from '../util/config' diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index 3ec79fa40..8d3da0926 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -5,9 +5,8 @@ import { isTokenExpired } from '../util/jwtHelper' import UserPermissions from './UserPermissions' import { getConfigProperty } from '../util/config' -// import '../../assets/mtc_manager_logo.png' - -import icon from '../../assets/application_logo.png' +// TODO: Fix PNG import +const icon = "" // import icon from '../../assets/application_logo.png' export default class Auth0Manager { diff --git a/src/main/client/editor/components/CalendarList.js b/src/main/client/editor/components/CalendarList.js index f6b7a3331..156b88859 100644 --- a/src/main/client/editor/components/CalendarList.js +++ b/src/main/client/editor/components/CalendarList.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Table, ListGroup, ListGroupItem, Button, ButtonToolbar, Nav, NavItem } from 'react-bootstrap' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index a64fb2ccb..133d8ee3d 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import { Panel, Row, Col, Table, ButtonToolbar, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' import { browserHistory } from 'react-router' import moment from 'moment' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import ConfirmModal from '../../common/components/ConfirmModal' import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 065d552d3..7fceea4dd 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -3,7 +3,7 @@ import { Map, Marker, Popup, Polyline, TileLayer, FeatureGroup, ZoomControl, Lay import { divIcon, Browser } from 'leaflet' import { Button, Dropdown, OverlayTrigger, Tooltip, Row, Col, ButtonGroup, MenuItem, SplitButton, FormGroup, ControlLabel } from 'react-bootstrap' import { shallowEqual } from 'react-pure-render' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import ll from 'lonlng' import lineString from 'turf-linestring' import bearing from 'turf-bearing' diff --git a/src/main/client/editor/components/EditorSidebar.js b/src/main/client/editor/components/EditorSidebar.js index 70541c799..733019b03 100644 --- a/src/main/client/editor/components/EditorSidebar.js +++ b/src/main/client/editor/components/EditorSidebar.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react' import { Nav, NavItem, OverlayTrigger, Tooltip } from 'react-bootstrap' import { browserHistory } from 'react-router' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' import { gtfsIcons } from '../util/gtfs' diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index e705c5ea7..a37cedcbd 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Checkbox, Radio, Button, ButtonToolbar, Form, FormControl, FormGroup, ControlLabel, Nav, NavItem, Tooltip, OverlayTrigger, Panel } from 'react-bootstrap' -import {Icon, IconStack} from 'react-fa' +import {Icon, IconStack} from '@conveyal/woonerf' import reactCSS from 'reactcss' import validator from 'validator' import { shallowEqual } from 'react-pure-render' diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index eb8cd9440..90b064d3e 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -1,9 +1,8 @@ import React, {Component, PropTypes} from 'react' import { Button, ButtonToolbar, Nav, NavItem, Tooltip, OverlayTrigger } from 'react-bootstrap' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { FlexTable, FlexColumn } from 'react-virtualized' import { shallowEqual } from 'react-pure-render' -import 'react-virtualized/styles.css' import VirtualizedEntitySelect from './VirtualizedEntitySelect' import GtfsTable from './GtfsTable' diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index fb1992d24..9a90cf4c2 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Button, ButtonGroup, DropdownButton, Dropdown, MenuItem, Tooltip, OverlayTrigger } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { browserHistory } from 'react-router' import CreateSnapshotModal from './CreateSnapshotModal' diff --git a/src/main/client/editor/components/PatternStopCard.js b/src/main/client/editor/components/PatternStopCard.js index 27ade7519..57fc46397 100644 --- a/src/main/client/editor/components/PatternStopCard.js +++ b/src/main/client/editor/components/PatternStopCard.js @@ -1,7 +1,7 @@ import React, { Component, PropTypes } from 'react' import { DragSource, DropTarget } from 'react-dnd' import { Row, Col, Button, Collapse, FormGroup, ControlLabel, Checkbox } from 'react-bootstrap' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { getEntityName } from '../util/gtfs' import MinuteSecondInput from './MinuteSecondInput' @@ -43,14 +43,7 @@ const cardTarget = { } } -@DropTarget('card', cardTarget, connect => ({ - connectDropTarget: connect.dropTarget() -})) -@DragSource('card', cardSource, (connect, monitor) => ({ - connectDragSource: connect.dragSource(), - isDragging: monitor.isDragging() -})) -export default class PatternStopCard extends Component { +class PatternStopCard extends Component { static propTypes = { connectDragSource: PropTypes.func.isRequired, connectDropTarget: PropTypes.func.isRequired, @@ -200,3 +193,8 @@ export default class PatternStopCard extends Component { )) } } + +const dropTargetCollect = (connect) => ({connectDropTarget: connect.dropTarget()}) +const dragSourceCollect = (connect, monitor) => ({connectDragSource: connect.dragSource(), isDragging: monitor.isDragging()}) + +export default DropTarget('card', cardTarget, dropTargetCollect)(DragSource('card', cardSource, dragSourceConnect)(PatternStopCard)) diff --git a/src/main/client/editor/components/PatternStopContainer.js b/src/main/client/editor/components/PatternStopContainer.js index c560c56a1..64e9f0aaf 100644 --- a/src/main/client/editor/components/PatternStopContainer.js +++ b/src/main/client/editor/components/PatternStopContainer.js @@ -12,11 +12,8 @@ const cardTarget = { drop () { } } -@DragDropContext(HTML5Backend) -@DropTarget('card', cardTarget, connect => ({ - connectDropTarget: connect.dropTarget() -})) -export default class PatternStopContainer extends Component { + +class PatternStopContainer extends Component { static propTypes = { connectDropTarget: PropTypes.func.isRequired, activePattern: PropTypes.object.isRequired, @@ -113,3 +110,6 @@ export default class PatternStopContainer extends Component { ) } } + +// TODO: Verify correct order +export default DrapDropContext(HTML5Backend)(DropTarget('card', cardTarget, (connect) => ({connectDropTarget: connect.dropTarget()}))(PatternStopContainer)) diff --git a/src/main/client/editor/components/RouteEditor.js b/src/main/client/editor/components/RouteEditor.js index 5968d9f0f..20a5ee27b 100644 --- a/src/main/client/editor/components/RouteEditor.js +++ b/src/main/client/editor/components/RouteEditor.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Table, Button, ButtonToolbar } from 'react-bootstrap' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' diff --git a/src/main/client/editor/components/TimetableEditor.js b/src/main/client/editor/components/TimetableEditor.js index f46833c65..711c51fec 100644 --- a/src/main/client/editor/components/TimetableEditor.js +++ b/src/main/client/editor/components/TimetableEditor.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { InputGroup, Checkbox, Nav, NavItem, NavDropdown, MenuItem, Button, Form, FormControl } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import clone from 'clone' import ReactDOM from 'react-dom' import moment from 'moment' diff --git a/src/main/client/editor/components/TripPatternList.js b/src/main/client/editor/components/TripPatternList.js index a3b893fb2..da3408b27 100644 --- a/src/main/client/editor/components/TripPatternList.js +++ b/src/main/client/editor/components/TripPatternList.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Table, Button, ButtonGroup, Alert, Checkbox, DropdownButton, MenuItem, ButtonToolbar, Collapse, FormGroup, OverlayTrigger, Tooltip, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' -import {Icon} from 'react-fa' +import {Icon} from '@conveyal/woonerf' import { sentence as toSentenceCase } from 'change-case' import Rcslider from 'rc-slider' @@ -14,8 +14,6 @@ import VirtualizedEntitySelect from './VirtualizedEntitySelect' import { polyline as getPolyline, getSegment } from '../../scenario-editor/utils/valhalla' import ll from 'lonlng' -import 'rc-slider/assets/index.css' - const DEFAULT_SPEED = 20 // km/hr export default class TripPatternList extends Component { diff --git a/src/main/client/editor/components/VirtualizedEntitySelect.js b/src/main/client/editor/components/VirtualizedEntitySelect.js index 3304350c0..f584480f0 100644 --- a/src/main/client/editor/components/VirtualizedEntitySelect.js +++ b/src/main/client/editor/components/VirtualizedEntitySelect.js @@ -1,8 +1,5 @@ import React, {Component, PropTypes} from 'react' import VirtualizedSelect from 'react-virtualized-select' -import 'react-virtualized/styles.css' -import 'react-select/dist/react-select.css' -import 'react-virtualized-select/styles.css' import { getEntityName } from '../util/gtfs' diff --git a/src/main/client/editor/reducers/index.js b/src/main/client/editor/reducers/index.js index 7735e1f82..aa8561b51 100644 --- a/src/main/client/editor/reducers/index.js +++ b/src/main/client/editor/reducers/index.js @@ -1 +1,3 @@ -export editor from './editor' +module.exports = { + editor: require('./editor') +} diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index fddaa46b6..780849ff5 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -5,7 +5,7 @@ import { Button, FormControl, ControlLabel } from 'react-bootstrap' import { shallowEqual } from 'react-pure-render' import { divIcon, Browser } from 'leaflet' import { Map, Marker, Popup, TileLayer, GeoJson, FeatureGroup, Rectangle } from 'react-leaflet' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { getFeed, getFeedId } from '../../common/util/modules' import { getFeedsBounds } from '../../common/util/geo' diff --git a/src/main/client/gtfs/reducers/index.js b/src/main/client/gtfs/reducers/index.js index 353d59d30..808fd0276 100644 --- a/src/main/client/gtfs/reducers/index.js +++ b/src/main/client/gtfs/reducers/index.js @@ -2,7 +2,6 @@ import { combineReducers } from 'redux' import filter from './filter' import feed from './feed' -// import pageLayout from './pageLayout' import patterns from './patterns' import routes from './routes' import stops from './stops' @@ -10,7 +9,6 @@ import stops from './stops' export default combineReducers({ filter, feed, - // pageLayout, patterns, routes, stops diff --git a/src/main/client/gtfsplus/reducers/index.js b/src/main/client/gtfsplus/reducers/index.js index 5bc3ba0d6..f24d524e9 100644 --- a/src/main/client/gtfsplus/reducers/index.js +++ b/src/main/client/gtfsplus/reducers/index.js @@ -1 +1,3 @@ -export gtfsplus from './gtfsplus' +module.exports = { + gtfsplus: require('./gtfsplus') +} diff --git a/src/main/client/index.css b/src/main/client/index.css new file mode 100644 index 000000000..ce2b0d249 --- /dev/null +++ b/src/main/client/index.css @@ -0,0 +1,15 @@ + +@import url(node_modules/leaflet/dist/leaflet.css); +@import url(node_modules/leaflet-draw/dist/leaflet.draw.css); + +@import url(node_modules/bootstrap/dist/css/bootstrap.min.css); +@import url(node_modules/react-bootstrap-table/dist/react-bootstrap-table.min.css); +@import url(node_modules/react-bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css); + +@import url(node_modules/react-select/dist/react-select.css); +@import url(node_modules/react-virtualized/styles.css); +@import url(node_modules/react-virtualized-select/styles.css); +@import url(node_modules/rc-slider/assets/index.css); + +@import url(src/main/client/common/style.css); +@import url(src/main/client/alerts/style.css); diff --git a/src/main/client/index.tpl.html b/src/main/client/index.tpl.html deleted file mode 100644 index abca13684..000000000 --- a/src/main/client/index.tpl.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - -
- - diff --git a/src/main/client/main.js b/src/main/client/main.js index a0ecd3804..4e42d9b2d 100644 --- a/src/main/client/main.js +++ b/src/main/client/main.js @@ -5,24 +5,23 @@ import { createStore, applyMiddleware, combineReducers } from 'redux' import thunkMiddleware from 'redux-thunk' import { browserHistory } from 'react-router' import { syncHistoryWithStore, routerReducer } from 'react-router-redux' -// import promise from 'redux-promise' import createLogger from 'redux-logger' import App from './common/containers/App' -import config from 'json!yaml!../../../config.yml' +import config from '../../../config.yml' if (config.modules.gtfsplus && config.modules.gtfsplus.enabled) { - config.modules.gtfsplus.spec = require('json!yaml!../../../gtfsplus.yml') + config.modules.gtfsplus.spec = require('../../../gtfsplus.yml') } -config.modules.editor.spec = require('json!yaml!../../../gtfs.yml') +config.modules.editor.spec = require('../../../gtfs.yml') -// function to require all lang files in i18n dir -function requireAll (requireContext) { - return requireContext.keys().map(requireContext) +var lang = { + english: require('../../../i18n/english.yml'), + espanol: require('../../../i18n/espanol.yml'), + francais: require('../../../i18n/francais.yml') } -// requires and returns all modules that match -var lang = requireAll(require.context('json!yaml!../../../i18n', true, /.yml/)) + // is an array containing all the matching modules config.messages = {} config.messages.all = lang diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index 162dbf575..b56f80499 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -4,7 +4,7 @@ import moment from 'moment' import { Button, Table, Checkbox, Glyphicon, Dropdown, MenuItem, Panel, ListGroupItem, ListGroup } from 'react-bootstrap' import { browserHistory, Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' -import { Icon, IconStack } from 'react-fa' +import { Icon, IconStack } from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' import EditableTextField from '../../common/components/EditableTextField' diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index ed09ae0fb..9db3cf6f3 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' import { LinkContainer } from 'react-router-bootstrap' diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index d597e390a..63770253a 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react' import { Row, Col, ButtonGroup, ButtonToolbar, DropdownButton, MenuItem, Button, Glyphicon } from 'react-bootstrap' import { browserHistory } from 'react-router' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { isModuleEnabled, getComponentMessages, getMessage } from '../../common/util/config' import { isValidZipFile } from '../../common/util/util' diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index 2a507cf2e..48eb15a1b 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react' import { Row, Col, Image, Button, Panel, ControlLabel, Label, Tabs, Tab, Glyphicon, FormControl, ButtonGroup, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import numeral from 'numeral' import Rcslider from 'rc-slider' import EditableTextField from '../../common/components/EditableTextField' diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index 316467194..d7ed6c2ed 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import { Row, Col, Button, Panel, Label, Glyphicon, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' import { LinkContainer } from 'react-router-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import GtfsValidationViewer from './validation/GtfsValidationViewer' import GtfsValidationExplorer from './validation/GtfsValidationExplorer' diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index 144e3d6db..d6775c68a 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -6,7 +6,7 @@ import DateTimeField from 'react-bootstrap-datetimepicker' import update from 'react-addons-update' import { shallowEqual } from 'react-pure-render' import { LinkContainer } from 'react-router-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { Row, Col, Button, Panel, Glyphicon, Form, Tabs, Tab, Radio, Checkbox, FormGroup, InputGroup, ControlLabel, FormControl, ListGroup, ListGroupItem } from 'react-bootstrap' import TimezoneSelect from '../../common/components/TimezoneSelect' diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index 8cf1ea213..9f7139062 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -3,7 +3,7 @@ import Helmet from 'react-helmet' import moment from 'moment' import { Tabs, Tab, Grid, Row, Label, Col, Button, InputGroup, Table, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' import { sentence as toSentenceCase } from 'change-case' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' import { shallowEqual } from 'react-pure-render' diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index 3cd69723a..d780eb6f7 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Grid, Row, Col, Panel, Button, ButtonToolbar, ButtonGroup, Jumbotron, Badge, FormControl, ListGroup, ListGroupItem, DropdownButton, MenuItem } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' import moment from 'moment' diff --git a/src/main/client/manager/components/validation/GtfsValidationSummary.js b/src/main/client/manager/components/validation/GtfsValidationSummary.js index 492f23e8d..c3fb4734c 100644 --- a/src/main/client/manager/components/validation/GtfsValidationSummary.js +++ b/src/main/client/manager/components/validation/GtfsValidationSummary.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react' import { Table, Glyphicon, Button } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' // import { connect } from 'react-redux' import { LinkContainer } from 'react-router-bootstrap' diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index 4caa2a235..80c257b64 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -1,7 +1,7 @@ import React from 'react' import { Panel, Table, Glyphicon, Button, Badge } from 'react-bootstrap' import { browserHistory } from 'react-router' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { isModuleEnabled, isExtensionEnabled, getComponentMessages, getMessage } from '../../../common/util/config' diff --git a/src/main/client/manager/reducers/index.js b/src/main/client/manager/reducers/index.js index abd0c8a9b..00822a3bd 100644 --- a/src/main/client/manager/reducers/index.js +++ b/src/main/client/manager/reducers/index.js @@ -1,5 +1,7 @@ -export user from './user' -export projects from './projects' -export status from './status' -export languages from './languages' -export ui from './ui' +module.exports = { + user: require('./user'), + projects: require('./projects'), + status: require('./status'), + languages: require('./languages'), + ui: require('./ui') +} diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index a00c34d44..088e10cf6 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { Grid, Row, Col, Button, Glyphicon, ButtonToolbar, DropdownButton, MenuItem } from 'react-bootstrap' import { browserHistory } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 26396d58d..0f64dca44 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -1,7 +1,7 @@ import React, { Component, PropTypes } from 'react' import { Grid, Row, Col, Button, Panel, Checkbox, ListGroup, ListGroupItem, ControlLabel } from 'react-bootstrap' import { Link } from 'react-router' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { LinkContainer } from 'react-router-bootstrap' import EditableTextField from '../../common/components/EditableTextField' diff --git a/src/main/client/signs/components/SignsList.js b/src/main/client/signs/components/SignsList.js index b380fa672..50b160f3c 100644 --- a/src/main/client/signs/components/SignsList.js +++ b/src/main/client/signs/components/SignsList.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react' import { Row, ButtonGroup, Button, FormControl, FormGroup, Badge } from 'react-bootstrap' -import Icon from 'react-fa' +import Icon from '@conveyal/woonerf' import { sentence as toSentenceCase } from 'change-case' import SignPreview from './SignPreview' diff --git a/src/main/client/signs/containers/ActiveSignEditor.js b/src/main/client/signs/containers/ActiveSignEditor.js index 664f380a5..53bc42641 100644 --- a/src/main/client/signs/containers/ActiveSignEditor.js +++ b/src/main/client/signs/containers/ActiveSignEditor.js @@ -12,8 +12,6 @@ import SignEditor from '../components/SignEditor' import { browserHistory } from 'react-router' import { getFeedsForPermission } from '../../common/util/permissions' -import '../style.css' - const agencyCompare = function(a, b) { if (a.name < b.name) return -1; diff --git a/src/main/client/signs/containers/MainSignsViewer.js b/src/main/client/signs/containers/MainSignsViewer.js index a83f3504c..9dadcc5db 100644 --- a/src/main/client/signs/containers/MainSignsViewer.js +++ b/src/main/client/signs/containers/MainSignsViewer.js @@ -6,8 +6,6 @@ import SignsViewer from '../components/SignsViewer' import { createSign } from '../actions/signs' import { fetchProjects } from '../actions/projects' -import '../style.css' - const mapStateToProps = (state, ownProps) => { return { activeFeeds: state.gtfs.filter.activeFeeds, diff --git a/src/main/client/signs/reducers/index.js b/src/main/client/signs/reducers/index.js index 24270e75b..061d4c844 100644 --- a/src/main/client/signs/reducers/index.js +++ b/src/main/client/signs/reducers/index.js @@ -1,2 +1,4 @@ -export activeSign from './activeSign' -export signs from './signs' +module.exports = { + activeSign: require('./activeSign'), + signs: require('./signs') +} diff --git a/src/main/client/signs/style.css b/src/main/client/signs/style.css index fce240229..19a2e3d09 100644 --- a/src/main/client/signs/style.css +++ b/src/main/client/signs/style.css @@ -1,11 +1,3 @@ -@import url(~react-select/dist/react-select.css); -@import url(~bootstrap/dist/css/bootstrap.min.css); -@import url(~react-bootstrap-table/dist/react-bootstrap-table.min.css); -@import url(~leaflet-draw/dist/leaflet.draw.css); -@import url(~react-virtualized/styles.css); -@import url(~leaflet/dist/leaflet.css); -@import url(~bootstrap/dist/css/bootstrap.min.css); - .leaflet-top, .leaflet-bottom { z-index: 0; diff --git a/webpack.config.js b/webpack.config.js deleted file mode 100644 index f83901398..000000000 --- a/webpack.config.js +++ /dev/null @@ -1,62 +0,0 @@ -'use strict' - -var path = require('path') -var webpack = require('webpack') -var HtmlWebpackPlugin = require('html-webpack-plugin') - -module.exports = { - devtool: 'eval-source-map', - entry: [ - 'babel-polyfill', - path.join(__dirname, 'src/main/client/main.js') - ], - output: { - path: path.join(__dirname, 'src/main/resources/public/'), - filename: '[name].js', - publicPath: '/' - }, - plugins: [ - new HtmlWebpackPlugin({ - template: 'src/main/client/index.tpl.html', - inject: 'body', - filename: 'index.html' - }), - new webpack.optimize.OccurenceOrderPlugin(), - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin(), - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify('development') - }) - ], - module: { - loaders: [ - { - test: /\.js?$/, - exclude: /node_modules/, - loader: 'babel' - }, { - test: /\.json?$/, - loader: 'json' - }, { - test: /\.yml$/, - loader: 'yaml' - // }, { - // test: /\.css$/, - // loader: 'style!css?modules&localIdentName=[name]---[local]---[hash:base64:5]' - }, - { - test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/, - // test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, - // loader: 'url-loader?limit=10000&mimetype=application/font-woff&name=./assets/fonts/[hash].[ext]' - loader: 'url-loader?limit=10000&mimetype=application/font-woff&name=./assets/fonts/[hash].[ext]' - }, - { - test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, - loader: 'file-loader?name=./assets/img/[hash].[ext]' - }, - // css-loader - { test: /\.css$/, loader: 'style-loader!css-loader' }, - { test: /\.(png|jpg)$/, loader: 'url-loader?limit=10000&name=./assets/img/[hash].[ext]'}, - ] - } -} diff --git a/yarn.lock b/yarn.lock index dc88a8b32..912fc4461 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,6 +1,29 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 -abbrev@1: +"@conveyal/woonerf": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@conveyal/woonerf/-/woonerf-0.2.0.tgz#53d75d549152082b8ac7d1ab1e980b1e583c72a5" + dependencies: + auth0-js "^7.3.0" + auth0-lock "^10.5.1" + isomorphic-fetch "^2.2.1" + lodash.isequal "^4.4.0" + lodash.isobject "^3.0.2" + lodash.merge "^4.6.0" + react "^15.3.2" + react-addons-perf "^15.3.2" + react-dom "^15.3.2" + react-router "^3.0.0" + react-router-redux "^4.0.6" + redux "^3.6.0" + redux-actions "^0.13.0" + redux-logger "^2.7.4" + +abab@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" + +abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -17,24 +40,45 @@ acorn-globals@^1.0.4: dependencies: acorn "^2.1.0" -acorn-to-esprima@^1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/acorn-to-esprima/-/acorn-to-esprima-1.0.7.tgz#9436259760098f9ead9b9da2242fab2f4850281b" +acorn-jsx@^3.0.0, acorn-jsx@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^1.0.3: + version "1.2.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-1.2.2.tgz#c8ce27de0acc76d896d2b1fad3df588d9e82f014" -acorn@^2.1.0, acorn@^2.4.0: +acorn@^2.1.0, acorn@^2.4.0, acorn@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" -acorn@^3.0.0, acorn@^3.1.0: +acorn@^3.0.4, acorn@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" +acorn@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.3.tgz#1a3e850b428e73ba6b09d1cc527f5aaad4d03ef1" + add-dom-event-listener@1.x: version "1.0.1" resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.1.tgz#70e74d0692b27108f9740554e0fa80a4683c2eb2" dependencies: object-assign "4.x" +ajv-keywords@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.1.1.tgz#02550bc605a3e576041565628af972e06c549d50" + +ajv@^4.7.0: + version "4.8.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.8.2.tgz#65486936ca36fea39a1504332a78bebd5d447bdc" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.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" @@ -43,32 +87,38 @@ align-text@^0.1.1, align-text@^0.1.3: longest "^1.0.1" repeat-string "^1.5.2" -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" - -alter@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" - dependencies: - stable "~0.1.3" - amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" -ansi-html@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.6.tgz#bda8e33dd2ee1c20f54c08eb405713cbfc0ed80e" +ansi-escapes@^1.1.0, ansi-escapes@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" + +ansi-regex@^0.2.0, ansi-regex@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-0.2.1.tgz#0d8e946967a3d8143f93e24e298525fc1b2235f9" ansi-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" +ansi-styles@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" +ansi-styles@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.0.1.tgz#b033f57f93e2d28adeb8bc11138fa13da0fd20a3" + +ansicolors@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" + anymatch@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" @@ -76,6 +126,10 @@ anymatch@^1.3.0: arrify "^1.0.0" micromatch "^2.1.5" +append-transform@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.3.0.tgz#d6933ce4a85f09445d9ccc4cc119051b7381a813" + aproba@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" @@ -93,6 +147,13 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@~0.1.15: + version "0.1.16" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-0.1.16.tgz#cfd01e0fbba3d6caed049fbd758d40f65196f57c" + dependencies: + underscore "~1.7.0" + underscore.string "~2.4.0" + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -103,10 +164,44 @@ arr-flatten@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" +array-differ@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" + +array-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + +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" + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + array-unique@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" @@ -133,6 +228,14 @@ asap@^2.0.3, asap@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" +asn1.js@^4.0.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.8.1.tgz#3949b7f5fd1e8bedc13be3abebf477f93490c810" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -145,9 +248,9 @@ assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" -assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" +assert@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.3.0.tgz#03939a622582a812cc202320a0b9a56c9b815849" dependencies: util "0.10.3" @@ -155,27 +258,21 @@ assertion-error@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" -ast-traverse@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" - -ast-types@0.8.12: - version "0.8.12" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" - ast-types@0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" +astw@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astw/-/astw-2.0.0.tgz#08121ac8288d35611c0ceec663f6cd545604897d" + dependencies: + acorn "^1.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" -async@^0.9.0: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" - -async@^1.2.1, async@^1.3.0, async@^1.5.0: +async@^1.4.0, async@^1.4.2, async@^1.5.0, async@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -191,7 +288,52 @@ attr-accept@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.0.tgz#b5cd35227f163935a8f1de10ed3eba16941f6be6" -autoprefixer@^6.0.3, autoprefixer@^6.3.1: +auth0-js@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-7.4.0.tgz#a2f1c472a4db261912afd20e1ed9c2773e251cdf" + dependencies: + Base64 "~0.1.3" + json-fallback "0.0.1" + jsonp "~0.0.4" + qs "^6.2.1" + reqwest "2.0.5" + trim "~0.0.1" + winchan "0.1.4" + xtend "~2.1.1" + +auth0-js@7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-7.3.0.tgz#2a05a5a4ca21faa28aa927bbeef3194b2a95847d" + dependencies: + Base64 "~0.1.3" + json-fallback "0.0.1" + jsonp "~0.0.4" + qs "^6.2.1" + reqwest "2.0.5" + trim "~0.0.1" + winchan "0.1.4" + xtend "~2.1.1" + +auth0-lock@^10.5.1: + version "10.5.1" + resolved "https://registry.yarnpkg.com/auth0-lock/-/auth0-lock-10.5.1.tgz#ae0a935ed05f6ff3c2abde29018decb302f9ec0b" + dependencies: + auth0-js "7.3.0" + blueimp-md5 "2.3.1" + fbjs "^0.3.1" + immutable "^3.7.3" + jsonp "^0.2.0" + password-sheriff "^1.0.0" + react "^15.0.0 || ^16.0.0" + react-addons-css-transition-group "^15.0.0 || ^16.0.0" + react-dom "^15.0.0 || ^16.0.0" + trim "0.0.1" + +autolinker@~0.15.0: + version "0.15.3" + resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" + +autoprefixer@^6.0.2, autoprefixer@^6.0.3: version "6.5.1" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.5.1.tgz#ae759a5221e709f3da17c2d656230e67c43cbb75" dependencies: @@ -202,6 +344,19 @@ autoprefixer@^6.0.3, autoprefixer@^6.3.1: postcss "^5.2.4" postcss-value-parser "^3.2.3" +aws-sdk@^2.4.2: + version "2.6.13" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.6.13.tgz#14860439ce40497e4b08cfd363d76777967dbaef" + dependencies: + buffer "4.9.1" + crypto-browserify "1.0.9" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.1.5" + url "0.10.3" + xml2js "0.4.15" + xmlbuilder "2.6.2" + aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" @@ -210,27 +365,6 @@ aws4@^1.2.1: version "1.5.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.5.0.tgz#0a29ffb79c31c9e712eeb087e8e7a64b4a56d755" -babel-cli@^6.4.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.18.0.tgz#92117f341add9dead90f6fa7d0a97c0cc08ec186" - dependencies: - babel-core "^6.18.0" - babel-polyfill "^6.16.0" - babel-register "^6.18.0" - babel-runtime "^6.9.0" - commander "^2.8.1" - convert-source-map "^1.1.0" - fs-readdir-recursive "^1.0.0" - glob "^5.0.5" - lodash "^4.2.0" - output-file-sync "^1.1.0" - path-is-absolute "^1.0.0" - slash "^1.0.0" - source-map "^0.5.0" - v8flags "^2.0.10" - optionalDependencies: - chokidar "^1.0.0" - babel-code-frame@^6.16.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.16.0.tgz#f90e60da0862909d3ce098733b5d3987c97cb8de" @@ -239,58 +373,7 @@ babel-code-frame@^6.16.0: esutils "^2.0.2" js-tokens "^2.0.0" -babel-core@^5.8.33: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558" - dependencies: - babel-plugin-constant-folding "^1.0.1" - babel-plugin-dead-code-elimination "^1.0.2" - babel-plugin-eval "^1.0.1" - babel-plugin-inline-environment-variables "^1.0.1" - babel-plugin-jscript "^1.0.4" - babel-plugin-member-expression-literals "^1.0.1" - babel-plugin-property-literals "^1.0.1" - babel-plugin-proto-to-assign "^1.0.3" - babel-plugin-react-constant-elements "^1.0.3" - babel-plugin-react-display-name "^1.0.3" - babel-plugin-remove-console "^1.0.1" - babel-plugin-remove-debugger "^1.0.1" - babel-plugin-runtime "^1.0.7" - babel-plugin-undeclared-variables-check "^1.0.2" - babel-plugin-undefined-to-void "^1.1.6" - babylon "^5.8.38" - bluebird "^2.9.33" - chalk "^1.0.0" - convert-source-map "^1.1.0" - core-js "^1.0.0" - debug "^2.1.1" - detect-indent "^3.0.0" - esutils "^2.0.0" - fs-readdir-recursive "^0.1.0" - globals "^6.4.0" - home-or-tmp "^1.0.0" - is-integer "^1.0.4" - js-tokens "1.0.1" - json5 "^0.4.0" - lodash "^3.10.0" - minimatch "^2.0.3" - output-file-sync "^1.1.0" - path-exists "^1.0.0" - path-is-absolute "^1.0.0" - private "^0.1.6" - regenerator "0.8.40" - regexpu "^1.3.0" - repeating "^1.1.2" - resolve "^1.1.6" - shebang-regex "^1.0.0" - slash "^1.0.0" - source-map "^0.5.0" - source-map-support "^0.2.10" - to-fast-properties "^1.0.0" - trim-right "^1.0.0" - try-resolve "^1.0.0" - -babel-core@^6.0.0, babel-core@^6.18.0, babel-core@^6.3.26: +babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.10.4, babel-core@^6.11.4, babel-core@^6.18.0, babel-core@^6.9.0: version "6.18.2" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.18.2.tgz#d8bb14dd6986fa4f3566a26ceda3964fa0e04e5b" dependencies: @@ -314,14 +397,14 @@ babel-core@^6.0.0, babel-core@^6.18.0, babel-core@^6.3.26: slash "^1.0.0" source-map "^0.5.0" -babel-eslint@^4.1.6: - version "4.1.8" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-4.1.8.tgz#4f79e7a4f5879ecf03f48cb16f552a355fcc31b2" +babel-eslint@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.0.tgz#d506a5174ba224e25a2d17e128e2ba8987139ddc" dependencies: - acorn-to-esprima "^1.0.5" - babel-core "^5.8.33" - lodash.assign "^3.2.0" - lodash.pick "^3.1.0" + babel-traverse "^6.15.0" + babel-types "^6.15.0" + babylon "^6.11.2" + lodash.pickby "^4.6.0" babel-generator@^6.18.0: version "6.18.0" @@ -470,43 +553,22 @@ babel-jest@^16.0.0: babel-plugin-istanbul "^2.0.0" babel-preset-jest "^16.0.0" -babel-loader@^6.2.1: - version "6.2.7" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-6.2.7.tgz#16fdbf64328030dc5a606827d389c8b92a2a8032" - dependencies: - find-cache-dir "^0.1.1" - loader-utils "^0.2.11" - mkdirp "^0.5.1" - object-assign "^4.0.1" - babel-messages@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" dependencies: babel-runtime "^6.0.0" +babel-plugin-add-module-exports@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz#9ae9a1f4a8dc67f0cdec4f4aeda1e43a5ff65e25" + babel-plugin-check-es2015-constants@^6.3.13: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz#dbf024c32ed37bfda8dee1e76da02386a8d26fe7" dependencies: babel-runtime "^6.0.0" -babel-plugin-constant-folding@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz#8361d364c98e449c3692bdba51eff0844290aa8e" - -babel-plugin-dead-code-elimination@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65" - -babel-plugin-eval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da" - -babel-plugin-inline-environment-variables@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz#1f58ce91207ad6a826a8bf645fafe68ff5fe3ffe" - babel-plugin-istanbul@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-2.0.3.tgz#266b304b9109607d60748474394676982f660df4" @@ -520,50 +582,6 @@ babel-plugin-jest-hoist@^16.0.0: version "16.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-16.0.0.tgz#b58ca3f770982a7e7c25b5614b2e57e9dafc6e76" -babel-plugin-jscript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz#8f342c38276e87a47d5fa0a8bd3d5eb6ccad8fcc" - -babel-plugin-member-expression-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz#cc5edb0faa8dc927170e74d6d1c02440021624d3" - -babel-plugin-property-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz#0252301900192980b1c118efea48ce93aab83336" - -babel-plugin-proto-to-assign@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz#c49e7afd02f577bc4da05ea2df002250cf7cd123" - dependencies: - lodash "^3.9.3" - -babel-plugin-react-constant-elements@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz#946736e8378429cbc349dcff62f51c143b34e35a" - -babel-plugin-react-display-name@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz#754fe38926e8424a4e7b15ab6ea6139dee0514fc" - -babel-plugin-react-transform@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz#515bbfa996893981142d90b1f9b1635de2995109" - dependencies: - lodash "^4.6.1" - -babel-plugin-remove-console@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz#d8f24556c3a05005d42aaaafd27787f53ff013a7" - -babel-plugin-remove-debugger@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz#fd2ea3cd61a428ad1f3b9c89882ff4293e8c14c7" - -babel-plugin-runtime@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz#bf7c7d966dd56ecd5c17fa1cb253c9acb7e54aaf" - babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -572,22 +590,14 @@ babel-plugin-syntax-async-generators@^6.5.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" -babel-plugin-syntax-class-constructor-call@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" - babel-plugin-syntax-class-properties@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" -babel-plugin-syntax-decorators@^6.1.18, babel-plugin-syntax-decorators@^6.13.0: +babel-plugin-syntax-decorators@^6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" -babel-plugin-syntax-do-expressions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz#5747756139aa26d390d09410b03744ba07e4796d" - babel-plugin-syntax-dynamic-import@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" @@ -596,18 +606,10 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" -babel-plugin-syntax-export-extensions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" - babel-plugin-syntax-flow@^6.18.0, babel-plugin-syntax-flow@^6.3.13: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" -babel-plugin-syntax-function-bind@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46" - babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -616,7 +618,7 @@ babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" -babel-plugin-syntax-trailing-function-commas@^6.3.13: +babel-plugin-syntax-trailing-function-commas@^6.13.0, babel-plugin-syntax-trailing-function-commas@^6.3.13: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz#2b84b7d53dd744f94ff1fad7669406274b23f541" @@ -628,7 +630,7 @@ babel-plugin-transform-async-generator-functions@^6.17.0: babel-plugin-syntax-async-generators "^6.5.0" babel-runtime "^6.0.0" -babel-plugin-transform-async-to-generator@^6.16.0: +babel-plugin-transform-async-to-generator@^6.16.0, babel-plugin-transform-async-to-generator@^6.8.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.16.0.tgz#19ec36cb1486b59f9f468adfa42ce13908ca2999" dependencies: @@ -636,13 +638,16 @@ babel-plugin-transform-async-to-generator@^6.16.0: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.0.0" -babel-plugin-transform-class-constructor-call@^6.3.13: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.18.0.tgz#80855e38a1ab47b8c6c647f8ea1bcd2c00ca3aae" +babel-plugin-transform-cjs-system-require@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-cjs-system-require/-/babel-plugin-transform-cjs-system-require-0.1.1.tgz#ffef26d31bc270e82bdbbd437db2777e85162a29" + +babel-plugin-transform-cjs-system-wrapper@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-cjs-system-wrapper/-/babel-plugin-transform-cjs-system-wrapper-0.2.1.tgz#e855078877b56d4d1b92b9f91b37f599db0200e3" dependencies: - babel-plugin-syntax-class-constructor-call "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" + babel-plugin-transform-cjs-system-require "^0.1.1" + babel-template "^6.9.0" babel-plugin-transform-class-properties@^6.18.0: version "6.18.0" @@ -652,14 +657,6 @@ babel-plugin-transform-class-properties@^6.18.0: babel-plugin-syntax-class-properties "^6.8.0" babel-runtime "^6.9.1" -babel-plugin-transform-decorators-legacy@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925" - dependencies: - babel-plugin-syntax-decorators "^6.1.18" - babel-runtime "^6.2.0" - babel-template "^6.3.0" - babel-plugin-transform-decorators@^6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.13.0.tgz#82d65c1470ae83e2d13eebecb0a1c2476d62da9d" @@ -671,13 +668,6 @@ babel-plugin-transform-decorators@^6.13.0: babel-template "^6.8.0" babel-types "^6.13.0" -babel-plugin-transform-do-expressions@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.8.0.tgz#fda692af339835cc255bb7544efb8f7c1306c273" - dependencies: - babel-plugin-syntax-do-expressions "^6.8.0" - babel-runtime "^6.0.0" - babel-plugin-transform-es2015-arrow-functions@^6.3.13: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.8.0.tgz#5b63afc3181bdc9a8c4d481b5a4f3f7d7fef3d9d" @@ -690,7 +680,7 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.3.13: dependencies: babel-runtime "^6.0.0" -babel-plugin-transform-es2015-block-scoping@^6.18.0: +babel-plugin-transform-es2015-block-scoping@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.18.0.tgz#3bfdcfec318d46df22525cdea88f1978813653af" dependencies: @@ -700,7 +690,7 @@ babel-plugin-transform-es2015-block-scoping@^6.18.0: babel-types "^6.18.0" lodash "^4.2.0" -babel-plugin-transform-es2015-classes@^6.18.0: +babel-plugin-transform-es2015-classes@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.18.0.tgz#ffe7a17321bf83e494dcda0ae3fc72df48ffd1d9" dependencies: @@ -722,7 +712,7 @@ babel-plugin-transform-es2015-computed-properties@^6.3.13: babel-runtime "^6.0.0" babel-template "^6.8.0" -babel-plugin-transform-es2015-destructuring@^6.18.0: +babel-plugin-transform-es2015-destructuring@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.18.0.tgz#a08fb89415ab82058649558bedb7bf8dafa76ba5" dependencies: @@ -735,13 +725,13 @@ babel-plugin-transform-es2015-duplicate-keys@^6.6.0: babel-runtime "^6.0.0" babel-types "^6.8.0" -babel-plugin-transform-es2015-for-of@^6.18.0: +babel-plugin-transform-es2015-for-of@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.18.0.tgz#4c517504db64bf8cfc119a6b8f177211f2028a70" dependencies: babel-runtime "^6.0.0" -babel-plugin-transform-es2015-function-name@^6.9.0: +babel-plugin-transform-es2015-function-name@^6.3.13: version "6.9.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.9.0.tgz#8c135b17dbd064e5bba56ec511baaee2fca82719" dependencies: @@ -755,7 +745,7 @@ babel-plugin-transform-es2015-literals@^6.3.13: dependencies: babel-runtime "^6.0.0" -babel-plugin-transform-es2015-modules-amd@^6.18.0: +babel-plugin-transform-es2015-modules-amd@^6.18.0, babel-plugin-transform-es2015-modules-amd@^6.8.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.18.0.tgz#49a054cbb762bdf9ae2d8a807076cfade6141e40" dependencies: @@ -763,7 +753,7 @@ babel-plugin-transform-es2015-modules-amd@^6.18.0: babel-runtime "^6.0.0" babel-template "^6.8.0" -babel-plugin-transform-es2015-modules-commonjs@^6.18.0: +babel-plugin-transform-es2015-modules-commonjs@^6.18.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz#c15ae5bb11b32a0abdcc98a5837baa4ee8d67bcc" dependencies: @@ -772,7 +762,7 @@ babel-plugin-transform-es2015-modules-commonjs@^6.18.0: babel-template "^6.16.0" babel-types "^6.18.0" -babel-plugin-transform-es2015-modules-systemjs@^6.18.0: +babel-plugin-transform-es2015-modules-systemjs@^6.12.0, babel-plugin-transform-es2015-modules-systemjs@^6.6.5: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.18.0.tgz#f09294707163edae4d3b3e8bfacecd01d920b7ad" dependencies: @@ -780,7 +770,7 @@ babel-plugin-transform-es2015-modules-systemjs@^6.18.0: babel-runtime "^6.11.6" babel-template "^6.14.0" -babel-plugin-transform-es2015-modules-umd@^6.18.0: +babel-plugin-transform-es2015-modules-umd@^6.12.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.18.0.tgz#23351770ece5c1f8e83ed67cb1d7992884491e50" dependencies: @@ -795,7 +785,7 @@ babel-plugin-transform-es2015-object-super@^6.3.13: babel-helper-replace-supers "^6.8.0" babel-runtime "^6.0.0" -babel-plugin-transform-es2015-parameters@^6.18.0: +babel-plugin-transform-es2015-parameters@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.18.0.tgz#9b2cfe238c549f1635ba27fc1daa858be70608b1" dependencies: @@ -806,7 +796,7 @@ babel-plugin-transform-es2015-parameters@^6.18.0: babel-traverse "^6.18.0" babel-types "^6.18.0" -babel-plugin-transform-es2015-shorthand-properties@^6.18.0: +babel-plugin-transform-es2015-shorthand-properties@^6.3.13: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.18.0.tgz#e2ede3b7df47bf980151926534d1dd0cbea58f43" dependencies: @@ -833,7 +823,7 @@ babel-plugin-transform-es2015-template-literals@^6.6.0: dependencies: babel-runtime "^6.0.0" -babel-plugin-transform-es2015-typeof-symbol@^6.18.0: +babel-plugin-transform-es2015-typeof-symbol@^6.6.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.18.0.tgz#0b14c48629c90ff47a0650077f6aa699bee35798" dependencies: @@ -847,7 +837,7 @@ babel-plugin-transform-es2015-unicode-regex@^6.3.13: babel-runtime "^6.0.0" regexpu-core "^2.0.0" -babel-plugin-transform-exponentiation-operator@^6.3.13: +babel-plugin-transform-exponentiation-operator@^6.3.13, babel-plugin-transform-exponentiation-operator@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.8.0.tgz#db25742e9339eade676ca9acec46f955599a68a4" dependencies: @@ -855,13 +845,6 @@ babel-plugin-transform-exponentiation-operator@^6.3.13: babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-runtime "^6.0.0" -babel-plugin-transform-export-extensions@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.8.0.tgz#fa80ff655b636549431bfd38f6b817bd82e47f5b" - dependencies: - babel-plugin-syntax-export-extensions "^6.8.0" - babel-runtime "^6.0.0" - babel-plugin-transform-flow-strip-types@^6.3.13: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.18.0.tgz#4d3e642158661e9b40db457c004a30817fa32592" @@ -869,12 +852,11 @@ babel-plugin-transform-flow-strip-types@^6.3.13: babel-plugin-syntax-flow "^6.18.0" babel-runtime "^6.0.0" -babel-plugin-transform-function-bind@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.8.0.tgz#e7f334ce69f50d28fe850a822eaaab9fa4f4d821" +babel-plugin-transform-global-system-wrapper@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-global-system-wrapper/-/babel-plugin-transform-global-system-wrapper-0.0.1.tgz#afb469cec0e04689b9fe7e8b1fd280fc94a6d8f2" dependencies: - babel-plugin-syntax-function-bind "^6.8.0" - babel-runtime "^6.0.0" + babel-template "^6.9.0" babel-plugin-transform-object-rest-spread@^6.16.0: version "6.16.0" @@ -911,7 +893,7 @@ babel-plugin-transform-react-jsx@^6.3.13: babel-plugin-syntax-jsx "^6.8.0" babel-runtime "^6.0.0" -babel-plugin-transform-regenerator@^6.16.0, babel-plugin-transform-regenerator@^6.9.0: +babel-plugin-transform-regenerator@^6.6.0: version "6.16.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz#a75de6b048a14154aae14b0122756c5bed392f59" dependencies: @@ -932,52 +914,42 @@ babel-plugin-transform-strict-mode@^6.18.0: babel-runtime "^6.0.0" babel-types "^6.18.0" -babel-plugin-undeclared-variables-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz#5cf1aa539d813ff64e99641290af620965f65dee" - dependencies: - leven "^1.0.2" - -babel-plugin-undefined-to-void@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" - -babel-polyfill@^6.16.0, babel-polyfill@^6.9.1: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.16.0.tgz#2d45021df87e26a374b6d4d1a9c65964d17f2422" - dependencies: - babel-runtime "^6.9.1" - core-js "^2.4.0" - regenerator-runtime "^0.9.5" +babel-plugin-transform-system-register@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-system-register/-/babel-plugin-transform-system-register-0.0.1.tgz#9dff40390c2763ac518f0b2ad7c5ea4f65a5be25" -babel-preset-es2015@^6.3.13: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.18.0.tgz#b8c70df84ec948c43dcf2bf770e988eb7da88312" +babel-preset-env@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-0.0.6.tgz#cda63a020069098fad12272a7a447a7c5bafb3c8" dependencies: babel-plugin-check-es2015-constants "^6.3.13" + babel-plugin-syntax-trailing-function-commas "^6.13.0" + babel-plugin-transform-async-to-generator "^6.8.0" babel-plugin-transform-es2015-arrow-functions "^6.3.13" babel-plugin-transform-es2015-block-scoped-functions "^6.3.13" - babel-plugin-transform-es2015-block-scoping "^6.18.0" - babel-plugin-transform-es2015-classes "^6.18.0" + babel-plugin-transform-es2015-block-scoping "^6.6.0" + babel-plugin-transform-es2015-classes "^6.6.0" babel-plugin-transform-es2015-computed-properties "^6.3.13" - babel-plugin-transform-es2015-destructuring "^6.18.0" + babel-plugin-transform-es2015-destructuring "^6.6.0" babel-plugin-transform-es2015-duplicate-keys "^6.6.0" - babel-plugin-transform-es2015-for-of "^6.18.0" - babel-plugin-transform-es2015-function-name "^6.9.0" + babel-plugin-transform-es2015-for-of "^6.6.0" + babel-plugin-transform-es2015-function-name "^6.3.13" babel-plugin-transform-es2015-literals "^6.3.13" - babel-plugin-transform-es2015-modules-amd "^6.18.0" - babel-plugin-transform-es2015-modules-commonjs "^6.18.0" - babel-plugin-transform-es2015-modules-systemjs "^6.18.0" - babel-plugin-transform-es2015-modules-umd "^6.18.0" + babel-plugin-transform-es2015-modules-amd "^6.8.0" + babel-plugin-transform-es2015-modules-commonjs "^6.6.0" + babel-plugin-transform-es2015-modules-systemjs "^6.12.0" + babel-plugin-transform-es2015-modules-umd "^6.12.0" babel-plugin-transform-es2015-object-super "^6.3.13" - babel-plugin-transform-es2015-parameters "^6.18.0" - babel-plugin-transform-es2015-shorthand-properties "^6.18.0" + babel-plugin-transform-es2015-parameters "^6.6.0" + babel-plugin-transform-es2015-shorthand-properties "^6.3.13" babel-plugin-transform-es2015-spread "^6.3.13" babel-plugin-transform-es2015-sticky-regex "^6.3.13" babel-plugin-transform-es2015-template-literals "^6.6.0" - babel-plugin-transform-es2015-typeof-symbol "^6.18.0" + babel-plugin-transform-es2015-typeof-symbol "^6.6.0" babel-plugin-transform-es2015-unicode-regex "^6.3.13" - babel-plugin-transform-regenerator "^6.16.0" + babel-plugin-transform-exponentiation-operator "^6.8.0" + babel-plugin-transform-regenerator "^6.6.0" + browserslist "^1.4.0" babel-preset-jest@^16.0.0: version "16.0.0" @@ -985,7 +957,7 @@ babel-preset-jest@^16.0.0: dependencies: babel-plugin-jest-hoist "^16.0.0" -babel-preset-react@^6.3.13: +babel-preset-react@^6.5.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.16.0.tgz#aa117d60de0928607e343c4828906e4661824316" dependencies: @@ -997,23 +969,7 @@ babel-preset-react@^6.3.13: babel-plugin-transform-react-jsx-self "^6.11.0" babel-plugin-transform-react-jsx-source "^6.3.13" -babel-preset-stage-0@^6.5.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.16.0.tgz#f5a263c420532fd57491f1a7315b3036e428f823" - dependencies: - babel-plugin-transform-do-expressions "^6.3.13" - babel-plugin-transform-function-bind "^6.3.13" - babel-preset-stage-1 "^6.16.0" - -babel-preset-stage-1@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.16.0.tgz#9d31fbbdae7b17c549fd3ac93e3cf6902695e479" - dependencies: - babel-plugin-transform-class-constructor-call "^6.3.13" - babel-plugin-transform-export-extensions "^6.3.13" - babel-preset-stage-2 "^6.16.0" - -babel-preset-stage-2@^6.16.0: +babel-preset-stage-2@^6.17.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.18.0.tgz#9eb7bf9a8e91c68260d5ba7500493caaada4b5b5" dependencies: @@ -1050,14 +1006,14 @@ babel-runtime@^5.6.18: dependencies: core-js "^1.0.0" -babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.2.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@6.x: +babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@6.x: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" dependencies: core-js "^2.4.0" regenerator-runtime "^0.9.5" -babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.3.0, babel-template@^6.8.0: +babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.8.0, babel-template@^6.9.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" dependencies: @@ -1067,7 +1023,7 @@ babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-te babylon "^6.11.0" lodash "^4.2.0" -babel-traverse@^6.16.0, babel-traverse@^6.18.0: +babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.18.0.tgz#5aeaa980baed2a07c8c47329cd90c3b90c80f05e" dependencies: @@ -1081,7 +1037,7 @@ babel-traverse@^6.16.0, babel-traverse@^6.18.0: invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.13.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.8.0, babel-types@^6.9.0: +babel-types@^6.13.0, babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.8.0, babel-types@^6.9.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.18.0.tgz#1f7d5a73474c59eb9151b2417bbff4e4fce7c3f8" dependencies: @@ -1090,19 +1046,26 @@ babel-types@^6.13.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.8. lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^5.8.38: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" +babelify@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" + dependencies: + babel-core "^6.0.14" + object-assign "^4.0.0" -babylon@^6.11.0, babylon@^6.13.0: +babylon@^6.11.0, babylon@^6.11.2, babylon@^6.13.0: version "6.13.1" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.13.1.tgz#adca350e088f0467647157652bafead6ddb8dfdb" +balanced-match@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7" + balanced-match@^0.4.1, balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" -balanced-match@~0.1.0: +balanced-match@~0.1.0, balanced-match@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" @@ -1118,9 +1081,9 @@ base64-url@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f" -Base64@~0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.2.1.tgz#ba3a4230708e186705065e66babdd4c35cf60028" +Base64@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.1.4.tgz#e9f6c6bef567fd635ea4162ab14dd329e74aa6de" base64url@~1.0.4: version "1.0.6" @@ -1129,41 +1092,39 @@ base64url@~1.0.4: concat-stream "~1.4.7" meow "~2.0.0" -batch@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" - bcrypt-pbkdf@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" dependencies: tweetnacl "^0.14.3" -big.js@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978" - binary-extensions@^1.0.0: version "1.7.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.7.0.tgz#6c1610db163abfb34edfe42fa423343a1e01185d" +bl@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + dependencies: + readable-stream "~2.0.5" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" dependencies: inherits "~2.0.0" -bluebird@^2.9.33: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - -bluebird@^3.0.5: +bluebird@^3.0.5, bluebird@^3.3.4: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" -blueimp-tmpl@^2.5.5: - version "2.5.7" - resolved "https://registry.yarnpkg.com/blueimp-tmpl/-/blueimp-tmpl-2.5.7.tgz#33fb12c139d65512ae40afbd8e2def8d9db96490" +blueimp-md5@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.3.1.tgz#992a6737733b9da1edd641550dc3acab2e9cfc5a" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" body-parser@^1.14.2: version "1.15.2" @@ -1180,6 +1141,29 @@ body-parser@^1.14.2: raw-body "~2.1.7" type-is "~1.6.13" +body-parser@~1.14.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.14.2.tgz#1015cb1fe2c443858259581db53332f8d0cf50f9" + dependencies: + bytes "2.2.0" + content-type "~1.0.1" + debug "~2.2.0" + depd "~1.1.0" + http-errors "~1.3.1" + iconv-lite "0.4.13" + on-finished "~2.3.0" + qs "5.2.0" + raw-body "~2.1.5" + type-is "~1.6.10" + +bole@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bole/-/bole-2.0.0.tgz#d8aa1c690467bfb4fe11b874acb2e8387e382615" + dependencies: + core-util-is ">=1.0.1 <1.1.0-0" + individual ">=3.0.0 <3.1.0-0" + json-stringify-safe ">=5.0.0 <5.1.0-0" + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -1209,35 +1193,203 @@ brackets2dots@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brackets2dots/-/brackets2dots-1.1.0.tgz#3f3d40375fc660ce0fd004fa27d67b34f9469ac3" -breakable@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" +brorand@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" + +browser-pack@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.1.tgz#779887c792eaa1f64a46a22c8f1051cdcd96755f" + dependencies: + combine-source-map "~0.7.1" + defined "^1.0.0" + JSONStream "^1.0.3" + through2 "^2.0.0" + umd "^3.0.0" "browser-request@>= 0.3.1 < 0.4.0": version "0.3.3" resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" -browserify-zlib@~0.1.4: +browser-resolve@^1.11.0, browser-resolve@^1.11.2, browser-resolve@^1.7.0: + version "1.11.2" + resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" + dependencies: + resolve "1.1.7" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a" + dependencies: + buffer-xor "^1.0.2" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + inherits "^2.0.1" + +browserify-cipher@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^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" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + +browserify-markdown@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-markdown/-/browserify-markdown-1.0.0.tgz#56321cdbcf92478c0467ef56b321a0766db78243" + dependencies: + highlight.js "^8.6.0" + remarkable "^1.6.0" + string-to-js "0.0.1" + through "^2.3.7" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" dependencies: pako "~0.2.0" -browserslist@~1.4.0: +browserify@^13.0.0, browserify@^13.0.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.1.1.tgz#72a2310e2f706ed87db929cf0ee73a5e195d9bb0" + dependencies: + assert "~1.3.0" + browser-pack "^6.0.1" + browser-resolve "^1.11.0" + browserify-zlib "~0.1.2" + buffer "^4.1.0" + cached-path-relative "^1.0.0" + concat-stream "~1.5.1" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.0" + domain-browser "~1.1.0" + duplexer2 "~0.1.2" + events "~1.1.0" + glob "^5.0.15" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "~0.0.0" + inherits "~2.0.1" + insert-module-globals "^7.0.0" + JSONStream "^1.0.3" + labeled-stream-splicer "^2.0.0" + module-deps "^4.0.8" + os-browserify "~0.1.1" + parents "^1.0.1" + path-browserify "~0.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum "^1.0.0" + shell-quote "^1.4.3" + stream-browserify "^2.0.0" + stream-http "^2.0.0" + string_decoder "~0.10.0" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "~0.0.0" + url "~0.11.0" + util "~0.10.1" + vm-browserify "~0.0.1" + xtend "^4.0.0" + +browserslist@^1.0.0, browserslist@^1.0.1, browserslist@^1.4.0, browserslist@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.4.0.tgz#9cfdcf5384d9158f5b70da2aa00b30e8ff019049" dependencies: caniuse-db "^1.0.30000539" +bser@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169" + dependencies: + node-int64 "^0.4.0" + +budo@^9.0.0: + version "9.2.1" + resolved "https://registry.yarnpkg.com/budo/-/budo-9.2.1.tgz#c36c6c5097bd5ad43720cb3ae9d269870b4735ed" + dependencies: + bole "^2.0.0" + browserify "^13.0.1" + chokidar "^1.0.1" + connect-pushstate "^1.0.0" + events "^1.0.2" + garnish "^5.0.0" + get-ports "^1.0.2" + http-proxy "^1.14.0" + inject-lr-script "^2.0.0" + internal-ip "^1.0.1" + micromatch "^2.2.0" + minimist "^1.1.0" + once "^1.3.2" + opn "^3.0.2" + pem "^1.8.3" + resolve "^1.1.6" + resp-modifier "^6.0.0" + serve-static "^1.10.0" + simple-html-index "^1.4.0" + stacked "^1.1.1" + stdout-stream "^1.4.0" + strip-ansi "^3.0.0" + term-color "^1.0.1" + tiny-lr "^0.2.0" + url-trim "^1.0.0" + watchify-middleware "^1.6.0" + xtend "^4.0.0" + buffer-equal-constant-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" +buffer-peek-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-peek-stream/-/buffer-peek-stream-1.0.1.tgz#53b47570a1347787c5bad4ca2ca3021f9d8b3cfd" + buffer-shims@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" -buffer@^4.9.0: +buffer-xor@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.1.0, buffer@4.9.1: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -1249,20 +1401,35 @@ builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -bytes@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.3.0.tgz#d5b680a165b6201739acb611542aabc2d8ceb070" +builtin-status-codes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz#6f22003baacf003ccd287afe6872151fddc58579" + +bytes@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588" bytes@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" -camel-case@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-1.2.2.tgz#1aca7c4d195359a2ce9955793433c6e5542511f2" - dependencies: - sentence-case "^1.1.1" - upper-case "^1.1.1" +cached-path-relative@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.0.tgz#d1094c577fbd9a8b8bd43c96af6188aa205d05f4" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + +callsites@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" camel-case@^3.0.0: version "3.0.0" @@ -1278,14 +1445,46 @@ camelcase-keys@^1.0.0: camelcase "^1.0.1" map-obj "^1.0.0" -camelcase@^1.0.1, camelcase@^1.0.2, camelcase@^1.2.1: +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^1.0.1, camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" -caniuse-db@^1.0.30000539, caniuse-db@^1.0.30000554: +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +caniuse-api@^1.3.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.5.2.tgz#8f393c682f661c0a997b77bba6e826483fb3600e" + dependencies: + browserslist "^1.0.1" + caniuse-db "^1.0.30000346" + lodash.memoize "^4.1.0" + lodash.uniq "^4.3.0" + shelljs "^0.7.0" + +caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000539, caniuse-db@^1.0.30000554: version "1.0.30000572" resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000572.tgz#81d0aa6b7de2d785c8dcab135502983276cc707d" +cardinal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-1.0.0.tgz#50e21c1b0aa37729f9377def196b5a9cec932ee9" + dependencies: + ansicolors "~0.2.1" + redeyed "~1.0.0" + caseless@~0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" @@ -1305,7 +1504,17 @@ chai@^3.2.0: deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" + dependencies: + ansi-styles "^1.1.0" + escape-string-regexp "^1.0.0" + has-ansi "^0.1.0" + strip-ansi "^0.3.0" + supports-color "^0.2.0" + +chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3, chalk@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -1338,28 +1547,7 @@ change-case@^3.0.0: upper-case "^1.1.1" upper-case-first "^1.1.0" -change-case@2.3.x: - version "2.3.1" - resolved "https://registry.yarnpkg.com/change-case/-/change-case-2.3.1.tgz#2c4fde3f063bb41d00cd68e0d5a09db61cbe894f" - dependencies: - camel-case "^1.1.1" - constant-case "^1.1.0" - dot-case "^1.1.0" - is-lower-case "^1.1.0" - is-upper-case "^1.1.0" - lower-case "^1.1.1" - lower-case-first "^1.0.0" - param-case "^1.1.0" - pascal-case "^1.1.0" - path-case "^1.1.0" - sentence-case "^1.1.1" - snake-case "^1.1.0" - swap-case "^1.1.0" - title-case "^1.1.0" - upper-case "^1.1.1" - upper-case-first "^1.1.0" - -chokidar@^1.0.0: +chokidar@^1.0.0, chokidar@^1.0.1, chokidar@^1.6.0: version "1.6.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" dependencies: @@ -1374,22 +1562,46 @@ chokidar@^1.0.0: optionalDependencies: fsevents "^1.0.0" -clap@^1.0.9: - version "1.1.1" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.1.tgz#a8a93e0bfb7581ac199c4f001a5525a724ce696d" +ci-info@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.0.0.tgz#dc5285f2b4e251821683681c381c3388f46ec534" + +cipher-base@^1.0.0, cipher-base@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" dependencies: - chalk "^1.1.3" + inherits "^2.0.1" + +circular-json@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" classnames@^2.1.2, classnames@^2.2.0, classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5, classnames@2.x: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" -clean-css@3.4.x: - version "3.4.20" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-3.4.20.tgz#c0d8963b5448e030f0bcd3ddd0dac4dfe3dea501" +cli-cursor@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + +cli-table@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23" + dependencies: + colors "1.0.3" + +cli-usage@^0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/cli-usage/-/cli-usage-0.1.4.tgz#7c01e0dc706c234b39c933838c8e20b2175776e2" dependencies: - commander "2.8.x" - source-map "0.4.x" + marked "^0.3.6" + marked-terminal "^1.6.2" + +cli-width@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a" cliui@^2.1.0: version "2.1.0" @@ -1399,15 +1611,21 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" -coa@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3" - dependencies: - q "^1.1.2" +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" code-point-at@^1.0.0: version "1.0.1" @@ -1415,6 +1633,10 @@ code-point-at@^1.0.0: dependencies: number-is-nan "^1.0.0" +color-convert@^0.5.3, color-convert@0.5.x: + version "0.5.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" + color-convert@^1.3.0: version "1.6.0" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.6.0.tgz#7592755faf53938a05b1ea8e5374cab77d6dd190" @@ -1425,12 +1647,29 @@ color-name@^1.0.0, color-name@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" +color-name@1.0.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.0.1.tgz#6b34b2b29b7716013972b0b9d5bedcfbb6718df8" + color-string@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" dependencies: color-name "^1.0.0" +color-string@0.2.x: + version "0.2.4" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.2.4.tgz#221ff64234f71aaa3e13bc8c7e8c95f3cdd8f81a" + dependencies: + color-name "1.0.x" + +color@^0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/color/-/color-0.10.1.tgz#c04188df82a209ddebccecdacd3ec320f193739f" + dependencies: + color-convert "^0.5.3" + color-string "^0.3.0" + color@^0.11.0: version "0.11.4" resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" @@ -1439,17 +1678,32 @@ color@^0.11.0: color-convert "^1.3.0" color-string "^0.3.0" -colormin@^1.0.5: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" +color@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/color/-/color-0.7.3.tgz#ab3ae4bc6cb8cfadb5d749c40f34aea088104f89" dependencies: - color "^0.11.0" - css-color-names "0.0.4" - has "^1.0.1" + color-convert "0.5.x" + color-string "0.2.x" -colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" +color@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/color/-/color-0.9.0.tgz#91146d460cdd5543ea1fa20aa0b597337509b64d" + dependencies: + color-convert "^0.5.3" + color-string "^0.3.0" + +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + +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" + dependencies: + convert-source-map "~1.1.0" + inline-source-map "~0.6.0" + lodash.memoize "~3.0.3" + source-map "~0.5.3" combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.5" @@ -1457,7 +1711,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.5.0, commander@^2.8.1, commander@^2.9.0, commander@2.9.x: +commander@^2.5.0, commander@^2.9.0, commander@2.9.x: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -1471,17 +1725,26 @@ commander@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" -commander@2.8.x: - version "2.8.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" - dependencies: - graceful-readlink ">= 1.0.0" - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - -commoner@^0.10.1, commoner@~0.10.3: +commitizen@^2.8.2: + version "2.8.6" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-2.8.6.tgz#578483abbf5b67368d1ccdb9d9d978c74972011b" + dependencies: + chalk "1.1.3" + cz-conventional-changelog "1.2.0" + dedent "0.6.0" + detect-indent "4.0.0" + find-node-modules "1.0.3" + find-root "1.0.0" + glob "7.0.5" + home-or-tmp "2.0.0" + inquirer "1.1.2" + lodash "4.15.0" + minimist "1.2.0" + path-exists "2.1.0" + shelljs "0.5.3" + strip-json-comments "2.0.1" + +commoner@^0.10.1: version "0.10.4" resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.4.tgz#98f3333dd3ad399596bb2d384a783bb7213d68f8" dependencies: @@ -1505,46 +1768,29 @@ component-indexof@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" -compressible@~2.0.8: - version "2.0.9" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.9.tgz#6daab4e2b599c2770dd9e21e7a891b1c5a755425" - dependencies: - mime-db ">= 1.24.0 < 2" - -compression@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.6.2.tgz#cceb121ecc9d09c52d7ad0c3350ea93ddd402bc3" - dependencies: - accepts "~1.3.3" - bytes "2.3.0" - compressible "~2.0.8" - debug "~2.2.0" - on-headers "~1.0.1" - vary "~1.1.0" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@~1.4.7: - version "1.4.10" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" +concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.1, 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" dependencies: inherits "~2.0.1" - readable-stream "~1.1.9" + readable-stream "~2.0.0" typedarray "~0.0.5" -concat-stream@1.5.x: - version "1.5.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" +concat-stream@~1.4.7: + version "1.4.10" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" dependencies: inherits "~2.0.1" - readable-stream "~2.0.0" + readable-stream "~1.1.9" typedarray "~0.0.5" -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" +connect-pushstate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/connect-pushstate/-/connect-pushstate-1.0.0.tgz#cdc82173076f01b90bb084983a1f4493d4a28d04" console-browserify@^1.1.0: version "1.1.0" @@ -1556,13 +1802,6 @@ 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" -constant-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-1.1.2.tgz#8ec2ca5ba343e00aa38dbf4e200fd5ac907efd63" - dependencies: - snake-case "^1.1.0" - upper-case "^1.1.1" - constant-case@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46" @@ -1570,22 +1809,34 @@ constant-case@^2.0.0: snake-case "^2.1.0" upper-case "^1.1.1" -constants-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-0.0.1.tgz#92577db527ba6c4cf0a4568d84bc031f441e21f2" +constants-browserify@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" content-disposition@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" -content-type@~1.0.2: +content-type-parser@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" + +content-type@~1.0.1, content-type@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed" +conventional-commit-types@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/conventional-commit-types/-/conventional-commit-types-2.1.0.tgz#45d860386c9a2e6537ee91d8a1b61bd0411b3d04" + convert-source-map@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.3.0.tgz#e9f3e9c6e2728efc2676696a70eb382f73106a67" +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" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -1594,7 +1845,7 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -core-js@^1.0.0: +core-js@^1.0.0, core-js@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -1606,7 +1857,7 @@ core-js@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65" -core-util-is@~1.0.0: +"core-util-is@>=1.0.1 <1.1.0-0", 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" @@ -1616,19 +1867,53 @@ cors@^2.3.1: dependencies: vary "^1" +create-ecdh@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^1.0.0" + sha.js "^2.3.6" + +create-hmac@^1.1.0, create-hmac@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" + dependencies: + create-hash "^1.1.0" + inherits "^2.0.1" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" dependencies: boom "2.x.x" -crypto-browserify@~3.2.6: - version "3.2.8" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.2.8.tgz#b9b11dbe6d9651dd882a01e6cc467df718ecf189" +crypto-browserify@^3.0.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" dependencies: - pbkdf2-compat "2.0.1" - ripemd160 "0.2.0" - sha.js "2.2.6" + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + +crypto-browserify@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0" css-animation@^1.3.0: version "1.3.0" @@ -1636,99 +1921,31 @@ css-animation@^1.3.0: dependencies: component-classes "^1.2.5" -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" - -css-loader@^0.23.1: - version "0.23.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.23.1.tgz#9fa23f2b5c0965235910ad5ecef3b8a36390fe50" - dependencies: - css-selector-tokenizer "^0.5.1" - cssnano ">=2.6.1 <4" - loader-utils "~0.2.2" - lodash.camelcase "^3.0.1" - object-assign "^4.0.1" - postcss "^5.0.6" - postcss-modules-extract-imports "^1.0.0" - postcss-modules-local-by-default "^1.0.1" - postcss-modules-scope "^1.0.0" - postcss-modules-values "^1.1.0" - source-list-map "^0.1.4" - -css-selector-tokenizer@^0.5.1: - version "0.5.4" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.5.4.tgz#139bafd34a35fd0c1428487049e0699e6f6a2c21" - dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - -css-selector-tokenizer@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152" - dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - regexpu-core "^1.0.0" - -cssesc@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" - -"cssnano@>=2.6.1 <4": - version "3.8.0" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.8.0.tgz#bb90ac5292f42b679d9a05f6da0e9697556bb80d" - dependencies: - autoprefixer "^6.3.1" - decamelize "^1.1.2" - defined "^1.0.0" - has "^1.0.1" - object-assign "^4.0.1" - postcss "^5.0.14" - postcss-calc "^5.2.0" - postcss-colormin "^2.1.8" - postcss-convert-values "^2.3.4" - postcss-discard-comments "^2.0.4" - postcss-discard-duplicates "^2.0.1" - postcss-discard-empty "^2.0.1" - postcss-discard-overridden "^0.1.1" - postcss-discard-unused "^2.2.1" - postcss-filter-plugins "^2.0.0" - postcss-merge-idents "^2.1.5" - postcss-merge-longhand "^2.0.1" - postcss-merge-rules "^2.0.3" - postcss-minify-font-values "^1.0.2" - postcss-minify-gradients "^1.0.1" - postcss-minify-params "^1.0.4" - postcss-minify-selectors "^2.0.4" - postcss-normalize-charset "^1.1.0" - postcss-normalize-url "^3.0.7" - postcss-ordered-values "^2.1.0" - postcss-reduce-idents "^2.2.2" - postcss-reduce-initial "^1.0.0" - postcss-reduce-transforms "^1.0.3" - postcss-svgo "^2.1.1" - postcss-unique-selectors "^2.0.2" - postcss-value-parser "^3.2.3" - postcss-zindex "^2.0.1" - -csso@~2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/csso/-/csso-2.2.1.tgz#51fbb5347e50e81e6ed51668a48490ae6fe2afe2" +css-color-function@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/css-color-function/-/css-color-function-1.3.0.tgz#72c767baf978f01b8a8a94f42f17ba5d22a776fc" dependencies: - clap "^1.0.9" - source-map "^0.5.3" + balanced-match "0.1.0" + color "^0.11.0" + debug "~0.7.4" + rgb "~0.1.0" "cssom@>= 0.3.0 < 0.4.0", cssom@0.3.x: version "0.3.1" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" -"cssstyle@>= 0.2.29 < 0.3.0": +"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.36 < 0.3.0": version "0.2.37" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" dependencies: cssom "0.3.x" +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + curry2@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/curry2/-/curry2-0.1.0.tgz#cc9837ec9148a6eaea47f6cedf254d103031a233" @@ -1739,9 +1956,28 @@ curry2@^1.0.0, curry2@^1.0.1: dependencies: fast-bind "^1.0.0" -d3@^3.5.6: - version "3.5.17" - resolved "https://registry.yarnpkg.com/d3/-/d3-3.5.17.tgz#bc46748004378b21a360c9fc7cf5231790762fb8" +cz-conventional-changelog@^1.1.6, cz-conventional-changelog@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-1.2.0.tgz#2bca04964c8919b23f3fd6a89ef5e6008b31b3f8" + dependencies: + conventional-commit-types "^2.0.0" + lodash.map "^4.5.1" + longest "^1.0.1" + pad-right "^0.2.2" + right-pad "^1.0.1" + word-wrap "^1.0.3" + +d@^0.1.1, d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + dependencies: + es5-ext "~0.10.2" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" dashdash@^1.12.0: version "1.14.0" @@ -1749,20 +1985,52 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-uri-to-buffer@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@^2.1.1, debug@^2.2.0, debug@~2.2.0, debug@2.2.0: +date-now@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c" + +debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.0.tgz#0948af513d2e4ce407916f8506a423d3f9cf72d8" + dependencies: + date-now "1.0.1" + +debug-log@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" + +debug@*, debug@^2.1.1, debug@^2.2.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" dependencies: ms "0.7.1" -decamelize@^1.0.0, decamelize@^1.1.2: +debug@~0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + +debug@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.1.3.tgz#ce8ab1b5ee8fbee2bfa3b633cab93d366b63418e" + dependencies: + ms "0.7.0" + +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" +dedent@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.6.0.tgz#0e6da8f0ce52838ef5cec5c8f9396b0c1b64a3cb" + deep-diff@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.4.tgz#aac5c39952236abe5f037a2349060ba01b00ae48" @@ -1789,20 +2057,28 @@ defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" -defs@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" +deglob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/deglob/-/deglob-2.0.0.tgz#dd087aa2971a0b1feeea66483c2c82685c73be86" dependencies: - alter "~0.2.0" - ast-traverse "~0.1.1" - breakable "~1.0.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - simple-fmt "~0.1.0" - simple-is "~0.2.0" - stringmap "~0.2.2" - stringset "~0.2.1" - tryor "~0.1.2" - yargs "~3.27.0" + find-root "^1.0.0" + glob "^7.0.5" + ignore "^3.0.9" + pkg-config "^1.1.0" + run-parallel "^1.1.2" + uniq "^1.0.1" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" delayed-stream@~1.0.0: version "1.0.0" @@ -1816,35 +2092,61 @@ depd@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3" +deps-sort@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/deps-sort/-/deps-sort-2.0.0.tgz#091724902e84658260eb910748cccd1af6e21fb5" + dependencies: + JSONStream "^1.0.3" + shasum "^1.0.0" + subarg "^1.0.0" + through2 "^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" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" -detect-indent@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" +detect-file@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - repeating "^1.1.0" + fs-exists-sync "^0.1.0" -detect-indent@^4.0.0: +detect-indent@^4.0.0, detect-indent@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" dependencies: repeating "^2.0.0" -detective@^4.3.1: +detective@^4.0.0, detective@^4.3.1: version "4.3.2" resolved "https://registry.yarnpkg.com/detective/-/detective-4.3.2.tgz#77697e2e7947ac3fe7c8e26a6d6f115235afa91c" dependencies: acorn "^3.1.0" defined "^1.0.0" +diff@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.0.1.tgz#a52d90cc08956994be00877bff97110062582c35" + diff@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" +diffie-hellman@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + disposables@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/disposables/-/disposables-1.0.1.tgz#064727a25b54f502bd82b89aa2dfb8df9f1b39e3" @@ -1858,6 +2160,13 @@ dnd-core@^2.0.1: lodash "^4.2.0" redux "^3.2.0" +doctrine@^1.2.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + dom-align@1.x: version "1.5.2" resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.5.2.tgz#bceb6c109a3442ebc001c04c48cb37a9d5e96df2" @@ -1873,11 +2182,7 @@ dom-serializer@0: domelementtype "~1.1.1" entities "~1.1.1" -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - -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" @@ -1902,12 +2207,6 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" -dot-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-1.1.2.tgz#1e73826900de28d6de5480bc1de31d0842b06bec" - dependencies: - sentence-case "^1.1.2" - dot-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.0.tgz#4b43dd0d7403c34cb645424add397e80bfe85ca6" @@ -1921,6 +2220,12 @@ dotsplit.js@^1.0.3: array.filter "^0.1.0" arraymap "^0.1.2" +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" + dependencies: + readable-stream "^2.0.2" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -1941,9 +2246,14 @@ element-class@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e" -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +elliptic@^6.0.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.2.tgz#e4c81e0829cf0a65ab70e998b8232723b5c1bc48" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + inherits "^2.0.1" encodeurl@~1.0.1: version "1.0.1" @@ -1955,26 +2265,28 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -enhanced-resolve@~0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.2.0" - tapable "^0.1.8" + once "~1.3.0" entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -envify@^3.0.0: +envify@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" dependencies: jstransform "^11.0.3" through "~2.3.4" -errno@^0.1.3: +err-code@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.1.tgz#739d71b6851f24d050ea18c79a5b722420771d59" + +"errno@>=0.1.1 <0.2.0-0": version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" dependencies: @@ -1986,15 +2298,78 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" +errorify@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/errorify/-/errorify-0.3.1.tgz#53e0aaeeb18adc3e55f9f1eb4e2d95929f41b79b" + +es5-ext@^0.10.12, es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@^0.10.9, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: + version "0.10.12" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" + dependencies: + es6-iterator "2" + es6-symbol "~3.1" + +es6-iterator@2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.0.tgz#bd968567d61635e33c0b80727613c9cb4b096bac" + dependencies: + d "^0.1.1" + es5-ext "^0.10.7" + es6-symbol "3" + +es6-map@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.4.tgz#a34b147be224773a4d7da8072794cefa3632b897" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-set "~0.1.3" + es6-symbol "~3.1.0" + event-emitter "~0.3.4" + es6-promise@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6" +es6-set@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.4.tgz#9516b6761c2964b92ff479456233a247dc707ce8" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-iterator "2" + es6-symbol "3" + event-emitter "~0.3.4" + +es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + +es6-template-strings@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-template-strings/-/es6-template-strings-2.0.1.tgz#b166c6a62562f478bb7775f6ca96103a599b4b2c" + dependencies: + es5-ext "^0.10.12" + esniff "^1.1" + +es6-weak-map@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" + dependencies: + d "^0.1.1" + es5-ext "^0.10.8" + es6-iterator "2" + es6-symbol "3" + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" -escape-string-regexp@^1.0.2: +escape-string-regexp@^1.0.0, 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" @@ -2002,7 +2377,7 @@ escape-string-regexp@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" -escodegen@^1.6.1: +escodegen@^1.6.1, escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" dependencies: @@ -2013,7 +2388,91 @@ escodegen@^1.6.1: optionalDependencies: source-map "~0.2.0" -esprima-fb@^15001.1.0-dev-harmony-fb: +escope@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" + dependencies: + es6-map "^0.1.3" + es6-weak-map "^2.0.1" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-config-standard-jsx@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.2.0.tgz#c240e26ed919a11a42aa4de8059472b38268d620" + +eslint-config-standard@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-6.2.1.tgz#d3a68aafc7191639e7ee441e7348739026354292" + +eslint-plugin-promise@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.3.0.tgz#20a1ef58b4243ffdaef82ee9360a02353a7cca89" + +eslint-plugin-react@~6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.4.1.tgz#7d1aade747db15892f71eee1fea4addf97bcfa2b" + dependencies: + doctrine "^1.2.2" + jsx-ast-utils "^1.3.1" + +eslint-plugin-standard@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz#3589699ff9c917f2c25f76a916687f641c369ff3" + +eslint@~3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.8.1.tgz#7d02db44cd5aaf4fa7aa489e1f083baa454342ba" + dependencies: + chalk "^1.1.3" + concat-stream "^1.4.6" + debug "^2.1.1" + doctrine "^1.2.2" + escope "^3.6.0" + espree "^3.3.1" + estraverse "^4.2.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + glob "^7.0.3" + globals "^9.2.0" + ignore "^3.1.5" + imurmurhash "^0.1.4" + inquirer "^0.12.0" + is-my-json-valid "^2.10.0" + is-resolvable "^1.0.0" + js-yaml "^3.5.1" + json-stable-stringify "^1.0.0" + levn "^0.3.0" + lodash "^4.0.0" + mkdirp "^0.5.0" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.1" + pluralize "^1.2.1" + progress "^1.1.8" + require-uncached "^1.0.2" + shelljs "^0.6.0" + strip-bom "^3.0.0" + strip-json-comments "~1.0.1" + table "^3.7.8" + text-table "~0.2.0" + user-home "^2.0.0" + +esniff@^1.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-1.1.0.tgz#c66849229f91464dede2e0d40201ed6abf65f2ac" + dependencies: + d "1" + es5-ext "^0.10.12" + +espree@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.3.2.tgz#dbf3fadeb4ecb4d4778303e50103b3d36c88b89c" + dependencies: + acorn "^4.0.1" + acorn-jsx "^3.0.0" + +esprima-fb@^15001.1.0-dev-harmony-fb: version "15001.1.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz#30a947303c6b8d5e955bee2b99b1d233206a6901" @@ -2021,14 +2480,33 @@ esprima-fb@~15001.1001.0-dev-harmony-fb: version "15001.1001.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" -esprima@^2.6.0, esprima@^2.7.1: +esprima@^2.6.0, esprima@^2.7.1, esprima@2.7.x: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esprima@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" + +esrecurse@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" + dependencies: + estraverse "~4.1.0" + object-assign "^4.0.1" + estraverse@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" +estraverse@^4.1.1, estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +estraverse@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" + esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" @@ -2037,19 +2515,44 @@ etag@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" +event-emitter@~0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.4.tgz#8d63ddfb4cfe1fae3b32ca265c4c720222080bb5" + dependencies: + d "~0.1.1" + es5-ext "~0.10.7" + eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" -events@^1.0.0: +events@^1.0.2, events@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" -eventsource@~0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" +evp_bytestokey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53" + dependencies: + create-hash "^1.1.1" + +exec-sh@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.0.tgz#14f75de3f20d286ef933099b2ce50a90359cef10" + dependencies: + merge "^1.1.3" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + +exorcist@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/exorcist/-/exorcist-0.4.0.tgz#1230ffdedd9248f42fbccf8b4a44d4cab29e3c64" dependencies: - original ">=0.0.5" + minimist "0.0.5" + mold-source-map "~0.4.0" + nave "~0.5.1" expand-brackets@^0.1.4: version "0.1.5" @@ -2063,6 +2566,12 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" +expand-tilde@^1.2.0, expand-tilde@^1.2.1, expand-tilde@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" + dependencies: + os-homedir "^1.0.1" + express-jwt@^3.3.0: version "3.4.0" resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-3.4.0.tgz#e6fe8730e61da81a8deab64680154ae46c0028cc" @@ -2107,23 +2616,28 @@ express@^4.13.3: utils-merge "1.0.0" vary "~1.1.0" -extend@~3.0.0: +extend@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8" + +extend@^3.0.0, extend@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" +external-editor@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b" + dependencies: + extend "^3.0.0" + spawn-sync "^1.0.15" + tmp "^0.0.29" + extglob@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" dependencies: is-extglob "^1.0.0" -extract-text-webpack-plugin@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-0.8.2.tgz#49309df325f53affc53972f711afba2360c1914c" - dependencies: - async "^1.2.1" - loader-utils "~0.2.3" - extsprintf@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" @@ -2136,25 +2650,21 @@ fast-levenshtein@~2.0.4: version "2.0.5" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.5.tgz#bd33145744519ab1c36c3ee9f31f08e9079b67f2" -fastparse@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" - -faye-websocket@^0.10.0: +faye-websocket@~0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" dependencies: websocket-driver ">=0.5.1" -faye-websocket@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.0.tgz#d9ccf0e789e7db725d74bc4877d23aa42972ac50" +fb-watchman@^1.8.0, fb-watchman@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.0.tgz#6f268f1f347a6b3c875d1e89da7e1ed79adfc0ec" dependencies: - websocket-driver ">=0.5.1" + bser "^1.0.2" -fbjs@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7" +fbjs@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.3.2.tgz#033a540595084b5de3509a405d06f1a2a8e5b9fb" dependencies: core-js "^1.0.0" loose-envify "^1.0.0" @@ -2182,16 +2692,31 @@ fbjs@0.1.0-alpha.10: promise "^7.0.3" whatwg-fetch "^0.9.0" -file-loader@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.8.5.tgz#9275d031fe780f27d47f5f4af02bd43713cc151b" +figures@^1.3.5: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" dependencies: - loader-utils "~0.2.5" + flat-cache "^1.2.1" + object-assign "^4.0.1" filename-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" +fileset@0.2.x: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-0.2.1.tgz#588ef8973c6623b2a76df465105696b96aac8067" + dependencies: + glob "5.x" + minimatch "2.x" + fill-range@^2.1.0: version "2.2.3" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" @@ -2212,13 +2737,16 @@ finalhandler@0.5.0: statuses "~1.3.0" unpipe "~1.0.0" -find-cache-dir@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" +find-node-modules@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-1.0.3.tgz#36117ea45c13d5d8352f82ba791c2b835d730a14" dependencies: - commondir "^1.0.1" - mkdirp "^0.5.1" - pkg-dir "^1.0.0" + findup-sync "^0.2.1" + merge "^1.2.0" + +find-root@^1.0.0, find-root@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.0.0.tgz#962ff211aab25c6520feeeb8d6287f8f6e95807a" find-up@^1.0.0, find-up@^1.1.2: version "1.1.2" @@ -2227,14 +2755,50 @@ find-up@^1.0.0, find-up@^1.1.2: path-exists "^2.0.0" pinkie-promise "^2.0.0" +findup-sync@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.2.1.tgz#e0a90a450075c49466ee513732057514b81e878c" + dependencies: + glob "~4.3.0" + +findup-sync@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" + dependencies: + detect-file "^0.1.0" + is-glob "^2.0.1" + micromatch "^2.3.7" + resolve-dir "^0.1.0" + +fined@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fined/-/fined-1.0.2.tgz#5b28424b760d7598960b7ef8480dff8ad3660e97" + dependencies: + expand-tilde "^1.2.1" + lodash.assignwith "^4.0.7" + lodash.isempty "^4.2.1" + lodash.isplainobject "^4.0.4" + lodash.isstring "^4.0.1" + lodash.pick "^4.2.1" + parse-filepath "^1.0.1" + +flagged-respawn@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" + +flat-cache@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.1.tgz#6c837d6225a7de5659323740b36d5361f71691ff" + dependencies: + circular-json "^0.3.0" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + flatten@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" -font-awesome@^4.3.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - for-in@^0.1.5: version "0.1.6" resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8" @@ -2271,13 +2835,22 @@ fresh@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" -fs-readdir-recursive@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" +from2-string@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/from2-string/-/from2-string-1.1.0.tgz#18282b27d08a267cb3030cd2b8b4b0f212af752a" + dependencies: + from2 "^2.0.3" -fs-readdir-recursive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.0.0.tgz#8cd1745c8b4f8a29c8caec392476921ba195f560" +from2@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-exists-sync@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" fs.realpath@^1.0.0: version "1.0.0" @@ -2311,6 +2884,21 @@ function-bind@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" +garnish@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/garnish/-/garnish-5.2.0.tgz#bed43659382e4b198e33c793897be7c701e65577" + dependencies: + chalk "^0.5.1" + minimist "^1.1.0" + pad-left "^2.0.0" + pad-right "^0.2.2" + prettier-bytes "^1.0.3" + pretty-ms "^2.1.0" + right-now "^1.0.0" + split2 "^0.2.1" + stdout-stream "^1.4.0" + url-trim "^1.0.0" + gauge@~2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" @@ -2335,10 +2923,24 @@ generate-object-property@^1.1.0: 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" + +get-ports@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-ports/-/get-ports-1.0.3.tgz#f40bd580aca7ec0efb7b96cbfcbeb03ef894b5e8" + dependencies: + map-limit "0.0.1" + get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + getpass@^0.1.1: version "0.1.6" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" @@ -2358,7 +2960,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^5.0.15, glob@^5.0.5: +glob@^5.0.15, glob@5.0.x, glob@5.x: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: @@ -2368,7 +2970,17 @@ glob@^5.0.15, glob@^5.0.5: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.5: +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: version "7.1.1" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: @@ -2379,6 +2991,15 @@ glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" +glob@~4.3.0: + version "4.3.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.3.5.tgz#80fbb08ca540f238acce5d11d1e9bc41e75173d3" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "^2.0.1" + once "^1.3.0" + glob@3.2.11: version "3.2.11" resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" @@ -2386,22 +3007,49 @@ glob@3.2.11: inherits "2" minimatch "0.3" -global@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/global/-/global-4.3.1.tgz#5f757908c7cbabce54f386ae440e11e26b7916df" +glob@7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" dependencies: - min-document "^2.19.0" - process "~0.5.1" + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" -globals@^6.4.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" +global-modules@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" + dependencies: + global-prefix "^0.1.4" + is-windows "^0.2.0" + +global-prefix@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.4.tgz#05158db1cde2dd491b455e290eb3ab8bfc45c6e1" + dependencies: + ini "^1.3.4" + is-windows "^0.2.0" + osenv "^0.1.3" + which "^1.2.10" -globals@^9.0.0: +globals@^9.0.0, globals@^9.2.0: version "9.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.12.0.tgz#992ce90828c3a55fa8f16fada177adb64664cf9d" -graceful-fs@^4.1.2, graceful-fs@^4.1.4: +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6: version "4.1.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.9.tgz#baacba37d19d11f9d146d3578bc99958c3787e29" @@ -2417,6 +3065,20 @@ growl@1.9.2: version "1.9.2" resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" +growly@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + +handlebars@^4.0.1, handlebars@^4.0.3: + version "4.0.5" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + har-validator@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" @@ -2426,6 +3088,12 @@ har-validator@~2.0.6: is-my-json-valid "^2.12.4" pinkie-promise "^2.0.0" +has-ansi@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-0.1.0.tgz#84f265aae8c0e6a88a12d7022894b7568894c62e" + dependencies: + ansi-regex "^0.2.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -2444,12 +3112,18 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" -has@^1.0.1: +has@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" dependencies: function-bind "^1.0.2" +hash.js@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573" + dependencies: + inherits "^2.0.1" + hawk@~3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" @@ -2459,10 +3133,6 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" -he@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.0.0.tgz#6da5b265d7f2c3b5e480749168e0e159d05728da" - header-case@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/header-case/-/header-case-1.0.0.tgz#d9e335909505d56051ec16a0106821889e910781" @@ -2470,6 +3140,10 @@ header-case@^1.0.0: no-case "^2.2.0" upper-case "^1.1.3" +highlight.js@^8.6.0: + version "8.9.1" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-8.9.1.tgz#b8a9c5493212a9392f0222b649c9611497ebfb88" + history@^2.0.1: version "2.1.2" resolved "https://registry.yarnpkg.com/history/-/history-2.1.2.tgz#4aa2de897a0e4867e4539843be6ecdb2986bfdec" @@ -2496,14 +3170,7 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -home-or-tmp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" - dependencies: - os-tmpdir "^1.0.1" - user-home "^1.1.1" - -home-or-tmp@^2.0.0: +home-or-tmp@^2.0.0, home-or-tmp@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" dependencies: @@ -2514,35 +3181,15 @@ hosted-git-info@^2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" -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" - -html-entities@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" - -html-minifier@^1.0.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-1.5.0.tgz#beb05fd9cc340945865c10f40aedf469af4b1534" +html-encoding-sniffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da" dependencies: - change-case "2.3.x" - clean-css "3.4.x" - commander "2.9.x" - concat-stream "1.5.x" - he "1.0.x" - ncname "1.0.x" - relateurl "0.2.x" - uglify-js "2.6.x" + whatwg-encoding "^1.0.1" -html-webpack-plugin@^1.6.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-1.7.0.tgz#cd0c73c791bd0c8c45b24e3001be334a6b74297b" - dependencies: - bluebird "^3.0.5" - blueimp-tmpl "^2.5.5" - html-minifier "^1.0.0" - lodash "^3.10.1" +htmlescape@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" "htmlparser2@>= 3.7.3 < 4.0.0": version "3.9.2" @@ -2555,12 +3202,12 @@ html-webpack-plugin@^1.6.1: inherits "^2.0.1" readable-stream "^2.0.2" -http-browserify@^1.3.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/http-browserify/-/http-browserify-1.7.0.tgz#33795ade72df88acfbfd36773cefeda764735b20" +http-errors@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" dependencies: - Base64 "~0.2.0" inherits "~2.0.1" + statuses "1" http-errors@~1.5.0: version "1.5.0" @@ -2570,16 +3217,7 @@ http-errors@~1.5.0: setprototypeof "1.0.1" statuses ">= 1.3.0 < 2" -http-proxy-middleware@~0.17.1: - version "0.17.2" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.2.tgz#572d517a6d2fb1063a469de294eed96066352007" - dependencies: - http-proxy "^1.15.1" - is-glob "^3.0.0" - lodash "^4.16.2" - micromatch "^2.3.11" - -http-proxy@^1.15.1: +http-proxy@^1.14.0: version "1.15.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31" dependencies: @@ -2594,30 +3232,34 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.0.tgz#b3ffdfe734b2a3d4a9efd58e8654c91fce86eafd" +https-browserify@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.13: +iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -icss-replace-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5" - ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" +ignore@^3.0.9, ignore@^3.1.5: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435" + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" -immutable@^3.7.6: +immutable@^3.7.3, immutable@^3.7.6: version "3.8.1" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + indent-string@^1.1.0: version "1.2.2" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-1.2.2.tgz#db99bcc583eb6abbb1e48dcbb1999a986041cb6b" @@ -2626,6 +3268,12 @@ indent-string@^1.1.0: minimist "^1.1.0" repeating "^1.1.0" +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + 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" @@ -2634,6 +3282,10 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" +"individual@>=3.0.0 <3.1.0-0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/individual/-/individual-3.0.0.tgz#e7ca4f85f8957b018734f285750dc22ec2f9862d" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2649,13 +3301,81 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -ini@~1.3.0: +ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" -interpret@^0.6.4: - version "0.6.6" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" +inject-lr-script@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/inject-lr-script/-/inject-lr-script-2.0.0.tgz#99487bbd224ac9c18f69c65a5c6dd6fa4191e903" + dependencies: + resp-modifier "^6.0.0" + +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" + dependencies: + source-map "~0.5.3" + +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" + dependencies: + ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + figures "^1.3.5" + lodash "^4.3.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +inquirer@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.1.2.tgz#ac3ba5f06b8e7291abd9f22912c03f09cfe2dd1f" + dependencies: + ansi-escapes "^1.1.0" + chalk "^1.0.0" + cli-cursor "^1.0.1" + cli-width "^2.0.0" + external-editor "^1.0.1" + figures "^1.3.5" + lodash "^4.3.0" + mute-stream "0.0.6" + pinkie-promise "^2.0.0" + run-async "^2.2.0" + rx "^4.1.0" + string-width "^1.0.1" + strip-ansi "^3.0.0" + through "^2.3.6" + +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" + dependencies: + combine-source-map "~0.7.1" + concat-stream "~1.5.1" + is-buffer "^1.1.0" + JSONStream "^1.0.3" + lexical-scope "^1.2.0" + process "~0.11.0" + through2 "^2.0.0" + xtend "^4.0.0" + +internal-ip@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" + dependencies: + meow "^3.3.0" + +interpret@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.1.tgz#d579fb7f693b858004947af39fa0db49f795602c" invariant@^2.0.0, invariant@^2.1.0, invariant@^2.2.0, invariant@^2.2.1: version "2.2.1" @@ -2671,9 +3391,12 @@ ipaddr.js@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" -is-absolute-url@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.0.0.tgz#9c4b20b0e5c0cbef9a479a367ede6f991679f359" +is-absolute@^0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" + dependencies: + is-relative "^0.2.1" + is-windows "^0.2.0" is-arrayish@^0.2.1: version "0.2.1" @@ -2685,7 +3408,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2: +is-buffer@^1.0.2, is-buffer@^1.1.0: version "1.1.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" @@ -2695,6 +3418,12 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-ci@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + is-dotfile@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" @@ -2713,11 +3442,7 @@ is-extglob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" -is-extglob@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.0.tgz#33411a482b046bf95e6b0cb27ee2711af4cf15ad" - -is-finite@^1.0.0: +is-finite@^1.0.0, is-finite@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" dependencies: @@ -2729,31 +3454,23 @@ is-fullwidth-code-point@^1.0.0: 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" + 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" dependencies: is-extglob "^1.0.0" -is-glob@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - -is-integer@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.6.tgz#5273819fada880d123e1ac00a938e7172dd8d95e" - dependencies: - is-finite "^1.0.0" - is-lower-case@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" dependencies: lower-case "^1.1.0" -is-my-json-valid@^2.12.4: +is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4: version "2.15.0" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz#936edda3ca3c211fd98f3b2d3e08da43f7b2915b" dependencies: @@ -2768,9 +3485,21 @@ is-number@^2.0.2, is-number@^2.1.0: dependencies: kind-of "^3.0.2" -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" +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" + +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" + 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" + dependencies: + path-is-inside "^1.0.1" is-posix-bracket@^0.1.0: version "0.1.1" @@ -2780,24 +3509,40 @@ is-primitive@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" +is-relative@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" + dependencies: + is-unc-path "^0.1.1" + +is-resolvable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" + dependencies: + tryit "^1.0.1" + is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" -is-svg@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.0.1.tgz#f93ab3bf1d6bbca30e9753cd3485b1300eebc013" - dependencies: - html-comment-regex "^1.1.0" - is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" +is-unc-path@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.1.tgz#ab2533d77ad733561124c3dc0f5cd8b90054c86b" + dependencies: + unc-path-regex "^0.1.0" + is-upper-case@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" @@ -2808,14 +3553,26 @@ is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" +is-windows@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" + +is@~0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" + 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" -isarray@0.0.1: +isarray@~0.0.1, isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +isexe@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -2833,11 +3590,34 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-lib-coverage@^1.0.0: +istanbul-api@^1.0.0-aplha.10: + version "1.0.0-aplha.10" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.0.0-aplha.10.tgz#902edf5cf5404e0eba7e00ef46408488a0d3e337" + dependencies: + async "1.x" + clone "^1.0.2" + fileset "0.2.x" + istanbul-lib-coverage "^1.0.0-alpha" + istanbul-lib-hook "^1.0.0-alpha" + istanbul-lib-instrument "^1.0.0-alpha" + istanbul-lib-report "^1.0.0-alpha" + istanbul-lib-source-maps "^1.0.0-alpha" + istanbul-reports "^1.0.0-alpha" + js-yaml "3.x" + mkdirp "0.5.x" + once "1.x" + +istanbul-lib-coverage@^1.0.0, istanbul-lib-coverage@^1.0.0-alpha, istanbul-lib-coverage@^1.0.0-alpha.0: version "1.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.0.tgz#c3f9b6d226da12424064cce87fce0fb57fdfa7a2" -istanbul-lib-instrument@^1.1.4: +istanbul-lib-hook@^1.0.0-alpha: + version "1.0.0-alpha.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0-alpha.4.tgz#8c5bb9f6fbd8526e0ae6cf639af28266906b938f" + dependencies: + append-transform "^0.3.0" + +istanbul-lib-instrument@^1.0.0-alpha, istanbul-lib-instrument@^1.1.1, istanbul-lib-instrument@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.2.0.tgz#73d5d108ab7568c373fdcb7d01c1d42d565bc8c4" dependencies: @@ -2849,6 +3629,51 @@ istanbul-lib-instrument@^1.1.4: istanbul-lib-coverage "^1.0.0" semver "^5.3.0" +istanbul-lib-report@^1.0.0-alpha: + version "1.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.0.0-alpha.3.tgz#32d5f6ec7f33ca3a602209e278b2e6ff143498af" + dependencies: + async "^1.4.2" + istanbul-lib-coverage "^1.0.0-alpha" + mkdirp "^0.5.1" + path-parse "^1.0.5" + rimraf "^2.4.3" + supports-color "^3.1.2" + +istanbul-lib-source-maps@^1.0.0-alpha: + version "1.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.0.2.tgz#9e91b0e5ae6ed203f67c69a34e6e98b10bb69a49" + dependencies: + istanbul-lib-coverage "^1.0.0-alpha.0" + mkdirp "^0.5.1" + rimraf "^2.4.4" + source-map "^0.5.3" + +istanbul-reports@^1.0.0-alpha: + version "1.0.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.0.tgz#24b4eb2b1d29d50f103b369bd422f6e640aa0777" + dependencies: + handlebars "^4.0.3" + +istanbul@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + jade@0.26.3: version "0.26.3" resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" @@ -2856,6 +3681,197 @@ jade@0.26.3: commander "0.6.1" mkdirp "0.3.0" +jasmine-check@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/jasmine-check/-/jasmine-check-0.1.5.tgz#dbad7eec56261c4b3d175ada55fe59b09ac9e415" + dependencies: + testcheck "^0.1.0" + +jest-changed-files@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-16.0.0.tgz#7931deff4424182b8173d80e06800d7363b19c45" + +jest-cli@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-16.0.2.tgz#d439b28affa7189aa3d046d2af931f7ebb9af69d" + dependencies: + ansi-escapes "^1.4.0" + callsites "^2.0.0" + chalk "^1.1.1" + graceful-fs "^4.1.6" + is-ci "^1.0.9" + istanbul-api "^1.0.0-aplha.10" + istanbul-lib-coverage "^1.0.0" + istanbul-lib-instrument "^1.1.1" + jest-changed-files "^16.0.0" + jest-config "^16.0.2" + jest-environment-jsdom "^16.0.2" + jest-file-exists "^15.0.0" + jest-haste-map "^16.0.2" + jest-jasmine2 "^16.0.2" + jest-mock "^16.0.2" + jest-resolve "^16.0.2" + jest-resolve-dependencies "^16.0.2" + jest-runtime "^16.0.2" + jest-snapshot "^16.0.2" + jest-util "^16.0.2" + json-stable-stringify "^1.0.0" + node-notifier "^4.6.1" + sane "~1.4.1" + strip-ansi "^3.0.1" + throat "^3.0.0" + which "^1.1.1" + worker-farm "^1.3.1" + yargs "^5.0.0" + +jest-config@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-16.0.2.tgz#8e82a9c08846f23dc7fd42b5c0a1f596c385772a" + dependencies: + chalk "^1.1.1" + istanbul "^0.4.5" + jest-environment-jsdom "^16.0.2" + jest-environment-node "^16.0.2" + jest-jasmine2 "^16.0.2" + jest-mock "^16.0.2" + jest-resolve "^16.0.2" + jest-util "^16.0.2" + json-stable-stringify "^1.0.0" + +jest-diff@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-16.0.0.tgz#4a5d13b1e36c5b8020d5d9e69639e486a675ce14" + dependencies: + chalk "^1.1.3" + diff "^3.0.0" + jest-matcher-utils "^16.0.0" + pretty-format "~4.2.1" + +jest-environment-jsdom@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-16.0.2.tgz#548d883b68f8ed0bd6466d8703986296724c1ef7" + dependencies: + jest-mock "^16.0.2" + jest-util "^16.0.2" + jsdom "^9.8.0" + +jest-environment-node@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-16.0.2.tgz#eb7b3a4a9c63b728ce023828d4b5661aad8c7a08" + dependencies: + jest-mock "^16.0.2" + jest-util "^16.0.2" + +jest-file-exists@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/jest-file-exists/-/jest-file-exists-15.0.0.tgz#b7fefdd3f4b227cb686bb156ecc7661ee6935a88" + +jest-haste-map@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-16.0.2.tgz#4562915b25171ae2d0d75118c992f0e97536a2ed" + dependencies: + fb-watchman "^1.9.0" + graceful-fs "^4.1.6" + multimatch "^2.1.0" + worker-farm "^1.3.1" + +jest-jasmine2@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-16.0.2.tgz#c91ae170d127aae22180dbfe181d77655a5da8c3" + dependencies: + graceful-fs "^4.1.6" + jasmine-check "^0.1.4" + jest-matchers "^16.0.2" + jest-snapshot "^16.0.2" + jest-util "^16.0.2" + +jest-matcher-utils@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-16.0.0.tgz#705af3ff85944bec1c25bc813f427aff8642b0cd" + dependencies: + chalk "^1.1.3" + pretty-format "~4.2.1" + +jest-matchers@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-16.0.2.tgz#c078c28cfe05b9b1f295f9ab27b5991f1095bbbf" + dependencies: + jest-diff "^16.0.0" + jest-matcher-utils "^16.0.0" + jest-util "^16.0.2" + +jest-mock@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-16.0.2.tgz#97b533343295d0082e9474a73ac4eb474d1636fe" + +jest-resolve-dependencies@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-16.0.2.tgz#b204166d50141469d10667dc216239c0be865729" + dependencies: + jest-file-exists "^15.0.0" + jest-resolve "^16.0.2" + +jest-resolve@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-16.0.2.tgz#46b92b9c2a44aa7ddd9a6b73dc234e9503e8c609" + dependencies: + browser-resolve "^1.11.2" + jest-file-exists "^15.0.0" + jest-haste-map "^16.0.2" + resolve "^1.1.6" + +jest-runtime@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-16.0.2.tgz#a741e8d55a7b5f011bbe17a22c673a83d278a45d" + dependencies: + babel-core "^6.11.4" + babel-jest "^16.0.0" + babel-plugin-istanbul "^2.0.0" + chalk "^1.1.3" + graceful-fs "^4.1.6" + jest-config "^16.0.2" + jest-file-exists "^15.0.0" + jest-haste-map "^16.0.2" + jest-mock "^16.0.2" + jest-resolve "^16.0.2" + jest-snapshot "^16.0.2" + jest-util "^16.0.2" + json-stable-stringify "^1.0.0" + multimatch "^2.1.0" + yargs "^5.0.0" + +jest-snapshot@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-16.0.2.tgz#f137a4176d661bd4058910850191d1816bebdaae" + dependencies: + jest-diff "^16.0.0" + jest-file-exists "^15.0.0" + jest-matcher-utils "^16.0.0" + jest-util "^16.0.2" + natural-compare "^1.4.0" + pretty-format "~4.2.1" + +jest-util@^16.0.2: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-16.0.2.tgz#db5123358278e7a34a6d9f837409d649a0db5d54" + dependencies: + chalk "^1.1.1" + diff "^3.0.0" + graceful-fs "^4.1.6" + jest-file-exists "^15.0.0" + jest-mock "^16.0.2" + mkdirp "^0.5.1" + +jest@^16.0.0: + version "16.0.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-16.0.2.tgz#4a2f7f3527465168a0bafe0c3d55055188253f3a" + dependencies: + jest-cli "^16.0.2" + +jmespath@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217" + jodid25519@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" @@ -2870,11 +3886,7 @@ js-tokens@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" -js-tokens@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae" - -js-yaml@^3.0.2, js-yaml@~3.6.1: +js-yaml@^3.5.1, js-yaml@3.x: version "3.6.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" dependencies: @@ -2906,6 +3918,31 @@ jsdom@^6.5.1: xmlhttprequest ">= 1.6.0 < 2.0.0" xtend "^4.0.0" +jsdom@^9.8.0: + version "9.8.3" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.8.3.tgz#fde29c109c32a1131e0b6c65914e64198f97c370" + dependencies: + abab "^1.0.0" + acorn "^2.4.0" + acorn-globals "^1.0.4" + array-equal "^1.0.0" + content-type-parser "^1.0.1" + cssom ">= 0.3.0 < 0.4.0" + cssstyle ">= 0.2.36 < 0.3.0" + escodegen "^1.6.1" + html-encoding-sniffer "^1.0.1" + iconv-lite "^0.4.13" + nwmatcher ">= 1.3.7 < 2.0.0" + parse5 "^1.5.1" + request "^2.55.0" + sax "^1.1.4" + symbol-tree ">= 3.1.0 < 4.0.0" + tough-cookie "^2.3.1" + webidl-conversions "^3.0.1" + whatwg-encoding "^1.0.1" + whatwg-url "^3.0.0" + xml-name-validator ">= 2.0.1 < 3.0.0" + jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -2914,34 +3951,65 @@ jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" -json-loader@^0.5.3: - version "0.5.4" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.4.tgz#8baa1365a632f58a3c46d20175fc6002c96e37de" +json-fallback@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/json-fallback/-/json-fallback-0.0.1.tgz#e8e3083c3fddad0f9b5f09d3312074442580d781" json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" -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" +json-stable-stringify@^1.0.0, 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" + dependencies: + jsonify "~0.0.0" -json3@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" +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" + dependencies: + jsonify "~0.0.0" -json5@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" +"json-stringify-safe@>=5.0.0 <5.1.0-0", 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" json5@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.0.tgz#9b20715b026cbe3778fd769edccd822d8332a5b2" +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonp@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/jsonp/-/jsonp-0.2.0.tgz#95fcd751cf0a67adb9a5a9c4628ffeb97bec63e8" + dependencies: + debug "2.1.3" + +jsonp@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/jsonp/-/jsonp-0.0.4.tgz#94665a4b771aabecb8aac84135b99594756918bd" + dependencies: + debug "*" + +jsonparse@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" + jsonpointer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" +JSONStream@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.2.1.tgz#32aa5790e799481083b49b4b7fa94e23bae69bf9" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + jsonwebtoken@^5.0.0: version "5.7.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz#1c90f9a86ce5b748f5f979c12b70402b4afcddb4" @@ -2950,6 +4018,73 @@ jsonwebtoken@^5.0.0: ms "^0.7.1" xtend "^4.0.1" +jspm-github@^0.14.11: + version "0.14.11" + resolved "https://registry.yarnpkg.com/jspm-github/-/jspm-github-0.14.11.tgz#5093b3a79289d63ff6e3982f3b527878ac808d5c" + dependencies: + bluebird "^3.0.5" + expand-tilde "^1.2.0" + graceful-fs "^4.1.3" + mkdirp "^0.5.1" + netrc "^0.1.3" + request "^2.74.0" + rimraf "^2.5.4" + semver "^5.0.1" + tar-fs "^1.13.0" + which "^1.0.9" + +jspm-npm@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/jspm-npm/-/jspm-npm-0.30.0.tgz#3f3ab3c33f1ae7070a240c607c6dd0814affdc39" + dependencies: + bluebird "^3.0.5" + buffer-peek-stream "^1.0.1" + graceful-fs "^4.1.3" + mkdirp "^0.5.1" + readdirp "^2.0.0" + request "^2.58.0" + rmdir "^1.1.0" + semver "^5.0.1" + systemjs-builder "^0.15.33" + tar-fs "^1.13.0" + traceur "0.0.105" + which "^1.1.1" + +jspm-registry@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/jspm-registry/-/jspm-registry-0.4.2.tgz#a78ec3f5935bd3c9363da10b94b9c93e5b555e7d" + dependencies: + graceful-fs "^4.1.3" + rimraf "^2.3.2" + rsvp "^3.0.18" + semver "^4.3.3" + +jspm@^0.17.0-beta.13: + version "0.17.0-beta.31" + resolved "https://registry.yarnpkg.com/jspm/-/jspm-0.17.0-beta.31.tgz#34cfd27a0f8d915e788356fd57f2c613595c4ae7" + dependencies: + bluebird "^3.0.5" + chalk "^1.1.1" + core-js "^1.2.6" + glob "^6.0.1" + graceful-fs "^4.1.2" + jspm-github "^0.14.11" + jspm-npm "^0.30.0" + jspm-registry "^0.4.1" + liftoff "^2.2.0" + minimatch "^3.0.0" + mkdirp "~0.5.1" + ncp "^2.0.0" + proper-lockfile "^1.1.2" + request "^2.67.0" + rimraf "^2.4.4" + sane "^1.3.3" + semver "^5.1.0" + systemjs "0.19.40" + systemjs-builder "0.15.33" + traceur "0.0.105" + uglify-js "^2.6.1" + jsprim@^1.2.2: version "1.3.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" @@ -2968,6 +4103,13 @@ jstransform@^11.0.3: object-assign "^2.0.0" source-map "^0.4.2" +jsx-ast-utils@^1.3.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.3.tgz#ccfdbe0320ba03f7a1fc4e67ceaf7e2cc0169721" + dependencies: + acorn-jsx "^3.0.1" + object-assign "^4.1.0" + jszip@^2.4.0: version "2.6.1" resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.1.tgz#b88f3a7b2e67a2a048152982c7a3756d9c4828f0" @@ -3013,6 +4155,14 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +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" + dependencies: + inherits "^2.0.1" + isarray "~0.0.1" + stream-splicer "^2.0.0" + lazy-cache@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" @@ -3035,23 +4185,43 @@ leaflet@^0.7.7: version "0.7.7" resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-0.7.7.tgz#1e352ba54e63d076451fa363c900890cb2cf75ee" -leven@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3" - -levn@~0.3.0: +levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" +lexical-scope@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/lexical-scope/-/lexical-scope-1.2.0.tgz#fcea5edc704a4b3a8796cdca419c3a0afaf22df4" + dependencies: + astw "^2.0.0" + lie@^3.0.1, lie@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.0.tgz#65e0139eaef9ae791a1f5c8c53692c8d3b4718f4" dependencies: immediate "~3.0.5" +liftoff@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" + dependencies: + extend "^3.0.0" + findup-sync "^0.4.2" + fined "^1.0.1" + flagged-respawn "^0.3.2" + lodash.isplainobject "^4.0.4" + lodash.isstring "^4.0.1" + lodash.mapvalues "^4.4.0" + rechoir "^0.6.2" + resolve "^1.1.7" + +livereload-js@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.2.2.tgz#6c87257e648ab475bc24ea257457edcc1f8d0bc2" + 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" @@ -3062,19 +4232,18 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" -loader-utils@^0.2.11, loader-utils@^0.2.7, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5, loader-utils@0.2.x: - version "0.2.16" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.16.tgz#f08632066ed8282835dff88dfb52704765adee6d" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - lodash-es@^4.2.1: version "4.16.6" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.16.6.tgz#c8552faedaa4d1d591de8da9b3980ef1c52efa08" +lodash._arraycopy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arraycopy/-/lodash._arraycopy-3.0.0.tgz#76e7b7c1f1fb92547374878a562ed06a3e50f6e1" + +lodash._arrayeach@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._arrayeach/-/lodash._arrayeach-3.0.0.tgz#bab156b2a90d3f1bbd5c653403349e5e5933ef9e" + lodash._baseassign@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e" @@ -3082,17 +4251,21 @@ lodash._baseassign@^3.0.0: lodash._basecopy "^3.0.0" lodash.keys "^3.0.0" +lodash._baseclone@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-3.3.0.tgz#303519bf6393fe7e42f34d8b630ef7794e3542b7" + dependencies: + lodash._arraycopy "^3.0.0" + lodash._arrayeach "^3.0.0" + lodash._baseassign "^3.0.0" + lodash._basefor "^3.0.0" + lodash.isarray "^3.0.0" + lodash.keys "^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" -lodash._baseflatten@^3.0.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/lodash._baseflatten/-/lodash._baseflatten-3.1.4.tgz#0770ff80131af6e34f3b511796a7ba5214e65ff7" - dependencies: - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - lodash._basefor@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" @@ -3101,63 +4274,28 @@ lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" -lodash._createassigner@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" - dependencies: - lodash._bindcallback "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash.restparam "^3.0.0" - -lodash._createcompounder@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._createcompounder/-/lodash._createcompounder-3.0.0.tgz#5dd2cb55372d6e70e0e2392fb2304d6631091075" - dependencies: - lodash.deburr "^3.0.0" - lodash.words "^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" -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - -lodash._pickbyarray@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._pickbyarray/-/lodash._pickbyarray-3.0.2.tgz#1f898d9607eb560b0e167384b77c7c6d108aa4c5" - -lodash._pickbycallback@^3.0.0: +lodash._reinterpolate@~3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._pickbycallback/-/lodash._pickbycallback-3.0.0.tgz#ff61b9a017a7b3af7d30e6c53de28afa19b8750a" - dependencies: - lodash._basefor "^3.0.0" - lodash.keysin "^3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" +lodash.assign@^4.1.0, lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" -lodash.assign@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa" - dependencies: - lodash._baseassign "^3.0.0" - lodash._createassigner "^3.0.0" - lodash.keys "^3.0.0" - -lodash.camelcase@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-3.0.1.tgz#932c8b87f8a4377897c67197533282f97aeac298" - dependencies: - lodash._createcompounder "^3.0.0" +lodash.assignwith@^4.0.7: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" -lodash.deburr@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-3.2.0.tgz#6da8f54334a366a7cf4c4c76ef8d80aa1b365ed5" +lodash.clonedeep@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz#a0a1e40d82a5ea89ff5b147b8444ed63d92827db" dependencies: - lodash._root "^3.0.0" + lodash._baseclone "^3.0.0" + lodash._bindcallback "^3.0.0" lodash.indexof@^4.0.5: version "4.0.5" @@ -3171,6 +4309,26 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" +lodash.isempty@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" + +lodash.isequal@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" + +lodash.isobject@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" + +lodash.isplainobject@^4.0.4: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -3179,45 +4337,73 @@ lodash.keys@^3.0.0, lodash.keys@^3.1.2: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.keysin@^3.0.0: - version "3.0.8" - resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" - dependencies: - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" +lodash.map@^4.5.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" -lodash.pick@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-3.1.0.tgz#f252a855b2046b61bcd3904b26f76bd2efc65550" - dependencies: - lodash._baseflatten "^3.0.0" - lodash._bindcallback "^3.0.0" - lodash._pickbyarray "^3.0.0" - lodash._pickbycallback "^3.0.0" - lodash.restparam "^3.0.0" +lodash.mapvalues@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" +lodash.memoize@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.memoize@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f" + +lodash.merge@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + +lodash.pickby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" lodash.set@^4.0.0: version "4.3.2" resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" -lodash.words@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-3.2.0.tgz#4e2a8649bc08745b17c695b1a3ce8fee596623b3" +lodash.template@^4.2.4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" dependencies: - lodash._root "^3.0.0" + lodash._reinterpolate "~3.0.0" + lodash.templatesettings "^4.0.0" -lodash@^3.10.0, lodash@^3.10.1, lodash@^3.9.3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" +lodash.templatesettings@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + dependencies: + lodash._reinterpolate "~3.0.0" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.16.1, lodash@^4.16.2, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.6.1: +lodash.uniq@^4.3.0, lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.16.1, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.16.6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" +lodash@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.5.0.tgz#19bb3f4d51278f0b8c818ed145c74ecf9fe40e6d" + +lodash@4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.15.0.tgz#3162391d8f0140aa22cf8f6b3c34d6b7f63d3aa9" + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + lolex@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" @@ -3236,6 +4422,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0: dependencies: js-tokens "^2.0.0" +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + lower-case-first@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" @@ -3250,14 +4443,85 @@ lru-cache@^2.7.0, lru-cache@2: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" -macaddress@^0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + +map-cache@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-limit@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/map-limit/-/map-limit-0.0.1.tgz#eb7961031c0f0e8d001bf2d56fab685d58822f38" + dependencies: + once "~1.3.0" -map-obj@^1.0.0: +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" +marked-terminal@^1.6.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-1.7.0.tgz#c8c460881c772c7604b64367007ee5f77f125904" + dependencies: + cardinal "^1.0.0" + chalk "^1.1.3" + cli-table "^0.3.1" + lodash.assign "^4.2.0" + node-emoji "^1.4.1" + +marked@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" + +mastarm: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mastarm/-/mastarm-2.0.0.tgz#4688e28616849c9b4438dd002e8b2c426bafed7f" + dependencies: + aws-sdk "^2.4.2" + babel-core "^6.10.4" + babel-eslint "^7.0.0" + babel-jest "^16.0.0" + babel-plugin-add-module-exports "^0.2.1" + babel-plugin-transform-runtime "^6.9.0" + babel-preset-env "^0.0.6" + babel-preset-react "^6.5.0" + babel-preset-stage-2 "^6.17.0" + babelify "^7.3.0" + browserify "^13.0.1" + browserify-markdown "1.0.0" + budo "^9.0.0" + chokidar "^1.6.0" + commander "^2.9.0" + commitizen "^2.8.2" + concat-stream "^1.5.1" + cz-conventional-changelog "^1.1.6" + envify "^3.4.1" + errorify "^0.3.1" + exorcist "^0.4.0" + http-proxy "^1.14.0" + isomorphic-fetch "^2.2.1" + jest "^16.0.0" + lodash.uniq "^4.5.0" + mime "^1.3.4" + mkdirp "^0.5.1" + postcss "^5.0.21" + postcss-cssnext "^2.6.0" + postcss-import "^8.1.2" + postcss-reporter "^1.3.3" + postcss-safe-parser "^2.0.0" + rimraf "^2.5.4" + standard "^8.3.0" + standard-engine "^5.0.0" + through2 "^2.0.1" + uglifyify "^3.0.2" + uuid "^2.0.2" + watchify "^3.7.0" + yamljs "^0.2.8" + material-colors@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.0.tgz#956f0c87660882333cff7b7557efb7b03592820e" @@ -3272,16 +4536,20 @@ media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" -memory-fs@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" - -memory-fs@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.3.0.tgz#7bcc6b629e3a43e871d7e29aca6ae8a7f15cbb20" +meow@^3.3.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" meow@~2.0.0: version "2.0.0" @@ -3296,7 +4564,7 @@ merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" -merge@^1.2.0: +merge@^1.1.3, merge@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" @@ -3308,7 +4576,7 @@ mgrs@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/mgrs/-/mgrs-0.0.3.tgz#3058d38ae92e1abfbf74b32a8f6cb5225a6eaa05" -micromatch@^2.1.5, micromatch@^2.3.11: +micromatch@^2.1.5, micromatch@^2.2.0, micromatch@^2.3.11, micromatch@^2.3.7: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -3326,7 +4594,14 @@ micromatch@^2.1.5, micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -"mime-db@>= 1.24.0 < 2", mime-db@~1.24.0: +miller-rabin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@~1.24.0: version "1.24.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.24.0.tgz#e2d13f939f0016c6e4e9ad25a8652f126c467f0c" @@ -3340,17 +4615,11 @@ mime@^1.3.4, mime@1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" -mime@1.2.x: - version "1.2.11" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.2.11.tgz#58203eed86e3a5ef17aed2b7d9ebd47f0a60dd10" - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - dependencies: - dom-walk "^0.1.0" +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -minimatch@^2.0.3: +minimatch@^2.0.1, minimatch@2.x: version "2.0.10" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" dependencies: @@ -3369,7 +4638,7 @@ minimatch@0.3: lru-cache "2" sigmund "~1.0.0" -minimist@^1.1.0, minimist@^1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -3377,11 +4646,15 @@ minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" +minimist@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.5.tgz#d7aa327bcecf518f9106ac6b8f003fa3bcea8566" + minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.0, mkdirp@~0.5.1, mkdirp@0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.1, mkdirp@0.5.1, mkdirp@0.5.x: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -3410,6 +4683,33 @@ mocha@^2.3.3: supports-color "1.2.0" to-iso-string "0.0.2" +module-deps@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.0.8.tgz#55fd70623399706c3288bef7a609ff1e8c0ed2bb" + dependencies: + browser-resolve "^1.7.0" + cached-path-relative "^1.0.0" + concat-stream "~1.5.0" + defined "^1.0.0" + detective "^4.0.0" + duplexer2 "^0.1.2" + inherits "^2.0.1" + JSONStream "^1.0.3" + parents "^1.0.0" + readable-stream "^2.0.2" + resolve "^1.1.3" + stream-combiner2 "^1.1.1" + subarg "^1.0.0" + through2 "^2.0.0" + xtend "^4.0.0" + +mold-source-map@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/mold-source-map/-/mold-source-map-0.4.0.tgz#cf67e0b31c47ab9badb5c9c25651862127bb8317" + dependencies: + convert-source-map "^1.1.0" + through "~2.2.7" + moment-timezone@^0.5.3: version "0.5.7" resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.7.tgz#1305bcada16f046dbbc7ac89abf66effff886cb5" @@ -3424,30 +4724,67 @@ ms@^0.7.1: version "0.7.2" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" +ms@0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" + ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" +multimatch@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" + dependencies: + array-differ "^1.0.0" + array-union "^1.0.1" + arrify "^1.0.0" + minimatch "^3.0.0" + +mute-stream@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" + +mute-stream@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" + nan@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" -ncname@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/ncname/-/ncname-1.0.0.tgz#5b57ad18b1ca092864ef62b0b1ed8194f383b71c" - dependencies: - xml-char-classes "^1.0.0" +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +nave@~0.5.1: + version "0.5.3" + resolved "https://registry.yarnpkg.com/nave/-/nave-0.5.3.tgz#5acec72375856e5c76c83bd21a68d713eb5f1ba4" + +ncp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +netrc@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" + no-case@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.0.tgz#ca2825ccb76b18e6f79d573dcfbf1eace33dd164" dependencies: lower-case "^1.1.1" +node-emoji@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.4.1.tgz#c9fa0cf91094335bcb967a6f42b2305c15af2ebc" + dependencies: + string.prototype.codepointat "^0.2.0" + node-fetch@^1.0.1: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -3455,33 +4792,21 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" -node-libs-browser@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-0.6.0.tgz#244806d44d319e048bc8607b5cc4eaf9a29d2e3c" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + +node-notifier@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-4.6.1.tgz#056d14244f3dcc1ceadfe68af9cff0c5473a33f3" dependencies: - assert "^1.1.1" - browserify-zlib "~0.1.4" - buffer "^4.9.0" - console-browserify "^1.1.0" - constants-browserify "0.0.1" - crypto-browserify "~3.2.6" - domain-browser "^1.1.1" - events "^1.0.0" - http-browserify "^1.3.2" - https-browserify "0.0.0" - os-browserify "~0.1.2" - path-browserify "0.0.0" - process "^0.11.0" - punycode "^1.2.4" - querystring-es3 "~0.2.0" - readable-stream "^1.1.13" - stream-browserify "^1.0.0" - string_decoder "~0.10.25" - timers-browserify "^1.0.1" - tty-browserify "0.0.0" - url "~0.10.1" - util "~0.10.3" - vm-browserify "0.0.4" + cli-usage "^0.1.1" + growly "^1.2.0" + lodash.clonedeep "^3.0.0" + minimist "^1.1.1" + semver "^5.1.0" + shellwords "^0.1.0" + which "^1.0.5" node-pre-gyp@^0.6.29: version "0.6.31" @@ -3501,13 +4826,26 @@ node-uuid@~1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" -nopt@~3.0.6: +node.extend@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.0.8.tgz#bab04379f7383f4587990c9df07b6a7f65db772b" + dependencies: + is "~0.2.6" + object-keys "~0.4.0" + +node.flow@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/node.flow/-/node.flow-1.2.3.tgz#e1c44a82aeca8d78b458a77fb3dc642f2eba2649" + dependencies: + node.extend "1.0.8" + +nopt@~3.0.6, nopt@3.x: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: abbrev "1" -normalize-package-data@^2.3.2: +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: version "2.3.5" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df" dependencies: @@ -3524,15 +4862,6 @@ normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" -normalize-url@^1.4.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.7.0.tgz#d82452d98d38821cffddab4d77a5f8d20ce66db0" - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - npmlog@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.0.tgz#e094503961c70c1774eb76692080e8d578a9f88f" @@ -3554,7 +4883,7 @@ numeral@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62" -"nwmatcher@>= 1.3.6 < 2.0.0": +"nwmatcher@>= 1.3.6 < 2.0.0", "nwmatcher@>= 1.3.7 < 2.0.0": version "1.3.9" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.3.9.tgz#8bab486ff7fa3dfd086656bbe8b17116d3692d2a" @@ -3570,10 +4899,14 @@ object-assign@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@4.x: +object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@4.x: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + object-path@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.2.tgz#74bf3b3c5a7f2024d75e333f12021353fa9d485e" @@ -3591,34 +4924,40 @@ on-finished@~2.3.0: 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" - -once@^1.3.0: +once@^1.3.0, once@^1.3.1, once@^1.3.2, once@1.x: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" -once@~1.3.3: +once@~1.3.0, once@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" dependencies: wrappy "1" -open@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" +onecolor@~2.4.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-2.4.2.tgz#a53ec3ff171c3446016dd5210d1a1b544bf7d874" + +onetime@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + +opn@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/opn/-/opn-3.0.3.tgz#b6d99e7399f78d65c3baaffef1fb288e9b85243a" + dependencies: + object-assign "^4.0.1" -optimist@~0.6.0, optimist@~0.6.1: +optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" dependencies: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" dependencies: @@ -3629,17 +4968,11 @@ optionator@^0.8.1: type-check "~0.3.2" wordwrap "~1.0.0" -original@>=0.0.5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" - dependencies: - url-parse "1.0.x" - -os-browserify@~0.1.2: +os-browserify@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.1.2.tgz#49ca0293e0b19590a5f5de10c7f265a617d8fe54" -os-homedir@^1.0.0: +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" @@ -3649,17 +4982,38 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" -os-tmpdir@^1.0.1: +os-shim@^0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -output-file-sync@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" +osenv@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.3.tgz#83cf05c6d6458fc4d5ac6362ea325d92f2754217" dependencies: - graceful-fs "^4.1.4" - mkdirp "^0.5.1" - object-assign "^4.1.0" + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +outpipe@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/outpipe/-/outpipe-1.1.1.tgz#50cf8616365e87e031e29a5ec9339a3da4725fa2" + dependencies: + shell-quote "^1.4.2" + +pad-left@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pad-left/-/pad-left-2.1.0.tgz#16e6a3b2d44a8e138cb0838cc7cb403a4fc9e994" + dependencies: + repeat-string "^1.5.4" + +pad-right@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/pad-right/-/pad-right-0.2.2.tgz#6fbc924045d244f2a2a244503060d3bfc6009774" + dependencies: + repeat-string "^1.5.2" pako@~0.2.0: version "0.2.9" @@ -3669,18 +5023,36 @@ pako@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.3.tgz#5f515b0c6722e1982920ae8005eacb0b7ca73ccf" -param-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-1.1.2.tgz#dcb091a43c259b9228f1c341e7b6a44ea0bf9743" - dependencies: - sentence-case "^1.1.2" - param-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.0.tgz#2619f90fd6c829ed0b958f1c84ed03a745a6d70a" 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" + dependencies: + path-platform "~0.11.15" + +parse-asn1@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-filepath@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" + dependencies: + is-absolute "^0.2.3" + map-cache "^0.2.0" + path-root "^0.1.1" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -3696,7 +5068,11 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse5@^1.4.2: +parse-ms@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" + +parse5@^1.4.2, parse5@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" @@ -3704,17 +5080,10 @@ parsedbf@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/parsedbf/-/parsedbf-0.1.2.tgz#7f9aaeff89961df9bea0d60957856dbc92f8a8ce" -parseurl@~1.3.1: +parseurl@~1.3.0, parseurl@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56" -pascal-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-1.1.2.tgz#3e5d64a20043830a7c49344c2d74b41be0c9c99b" - dependencies: - camel-case "^1.1.1" - upper-case-first "^1.1.0" - pascal-case@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-2.0.0.tgz#39c248bde5a8dc02d5160696bdb01e044d016ee1" @@ -3722,27 +5091,23 @@ pascal-case@^2.0.0: camel-case "^3.0.0" upper-case-first "^1.1.0" -path-browserify@0.0.0: +password-sheriff@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/password-sheriff/-/password-sheriff-1.0.1.tgz#fd63fbb44714258a26419f4800c3121e0dcb40b2" + dependencies: + underscore "^1.6.0" + +path-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" -path-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/path-case/-/path-case-1.1.2.tgz#50ce6ba0d3bed3dd0b5c2a9c4553697434409514" - dependencies: - sentence-case "^1.1.2" - path-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-case/-/path-case-2.1.0.tgz#5ac491de642936e5dfe0e18d16c461b8be8cf073" dependencies: no-case "^2.2.0" -path-exists@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" - -path-exists@^2.0.0: +path-exists@^2.0.0, path-exists@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" dependencies: @@ -3752,6 +5117,28 @@ 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" +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" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-platform@~0.11.15: + version "0.11.15" + resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" + +path-root-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + +path-root@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + dependencies: + path-root-regex "^0.1.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" @@ -3764,15 +5151,24 @@ path-type@^1.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -pbkdf2-compat@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288" +pbkdf2@^3.0.3: + version "3.0.9" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693" + dependencies: + create-hmac "^1.1.2" + +pem@^1.8.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/pem/-/pem-1.8.3.tgz#e78fc65469698c7e85e4d62dd1018926ed89f3bd" + dependencies: + os-tmpdir "^1.0.1" + which "^1.2.4" performance-now@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" -pify@^2.0.0: +pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3786,17 +5182,63 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" +pixrem@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pixrem/-/pixrem-3.0.2.tgz#30d1bafb4c3bdce8e9bb4bd56a13985619320c34" dependencies: - find-up "^1.0.0" + browserslist "^1.0.0" + postcss "^5.0.0" + reduce-css-calc "^1.2.7" + +pkg-config@^1.0.1, pkg-config@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pkg-config/-/pkg-config-1.1.1.tgz#557ef22d73da3c8837107766c52eadabde298fe4" + dependencies: + debug-log "^1.0.0" + find-root "^1.0.0" + xtend "^4.0.1" + +pkg-resolve@^0.1.7: + version "0.1.14" + resolved "https://registry.yarnpkg.com/pkg-resolve/-/pkg-resolve-0.1.14.tgz#329b2e76ccbb372e22e6a3a41cb30ab0457836ba" + dependencies: + jspm "^0.17.0-beta.13" + resolve "^1.1.7" + +pleeease-filters@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pleeease-filters/-/pleeease-filters-3.0.0.tgz#35a4d4c2086413eabc2ce17aaa2ec29054e3075c" + dependencies: + onecolor "~2.4.0" + postcss "^5.0.4" + +plur@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156" + +pluralize@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" polyline@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/polyline/-/polyline-0.2.0.tgz#4f2b716ca81134a6cbaa488975d236ecb1cc2840" -postcss-calc@^5.2.0: +postcss-apply@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/postcss-apply/-/postcss-apply-0.3.0.tgz#a2f37c5bdfa881e4c15f4f245ec0cd96dd2e70d5" + dependencies: + balanced-match "^0.4.1" + postcss "^5.0.21" + +postcss-attribute-case-insensitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-1.0.1.tgz#ceb73777e106167eb233f1938c9bd9f2e697308d" + dependencies: + postcss "^5.1.1" + postcss-selector-parser "^2.2.0" + +postcss-calc@^5.0.0: version "5.3.1" resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" dependencies: @@ -3804,194 +5246,206 @@ postcss-calc@^5.2.0: postcss-message-helpers "^2.0.0" reduce-css-calc "^1.2.6" -postcss-colormin@^2.1.8: - version "2.2.1" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.1.tgz#dc5421b6ae6f779ef6bfd47352b94abe59d0316b" - dependencies: - colormin "^1.0.5" - postcss "^5.0.13" - postcss-value-parser "^3.2.3" - -postcss-convert-values@^2.3.4: - version "2.4.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.4.1.tgz#45dce4d4e33b7d967b97a4d937f270ea98d2fe7a" +postcss-color-function@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-function/-/postcss-color-function-2.0.1.tgz#9ad226f550e8a7c7f8b8a77860545b6dd7f55241" dependencies: - postcss "^5.0.11" - postcss-value-parser "^3.1.2" + css-color-function "^1.2.0" + postcss "^5.0.4" + postcss-message-helpers "^2.0.0" + postcss-value-parser "^3.3.0" -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" +postcss-color-gray@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-3.0.0.tgz#dd65f7ad7bec4b63b458a5de88d6dd49c86cbf7b" dependencies: - postcss "^5.0.14" + color "^0.7.3" + postcss "^5.0.4" + postcss-message-helpers "^2.0.0" + reduce-function-call "^1.0.1" -postcss-discard-duplicates@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.0.1.tgz#5fae3f1a71df3e19cffb37309d1a7dba56c4589c" +postcss-color-hex-alpha@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-2.0.0.tgz#44fd6ecade66028648c881cb6504cdcbfdc6cd09" dependencies: + color "^0.10.1" postcss "^5.0.4" + postcss-message-helpers "^2.0.0" -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" +postcss-color-hwb@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-hwb/-/postcss-color-hwb-2.0.0.tgz#2c0d30a9563158f41eb3f6bddfdf1dae9cadae37" dependencies: - postcss "^5.0.14" + color "^0.10.1" + postcss "^5.0.4" + postcss-message-helpers "^2.0.0" + reduce-function-call "^1.0.1" -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" +postcss-color-rebeccapurple@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-2.0.0.tgz#5a88225aeb924a1e3e8e2ea066c5cf4fe39d818a" dependencies: - postcss "^5.0.16" + color "^0.9.0" + postcss "^5.0.4" -postcss-discard-unused@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.2.tgz#5d72f7d05d11de0a9589e001958067ccae1b4931" +postcss-color-rgba-fallback@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-color-rgba-fallback/-/postcss-color-rgba-fallback-2.2.0.tgz#6d29491be5990a93173d47e7c76f5810b09402ba" dependencies: - postcss "^5.0.14" - uniqs "^2.0.0" + postcss "^5.0.0" + postcss-value-parser "^3.0.2" + rgb-hex "^1.0.0" -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" +postcss-cssnext@^2.6.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-2.8.0.tgz#fbaef792185967457812f66355d0c24707bb8cf3" dependencies: + autoprefixer "^6.0.2" + caniuse-api "^1.3.2" + chalk "^1.1.1" + pixrem "^3.0.0" + pleeease-filters "^3.0.0" postcss "^5.0.4" - uniqid "^4.0.0" - -postcss-loader@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-0.6.0.tgz#984f9c9add2f8b153d754909dad04fbbb6b02f39" + postcss-apply "^0.3.0" + postcss-attribute-case-insensitive "^1.0.1" + postcss-calc "^5.0.0" + postcss-color-function "^2.0.0" + postcss-color-gray "^3.0.0" + postcss-color-hex-alpha "^2.0.0" + postcss-color-hwb "^2.0.0" + postcss-color-rebeccapurple "^2.0.0" + postcss-color-rgba-fallback "^2.0.0" + postcss-custom-media "^5.0.0" + postcss-custom-properties "^5.0.0" + postcss-custom-selectors "^3.0.0" + postcss-font-variant "^2.0.0" + postcss-initial "^1.3.1" + postcss-media-minmax "^2.1.0" + postcss-nesting "^2.0.5" + postcss-pseudo-class-any-link "^1.0.0" + postcss-pseudoelements "^3.0.0" + postcss-replace-overflow-wrap "^1.0.0" + postcss-selector-matches "^2.0.0" + postcss-selector-not "^2.0.0" + +postcss-custom-media@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-5.0.1.tgz#138d25a184bf2eb54de12d55a6c01c30a9d8bd81" dependencies: - loader-utils "^0.2.11" postcss "^5.0.0" -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" +postcss-custom-properties@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-5.0.1.tgz#e07d4f6c78e547cf04274f120f490d236e33ea19" dependencies: - has "^1.0.1" - postcss "^5.0.10" - postcss-value-parser "^3.1.1" + balanced-match "~0.1.0" + postcss "^5.0.0" -postcss-merge-longhand@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.1.tgz#ff59b5dec6d586ce2cea183138f55c5876fa9cdc" +postcss-custom-selectors@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-3.0.0.tgz#8f81249f5ed07a8d0917cf6a39fe5b056b7f96ac" dependencies: - postcss "^5.0.4" + balanced-match "^0.2.0" + postcss "^5.0.0" + postcss-selector-matches "^2.0.0" -postcss-merge-rules@^2.0.3: - version "2.0.10" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.0.10.tgz#54b360be804e7e69a5c7222635247b92a3569e9b" +postcss-font-variant@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-2.0.1.tgz#7ca29103f59fa02ca3ace2ca22b2f756853d4ef8" dependencies: postcss "^5.0.4" - vendors "^1.0.0" - -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" -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" +postcss-import@^8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-8.1.2.tgz#f1dbcce590c93b536a121ffedcb63f1b751749f9" dependencies: object-assign "^4.0.1" - postcss "^5.0.4" - postcss-value-parser "^3.0.2" + pkg-resolve "^0.1.7" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + read-cache "^1.0.0" + resolve "^1.1.7" -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" +postcss-initial@^1.3.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-1.5.2.tgz#61eb5ad871e7991aadfb3f497b16fe61aea92ffb" dependencies: - postcss "^5.0.12" - postcss-value-parser "^3.3.0" + lodash.template "^4.2.4" + postcss "^5.0.19" -postcss-minify-params@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.0.5.tgz#82d602643b8616a61fb3634d7ede0289836d67f9" +postcss-media-minmax@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-2.1.2.tgz#444c5cf8926ab5e4fd8a2509e9297e751649cdf8" dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.2" - postcss-value-parser "^3.0.2" - uniqs "^2.0.0" + postcss "^5.0.4" -postcss-minify-selectors@^2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.0.5.tgz#4e1f966fb49c95266804016ba9a3c6645bb601e0" - dependencies: - alphanum-sort "^1.0.2" - postcss "^5.0.14" - postcss-selector-parser "^2.0.0" +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" -postcss-modules-extract-imports@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341" +postcss-nesting@^2.0.5: + version "2.3.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-2.3.1.tgz#94a6b6a4ef707fbec20a87fee5c957759b4e01cf" dependencies: - postcss "^5.0.4" + postcss "^5.0.19" -postcss-modules-local-by-default@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce" +postcss-pseudo-class-any-link@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-1.0.0.tgz#903239196401d335fe73ac756186fa62e693af26" dependencies: - css-selector-tokenizer "^0.6.0" - postcss "^5.0.4" + postcss "^5.0.3" + postcss-selector-parser "^1.1.4" -postcss-modules-scope@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29" +postcss-pseudoelements@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudoelements/-/postcss-pseudoelements-3.0.0.tgz#6c682177c7900ba053b6df17f8c590284c7b8bbc" dependencies: - css-selector-tokenizer "^0.6.0" postcss "^5.0.4" -postcss-modules-values@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1" - dependencies: - icss-replace-symbols "^1.0.2" - postcss "^5.0.14" - -postcss-normalize-charset@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.0.tgz#2fbd30e12248c442981d31ea2484d46fd0628970" +postcss-replace-overflow-wrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-1.0.0.tgz#f0a03b31eab9636a6936bfd210e2aef1b434a643" dependencies: - postcss "^5.0.5" + postcss "^5.0.16" -postcss-normalize-url@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.7.tgz#6bd90d0a4bc5a1df22c26ea65c53257dc3829f4e" +postcss-reporter@^1.3.3: + version "1.4.1" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-1.4.1.tgz#c136f0a5b161915f379dd3765c61075f7e7b9af2" dependencies: - is-absolute-url "^2.0.0" - normalize-url "^1.4.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" + chalk "^1.0.0" + lodash "^4.1.0" + log-symbols "^1.0.2" + postcss "^5.0.0" -postcss-ordered-values@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.2.tgz#be8b511741fab2dac8e614a2302e9d10267b0771" +postcss-safe-parser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-2.0.0.tgz#5a629fe1363225a3a2b4b1f657b59d3462455c6b" dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.1" + postcss "^5.2.0" -postcss-reduce-idents@^2.2.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.3.1.tgz#024e8e219f52773313408573db9645ba62d2d2fe" +postcss-selector-matches@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-2.0.5.tgz#fa0f43be57b68e77aa4cd11807023492a131027f" dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.2" + balanced-match "^0.4.2" + postcss "^5.0.0" -postcss-reduce-initial@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.0.tgz#8f739b938289ef2e48936d7101783e4741ca9bbb" +postcss-selector-not@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-2.0.0.tgz#c73ad21a3f75234bee7fee269e154fd6a869798d" dependencies: - postcss "^5.0.4" + balanced-match "^0.2.0" + postcss "^5.0.0" -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" +postcss-selector-parser@^1.1.4: + version "1.3.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-1.3.3.tgz#d2ee19df7a64f8ef21c1a71c86f7d4835c88c281" dependencies: - has "^1.0.1" - postcss "^5.0.8" - postcss-value-parser "^3.0.1" + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" -postcss-selector-parser@^2.0.0: +postcss-selector-parser@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.1.tgz#fdbf696103b12b0a64060e5610507f410491f7c8" dependencies: @@ -3999,35 +5453,11 @@ postcss-selector-parser@^2.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-svgo@^2.1.1: - version "2.1.5" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.5.tgz#46fc0363f01bab6a36a9abb01c229fcc45363094" - dependencies: - is-svg "^2.0.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - svgo "^0.7.0" - -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" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -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: +postcss-value-parser@^3.0.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" -postcss-zindex@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.1.1.tgz#ea3fbe656c9738aa8729e2ee96ec2a46089b720f" - dependencies: - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss@^5.0.0, 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.4: +postcss@^5.0.0, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.21, postcss@^5.0.3, postcss@^5.0.4, postcss@^5.1.1, postcss@^5.2.0, postcss@^5.2.4: version "5.2.5" resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.5.tgz#ec428c27dffc7fac65961340a9b022fa4af5f056" dependencies: @@ -4040,17 +5470,25 @@ prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" -prepend-http@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -prettysize@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-0.0.3.tgz#14afff6a645e591a4ddf1c72919c23b4146181a1" +prettier-bytes@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.3.tgz#932b31c23efddb36fc66a82dcef362af3122982f" + +pretty-format@~4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-4.2.2.tgz#f80bf8d98a6f4d20997a51d18bf331f2ad789a64" + +pretty-ms@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-2.1.0.tgz#4257c256df3fb0b451d6affaab021884126981dc" + dependencies: + is-finite "^1.0.1" + parse-ms "^1.0.0" + plur "^1.0.0" private@^0.1.6, private@~0.1.5: version "0.1.6" @@ -4060,13 +5498,13 @@ 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" -process@^0.11.0, process@~0.11.0: +process@~0.11.0: version "0.11.9" resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1" -process@~0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" +progress@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" proj4@^2.1.4: version "2.3.15" @@ -4080,6 +5518,15 @@ promise@^7.0.3, promise@^7.1.1: dependencies: asap "~2.0.3" +proper-lockfile@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-1.2.0.tgz#ceff5dd89d3e5f10fb75e1e8e76bc75801a59c34" + dependencies: + err-code "^1.0.0" + extend "^3.0.0" + graceful-fs "^4.1.2" + retry "^0.10.0" + proxy-addr@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" @@ -4091,7 +5538,24 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" -punycode@^1.2.4, punycode@^1.4.1: +public-encrypt@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.1.tgz#f1f1409fb9bd1085bbdb576b43b84ec4b5eadc1a" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +punycode@^1.3.2, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -4107,6 +5571,14 @@ qs@^6.2.1, qs@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" +qs@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" + +qs@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" + qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" @@ -4117,7 +5589,7 @@ query-string@^3.0.0: dependencies: strict-uri-encode "^1.0.0" -query-string@^4.1.0, query-string@^4.2.2: +query-string@^4.2.2: version "4.2.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.2.3.tgz#9f27273d207a25a8ee4c7b8c74dcd45d556db822" dependencies: @@ -4128,14 +5600,10 @@ querystring-es3@~0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" -querystring@^0.2.0, querystring@0.2.0: +querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" -querystringify@0.0.x: - version "0.0.4" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" - quickselect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-1.0.0.tgz#02630818f9aae4ecab26f0103f98d061c17c58f3" @@ -4153,11 +5621,15 @@ randomatic@^1.1.3: is-number "^2.0.2" kind-of "^3.0.2" -range-parser@^1.0.3, range-parser@~1.2.0: +randombytes@^2.0.0, randombytes@^2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec" + +range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" -raw-body@~2.1.7: +raw-body@~2.1.5, raw-body@~2.1.7: version "2.1.7" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" dependencies: @@ -4226,7 +5698,11 @@ rc@~1.1.6: minimist "^1.2.0" strip-json-comments "~1.0.4" -react-addons-perf@^15.2.1: +"react-addons-css-transition-group@^15.0.0 || ^16.0.0": + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-addons-css-transition-group/-/react-addons-css-transition-group-15.3.2.tgz#d8fa52bec9bb61bdfde8b9e4652b80297cbff667" + +react-addons-perf@^15.2.1, react-addons-perf@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.3.2.tgz#bbdbebe8649f936f9636a5750ac145bf5c620213" @@ -4287,10 +5763,6 @@ react-color@^2.3.4: reactcss "^1.0.6" tinycolor2 "^1.1.2" -react-deep-force-update@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/react-deep-force-update/-/react-deep-force-update-1.0.1.tgz#f911b5be1d2a6fe387507dd6e9a767aa2924b4c7" - react-dnd-html5-backend@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-2.1.2.tgz#bcff5866629c335b310b1062fe6537af35073c66" @@ -4306,11 +5778,7 @@ react-dnd@^2.1.4: invariant "^2.1.0" lodash "^4.2.0" -react-dom@^0.14.0: - version "0.14.8" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.14.8.tgz#0f1c547514263f771bd31814a739e5306575069e" - -"react-dom@^0.14.0 || ^15.0.0", react-dom@^15.2.1: +"react-dom@^0.14.0 || ^15.0.0", "react-dom@^15.0.0 || ^16.0.0", react-dom@^15.2.1, react-dom@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f" @@ -4320,12 +5788,6 @@ react-dropzone@^3.5.3: dependencies: attr-accept "^1.0.3" -react-fa@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/react-fa/-/react-fa-4.1.2.tgz#5d828916da05efc1ed58c97531f36120146f1f16" - dependencies: - font-awesome "^4.3.0" - react-helmet@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-3.1.0.tgz#63486194682f33004826f3687dc49a138b557050" @@ -4368,13 +5830,6 @@ react-prop-types@^0.4.0: dependencies: warning "^3.0.0" -react-proxy@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/react-proxy/-/react-proxy-1.1.8.tgz#9dbfd9d927528c3aa9f444e4558c37830ab8c26a" - dependencies: - lodash "^4.6.1" - react-deep-force-update "^1.0.0" - react-pure-render@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/react-pure-render/-/react-pure-render-1.0.2.tgz#9d8a928c7f2c37513c2d064e57b3e3c356e9fabb" @@ -4392,11 +5847,11 @@ react-router-bootstrap@^0.20.1: version "0.20.1" resolved "https://registry.yarnpkg.com/react-router-bootstrap/-/react-router-bootstrap-0.20.1.tgz#fc2059660e268b022b05abc69db5281ca32b63b3" -react-router-redux@^4.0.0: +react-router-redux@^4.0.0, react-router-redux@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.6.tgz#10cf98dce911d7dd912a05bdb07fee4d3c563dee" -react-router@^3.0.0-alpha.1: +react-router@^3.0.0, react-router@^3.0.0-alpha.1: version "3.0.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.0.0.tgz#3f313e4dbaf57048c48dd0a8c3cac24d93667dff" dependencies: @@ -4433,13 +5888,6 @@ react-toastr@^2.8.0: react-addons-update "^0.14.0 || ^15.0.0" react-dom "^0.14.0 || ^15.0.0" -react-transform-hmr@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz#e1a40bd0aaefc72e8dfd7a7cda09af85066397bb" - dependencies: - global "^4.3.0" - react-proxy "^1.1.7" - react-virtualized-select@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-1.4.0.tgz#6a7adededbcbf0421280f0a106a920dbff2d5f38" @@ -4456,14 +5904,7 @@ react-virtualized@^7.0.0, react-virtualized@^7.11.2: dom-helpers "^2.4.0" raf "^3.1.0" -react@^0.14.0: - version "0.14.8" - resolved "https://registry.yarnpkg.com/react/-/react-0.14.8.tgz#078dfa454d4745bcc54a9726311c2bf272c23684" - dependencies: - envify "^3.0.0" - fbjs "^0.6.1" - -react@^15.2.1: +"react@^15.0.0 || ^16.0.0", react@^15.2.1, react@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e" dependencies: @@ -4480,6 +5921,18 @@ reactcss@^1.0.4, reactcss@^1.0.6: merge "^1.2.0" object-assign "^4.1.0" +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + 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" + 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" @@ -4495,16 +5948,7 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@~2.1.4: +readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" dependencies: @@ -4516,7 +5960,25 @@ readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@~1.1.9: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~2.0.0, readable-stream@~2.0.6: +"readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +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" dependencies: @@ -4536,7 +5998,15 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" -recast@^0.10.0, recast@^0.10.10: +readline2@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + mute-stream "0.0.5" + +recast@^0.10.0: version "0.10.43" resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" dependencies: @@ -4545,16 +6015,26 @@ recast@^0.10.0, recast@^0.10.10: private "~0.1.5" source-map "~0.5.0" -recast@0.10.33: - version "0.10.33" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" +rechoir@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" dependencies: - ast-types "0.8.12" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" + resolve "^1.1.6" -reduce-css-calc@^1.2.6: +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +redeyed@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-1.0.1.tgz#e96c193b40c0816b00aec842698e61185e55498a" + dependencies: + esprima "~3.0.0" + +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" dependencies: @@ -4568,7 +6048,18 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "~0.1.0" -redux-logger@^2.6.1: +reduce-reducers@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-0.1.2.tgz#fa1b4718bc5292a71ddd1e5d839c9bea9770f14b" + +redux-actions@^0.13.0: + version "0.13.0" + resolved "https://registry.yarnpkg.com/redux-actions/-/redux-actions-0.13.0.tgz#34bf77c5698e86d86b0edb533ea23a1c0658f4fe" + dependencies: + lodash "^4.13.1" + reduce-reducers "^0.1.0" + +redux-logger@^2.6.1, redux-logger@^2.7.4: version "2.7.4" resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-2.7.4.tgz#891e5d29e7f111d08b5781a237b9965b5858c7f8" dependencies: @@ -4578,7 +6069,7 @@ redux-thunk@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.1.0.tgz#c724bfee75dbe352da2e3ba9bc14302badd89a98" -redux@^3.2.0, redux@^3.3.1: +redux@^3.2.0, redux@^3.3.1, redux@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/redux/-/redux-3.6.0.tgz#887c2b3d0b9bd86eca2be70571c27654c19e188d" dependencies: @@ -4595,17 +6086,6 @@ regenerator-runtime@^0.9.5: version "0.9.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz#403d6d40a4bdff9c330dd9392dcbb2d9a8bba1fc" -regenerator@0.8.40: - version "0.8.40" - resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.40.tgz#a0e457c58ebdbae575c9f8cd75127e93756435d8" - dependencies: - commoner "~0.10.3" - defs "~1.1.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - recast "0.10.33" - through "~2.3.8" - regex-cache@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" @@ -4613,14 +6093,6 @@ regex-cache@^0.4.2: is-equal-shallow "^0.1.3" is-primitive "^2.0.0" -regexpu-core@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" @@ -4629,16 +6101,6 @@ regexpu-core@^2.0.0: regjsgen "^0.2.0" regjsparser "^0.1.4" -regexpu@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexpu/-/regexpu-1.3.0.tgz#e534dc991a9e5846050c98de6d7dd4a55c9ea16d" - dependencies: - esprima "^2.6.0" - recast "^0.10.10" - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - regjsgen@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" @@ -4649,19 +6111,22 @@ regjsparser@^0.1.4: 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" +remarkable@^1.6.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.1.tgz#aaca4972100b66a642a63a1021ca4bac1be3bff6" + dependencies: + argparse "~0.1.15" + autolinker "~0.15.0" repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.2: +repeat-string@^1.5.2, repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^1.1.0, repeating@^1.1.2: +repeating@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" dependencies: @@ -4673,7 +6138,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.55.0, request@^2.67.0, request@^2.75.0: +request@^2.55.0, request@^2.58.0, request@^2.67.0, request@^2.74.0, request@^2.75.0: version "2.76.0" resolved "https://registry.yarnpkg.com/request/-/request-2.76.0.tgz#be44505afef70360a0436955106be3945d95560e" dependencies: @@ -4698,33 +6163,134 @@ request@^2.55.0, request@^2.67.0, request@^2.75.0: tough-cookie "~2.3.0" tunnel-agent "~0.4.1" +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + 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" -requires-port@1.0.x, requires-port@1.x.x: +require-uncached@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.2.tgz#67dad3b733089e77030124678a459589faf6a7ec" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" -resolve@^1.1.6: +reqwest@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-2.0.5.tgz#00fb15ac4918c419ca82b43f24c78882e66039a1" + +resolve-dir@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" + dependencies: + expand-tilde "^1.2.2" + global-modules "^0.2.3" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +resp-modifier@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f" + dependencies: + debug "^2.2.0" + minimatch "^3.0.2" + +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + +retry@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.0.tgz#649e15ca408422d98318161935e7f7d652d435dd" + +rgb-hex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-1.0.0.tgz#bfaf8cd9cd9164b5a26d71eb4f15a0965324b3c1" + +rgb@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" + right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" dependencies: align-text "^0.1.1" -rimraf@^2.4.3, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +right-now@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/right-now/-/right-now-1.0.0.tgz#6e89609deebd7dcdaf8daecc9aea39cf585a0918" + +right-pad@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" + +rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: glob "^7.0.5" -ripemd160@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-0.2.0.tgz#2bf198bde167cacfa51c0a928e84b68bbe171fce" +ripemd160@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" + +rmdir@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rmdir/-/rmdir-1.2.0.tgz#4fe0357cb06168c258e73e968093dc4e8a0f3253" + dependencies: + node.flow "1.2.3" + +rollup@^0.36.3: + version "0.36.3" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.36.3.tgz#c89ac479828924ff8f69c1d44541cb4ea2fc11fc" + dependencies: + source-map-support "^0.4.0" + +rsvp@^3.0.13, rsvp@^3.0.18: + version "3.3.3" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.3.3.tgz#34633caaf8bc66ceff4be3c2e1dffd032538a813" + +run-async@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" + dependencies: + once "^1.3.0" + +run-async@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.2.0.tgz#8783abd83c7bb86f41ee0602fc82404b3bd6e8b9" + dependencies: + is-promise "^2.1.0" + pinkie-promise "^2.0.0" + +run-parallel@^1.1.2: + version "1.1.6" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.6.tgz#29003c9a2163e01e2d2dfc90575f2c6c1d61a039" + +rx-lite@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" + +rx@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" samsam@~1.1: version "1.1.3" @@ -4734,10 +6300,25 @@ samsam@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" -sax@~1.2.1: +sane@^1.3.3, sane@~1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sane/-/sane-1.4.1.tgz#88f763d74040f5f0c256b6163db399bf110ac715" + dependencies: + exec-sh "^0.2.0" + fb-watchman "^1.8.0" + minimatch "^3.0.2" + minimist "^1.1.1" + walker "~1.0.5" + watch "~0.10.0" + +sax@^1.1.4, sax@>=0.6.0: version "1.2.1" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" +sax@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.5.tgz#1da50a8d00cdecd59405659f5ff85349fe773743" + selectn@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/selectn/-/selectn-0.10.0.tgz#8ad4fabdd6338677f519a28711792c45a3a14c39" @@ -4751,7 +6332,11 @@ selectn@^1.0.20, selectn@^1.0.5: debug "^2.2.0" dotsplit.js "^1.0.3" -semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": +semver@^4.3.3: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +semver@^5.0.1, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -4773,12 +6358,6 @@ send@0.14.1: range-parser "~1.2.0" statuses "~1.3.0" -sentence-case@^1.1.1, sentence-case@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-1.1.3.tgz#8034aafc2145772d3abe1509aa42c9e1042dc139" - dependencies: - lower-case "^1.1.1" - sentence-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.0.tgz#d592fbed457fd1a59e3af0ee17e99f6fd70d7efd" @@ -4786,19 +6365,7 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -serve-index@^1.7.2: - version "1.8.0" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.8.0.tgz#7c5d96c13fb131101f93c1c5774f8516a1e78d3b" - dependencies: - accepts "~1.3.3" - batch "0.5.3" - debug "~2.2.0" - escape-html "~1.0.3" - http-errors "~1.5.0" - mime-types "~2.1.11" - parseurl "~1.3.1" - -serve-static@~1.11.1: +serve-static@^1.10.0, serve-static@~1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" dependencies: @@ -4807,7 +6374,7 @@ serve-static@~1.11.1: parseurl "~1.3.1" send "0.14.1" -set-blocking@~2.0.0: +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" @@ -4819,9 +6386,11 @@ setprototypeof@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" -sha.js@2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.2.6.tgz#17ddeddc5f722fb66501658895461977867315ba" +sha.js@^2.3.6, sha.js@~2.4.4: + version "2.4.5" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.5.tgz#27d171efcc82a118b99639ff581660242b506e7c" + dependencies: + inherits "^2.0.1" shallowequal@0.2.2, shallowequal@0.2.x: version "0.2.2" @@ -4829,9 +6398,41 @@ shallowequal@0.2.2, shallowequal@0.2.x: dependencies: lodash.keys "^3.1.2" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +shasum@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/shasum/-/shasum-1.0.2.tgz#e7012310d8f417f4deb5712150e5678b87ae565f" + dependencies: + json-stable-stringify "~0.0.0" + sha.js "~2.4.4" + +shell-quote@^1.4.2, shell-quote@^1.4.3: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +shelljs@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" + +shelljs@^0.7.0: + version "0.7.5" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675" + dependencies: + glob "^7.0.0" + interpret "^1.0.0" + rechoir "^0.6.2" + +shelljs@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.5.3.tgz#c54982b996c76ef0c1e6b59fbdc5825f5b713113" + +shellwords@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" shpjs@^3.3.2: version "3.3.2" @@ -4851,13 +6452,11 @@ signal-exit@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" -simple-fmt@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" - -simple-is@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" +simple-html-index@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/simple-html-index/-/simple-html-index-1.5.0.tgz#2c93eeaebac001d8a135fc0022bd4ade8f58996f" + dependencies: + from2-string "^1.1.0" sinon-chai@^2.8.0: version "2.8.0" @@ -4876,11 +6475,9 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" -snake-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-1.1.2.tgz#0c2f25e305158d9a18d3d977066187fef8a5a66a" - dependencies: - sentence-case "^1.1.2" +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" snake-case@^2.1.0: version "2.1.0" @@ -4889,58 +6486,30 @@ snake-case@^2.1.0: no-case "^2.2.0" sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -sockjs-client@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.1.tgz#284843e9a9784d7c474b1571b3240fca9dda4bb0" - dependencies: - debug "^2.2.0" - eventsource "~0.1.6" - faye-websocket "~0.11.0" - inherits "^2.0.1" - json3 "^3.3.2" - url-parse "^1.1.1" - -sockjs@^0.3.15: - version "0.3.18" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207" - dependencies: - faye-websocket "^0.10.0" - uuid "^2.0.2" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^0.1.4, source-list-map@~0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.6.tgz#e1e6f94f0b40c4d28dcf8f5b8766e0e45636877f" - -source-map-support@^0.2.10: - version "0.2.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" dependencies: - source-map "0.1.32" + hoek "2.x.x" -source-map-support@^0.4.2: +source-map-support@^0.4.0, source-map-support@^0.4.2: version "0.4.6" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.6.tgz#32552aa64b458392a85eab3b0b5ee61527167aeb" dependencies: source-map "^0.5.3" -source-map@^0.4.2, source-map@~0.4.1, source-map@0.4.x: +source-map-support@~0.2.8: + version "0.2.10" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" + dependencies: + source-map "0.1.32" + +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" dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" @@ -4956,6 +6525,13 @@ source-map@0.1.32: dependencies: amdefine ">=0.0.4" +spawn-sync@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" + dependencies: + concat-stream "^1.4.7" + os-shim "^0.1.2" + spdx-correct@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" @@ -4970,6 +6546,12 @@ 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" +split2@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/split2/-/split2-0.2.1.tgz#02ddac9adc03ec0bb78c1282ec079ca6e85ae900" + dependencies: + through2 "~0.6.1" + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4989,38 +6571,87 @@ sshpk@^1.7.0: jsbn "~0.1.0" tweetnacl "~0.14.0" -stable@~0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.5.tgz#08232f60c732e9890784b5bed0734f8b32a887b9" +stacked@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stacked/-/stacked-1.1.1.tgz#2c7fa38cc7e37a3411a77cd8e792de448f9f6975" -stats-webpack-plugin@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stats-webpack-plugin/-/stats-webpack-plugin-0.2.2.tgz#7d6a839669ca67909a9db801d502bc395cc66695" +standard-engine@^5.0.0, standard-engine@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-5.1.1.tgz#cb775eae1c50cfa8e76ab25456dd122af7f34788" + dependencies: + deglob "^2.0.0" + find-root "^1.0.0" + get-stdin "^5.0.1" + home-or-tmp "^2.0.0" + minimist "^1.1.0" + pkg-config "^1.0.1" -"statuses@>= 1.3.0 < 2", statuses@~1.3.0: +standard@^8.3.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/standard/-/standard-8.5.0.tgz#df78a505da59382287b92a86b55ae02df3b54a31" + dependencies: + eslint "~3.8.1" + eslint-config-standard "6.2.1" + eslint-config-standard-jsx "3.2.0" + eslint-plugin-promise "~3.3.0" + eslint-plugin-react "~6.4.1" + eslint-plugin-standard "~2.0.1" + standard-engine "~5.1.0" + +"statuses@>= 1.3.0 < 2", statuses@~1.3.0, statuses@1: version "1.3.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" -stream-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-1.0.0.tgz#bf9b4abfb42b274d751479e44e0ff2656b6f1193" +stdout-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + dependencies: + readable-stream "^2.0.1" + +stream-browserify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" dependencies: inherits "~2.0.1" - readable-stream "^1.0.27-1" + readable-stream "^2.0.2" -stream-cache@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/stream-cache/-/stream-cache-0.0.2.tgz#1ac5ad6832428ca55667dbdee395dad4e6db118f" +stream-combiner2@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stream-combiner2/-/stream-combiner2-1.1.1.tgz#fb4d8a1420ea362764e21ad4780397bebcb41cbe" + dependencies: + duplexer2 "~0.1.0" + readable-stream "^2.0.2" + +stream-http@^2.0.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.4.1.tgz#8ee5689ae69169e8eb8edd6aeb2ca08ab47e8f59" + dependencies: + builtin-status-codes "^2.0.0" + inherits "^2.0.1" + readable-stream "^2.1.0" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-splicer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/stream-splicer/-/stream-splicer-2.0.0.tgz#1b63be438a133e4b671cc1935197600175910d83" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.2" 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" -string_decoder@~0.10.25, string_decoder@~0.10.x: +string_decoder@~0.10.0, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string-width@^1.0.1: +string-to-js@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/string-to-js/-/string-to-js-0.0.1.tgz#bf153c760636faa30769b804a0195552ba7ad80f" + +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" dependencies: @@ -5028,18 +6659,27 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -stringmap@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" +string-width@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^3.0.0" -stringset@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" +string.prototype.codepointat@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz#6b26e9bd3afcaa7be3b4269b526de1b82000ac78" stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" +strip-ansi@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" + dependencies: + ansi-regex "^0.2.1" + 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" @@ -5052,21 +6692,39 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" -strip-json-comments@~1.0.4: +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +strip-json-comments@~1.0.1, strip-json-comments@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" -style-loader@^0.13.0: - version "0.13.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.13.1.tgz#468280efbc0473023cd3a6cd56e33b5a1d7fc3a9" +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" + +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" dependencies: - loader-utils "^0.2.7" + minimist "^1.1.0" + +supports-color@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2: +supports-color@^3.1.0, supports-color@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" dependencies: @@ -5076,17 +6734,9 @@ supports-color@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" -svgo@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.1.tgz#287320fed972cb097e72c2bb1685f96fe08f8034" - dependencies: - coa "~1.0.1" - colors "~1.1.2" - csso "~2.2.1" - js-yaml "~3.6.1" - mkdirp "~0.5.1" - sax "~1.2.1" - whet.extend "~0.9.9" +supports-color@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" swap-case@^1.1.0: version "1.1.2" @@ -5103,9 +6753,56 @@ symbol-observable@^1.0.2: version "3.1.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.1.4.tgz#02b279348d337debc39694c5c95f882d448a312a" -tapable@^0.1.8, tapable@~0.1.8: - version "0.1.10" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" +syntax-error@^1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.1.6.tgz#b4549706d386cc1c1dc7c2423f18579b6cade710" + dependencies: + acorn "^2.7.0" + +systemjs-builder@^0.15.33, systemjs-builder@0.15.33: + version "0.15.33" + resolved "https://registry.yarnpkg.com/systemjs-builder/-/systemjs-builder-0.15.33.tgz#7bd4d045769a67b52f9596141ba21cd94b49910c" + dependencies: + babel-core "^6.9.0" + babel-plugin-transform-cjs-system-wrapper "^0.2.1" + babel-plugin-transform-es2015-modules-systemjs "^6.6.5" + babel-plugin-transform-global-system-wrapper "0.0.1" + babel-plugin-transform-system-register "0.0.1" + bluebird "^3.3.4" + data-uri-to-buffer "0.0.4" + es6-template-strings "^2.0.0" + glob "^7.0.3" + mkdirp "^0.5.1" + rollup "^0.36.3" + source-map "^0.5.3" + systemjs "^0.19.39" + traceur "0.0.105" + uglify-js "^2.6.1" + +systemjs@^0.19.39, systemjs@0.19.40: + version "0.19.40" + resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-0.19.40.tgz#158f64a9f4ef541a7fda6b40e527ee46b6c54cd0" + dependencies: + when "^3.7.5" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +tar-fs@^1.13.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.14.0.tgz#f99cc074bf33bed21cd921a21720797bb18e6c96" + dependencies: + mkdirp "^0.5.0" + pump "^1.0.0" + tar-stream "^1.1.2" tar-pack@~3.3.0: version "3.3.0" @@ -5120,6 +6817,15 @@ tar-pack@~3.3.0: tar "~2.2.1" uid-number "~0.0.6" +tar-stream@^1.1.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" + dependencies: + bl "^1.0.0" + end-of-stream "^1.0.0" + readable-stream "^2.0.0" + xtend "^4.0.0" + tar@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" @@ -5128,6 +6834,13 @@ tar@~2.2.1: fstream "^1.0.2" inherits "2" +term-color@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/term-color/-/term-color-1.0.1.tgz#38e192553a473e35e41604ff5199846bf8117a3a" + dependencies: + ansi-styles "2.0.1" + supports-color "1.3.1" + test-exclude@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-2.1.3.tgz#a8d8968e1da83266f9864f2852c55e220f06434a" @@ -5138,27 +6851,61 @@ test-exclude@^2.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -through@~2.3.4, through@~2.3.8: +testcheck@^0.1.0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/testcheck/-/testcheck-0.1.4.tgz#90056edd48d11997702616ce6716f197d8190164" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +throat@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-3.0.0.tgz#e7c64c867cbb3845f10877642f7b60055b8ec0d6" + +through@^2.3.6, through@^2.3.7, "through@>=2.2.7 <3", through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" +through@~2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" + +through2@^2.0.0, through2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" + dependencies: + readable-stream "~2.0.0" + xtend "~4.0.0" + +through2@~0.6.1: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + timers-browserify@^1.0.1: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" dependencies: process "~0.11.0" +tiny-lr@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-0.2.1.tgz#b3fdba802e5d56a33c2f6f10794b32e477ac729d" + dependencies: + body-parser "~1.14.0" + debug "~2.2.0" + faye-websocket "~0.10.0" + livereload-js "^2.2.0" + parseurl "~1.3.0" + qs "~5.1.0" + tinycolor2@^1.1.2: version "1.4.1" resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8" -title-case@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/title-case/-/title-case-1.1.2.tgz#fae4a6ae546bfa22d083a0eea910a40d12ed4f5a" - dependencies: - sentence-case "^1.1.1" - upper-case "^1.0.3" - title-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/title-case/-/title-case-2.1.0.tgz#c68ccb4232079ded64f94b91b4941ade91391979" @@ -5166,7 +6913,21 @@ title-case@^2.1.0: no-case "^2.2.0" upper-case "^1.0.3" -to-fast-properties@^1.0.0, to-fast-properties@^1.0.1: +tmp@^0.0.29: + version "0.0.29" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.29.tgz#f25125ff0dd9da3ccb0c2dd371ee1288bb9128c0" + dependencies: + os-tmpdir "~1.0.1" + +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" @@ -5174,33 +6935,43 @@ to-iso-string@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" -tough-cookie@^2.0.0, tough-cookie@~2.3.0: +tough-cookie@^2.0.0, tough-cookie@^2.3.1, tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" dependencies: punycode "^1.4.1" -tr46@~0.0.1: +tr46@~0.0.1, tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" -trim-right@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" +traceur@0.0.105: + version "0.0.105" + resolved "https://registry.yarnpkg.com/traceur/-/traceur-0.0.105.tgz#5cf9dee83d6b77861c3d6c44d53859aed7ab0479" + dependencies: + commander "2.9.x" + glob "5.0.x" + rsvp "^3.0.13" + semver "^4.3.3" + source-map-support "~0.2.8" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +trim@~0.0.1, trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" truncate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/truncate/-/truncate-2.0.0.tgz#09d5bc4163f3e257cd687351241071c24f14112f" -try-resolve@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" - -tryor@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" +tryit@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb" -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" @@ -5309,7 +7080,7 @@ type-detect@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" -type-is@~1.6.13: +type-is@~1.6.10, type-is@~1.6.13: version "1.6.13" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" dependencies: @@ -5324,7 +7095,7 @@ ua-parser-js@^0.7.9: version "0.7.10" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f" -uglify-js@~2.7.3: +uglify-js@^2.6, uglify-js@^2.6.1, uglify-js@2.x.x: version "2.7.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.4.tgz#a295a0de12b6a650c031c40deb0dc40b14568bd2" dependencies: @@ -5333,43 +7104,54 @@ uglify-js@~2.7.3: uglify-to-browserify "~1.0.0" yargs "~3.10.0" -uglify-js@2.6.x: - version "2.6.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.6.4.tgz#65ea2fb3059c9394692f15fed87c2b36c16b9adf" - dependencies: - async "~0.2.6" - source-map "~0.5.1" - uglify-to-browserify "~1.0.0" - yargs "~3.10.0" - 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" +uglifyify@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/uglifyify/-/uglifyify-3.0.4.tgz#487e080a5a7798880e68e90def9b06681fb13bd2" + dependencies: + convert-source-map "~1.1.0" + extend "^1.2.1" + minimatch "^3.0.2" + through "~2.3.4" + uglify-js "2.x.x" + uid-number@~0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" +umd@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" + +unc-path-regex@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + uncontrollable@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.0.3.tgz#06ec76cb9e02914756085d9cea0354fc746b09b4" dependencies: invariant "^2.1.0" +underscore.string@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b" + +underscore@^1.6.0: + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -uniqid@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.0.tgz#33d9679f65022f48988a03fd24e7dcaf8f109eca" - 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" - unpipe@~1.0.0, unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -5384,43 +7166,35 @@ upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1, upper-case@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" -url-loader@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.7.tgz#67e8779759f8000da74994906680c943a9b0925d" - dependencies: - loader-utils "0.2.x" - mime "1.2.x" - -url-parse@^1.1.1: - version "1.1.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a" - dependencies: - querystringify "0.0.x" - requires-port "1.0.x" +url-trim@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-trim/-/url-trim-1.0.0.tgz#40057e2f164b88e5daca7269da47e6d1dd837adc" -url-parse@1.0.x: - version "1.0.5" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" +url@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: - querystringify "0.0.x" - requires-port "1.0.x" + punycode "1.3.2" + querystring "0.2.0" -url@~0.10.1: +url@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" dependencies: punycode "1.3.2" querystring "0.2.0" -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" +user-home@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + dependencies: + os-homedir "^1.0.0" util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -"util@>=0.10.3 <1", util@~0.10.3, util@0.10.3: +"util@>=0.10.3 <1", util@~0.10.1, util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -5434,12 +7208,6 @@ uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" -v8flags@^2.0.10: - version "2.0.11" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" - dependencies: - user-home "^1.1.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" @@ -5455,22 +7223,24 @@ vary@^1, vary@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" -vendors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" - verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" dependencies: extsprintf "1.0.2" -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" dependencies: indexof "0.0.1" +walker@~1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + warning@^2.0.0, warning@2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" @@ -5483,85 +7253,36 @@ warning@^3.0.0: dependencies: loose-envify "^1.0.0" -watchpack@^0.2.1: - version "0.2.9" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-0.2.9.tgz#62eaa4ab5e5ba35fdfc018275626e3c0f5e3fb0b" - dependencies: - async "^0.9.0" - chokidar "^1.0.0" - graceful-fs "^4.1.2" - -webpack-core@~0.6.0: - version "0.6.8" - resolved "https://registry.yarnpkg.com/webpack-core/-/webpack-core-0.6.8.tgz#edf9135de00a6a3c26dd0f14b208af0aa4af8d0a" - dependencies: - source-list-map "~0.1.0" - source-map "~0.4.1" - -webpack-dev-middleware@^1.2.0, webpack-dev-middleware@^1.4.0: - version "1.8.4" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.8.4.tgz#e8765c9122887ce9e3abd4cc9c3eb31b61e0948d" - dependencies: - memory-fs "~0.3.0" - mime "^1.3.4" - path-is-absolute "^1.0.0" - range-parser "^1.0.3" - -webpack-dev-server@^1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-1.16.2.tgz#8bebc2c4ce1c45a15c72dd769d9ba08db306a793" - dependencies: - compression "^1.5.2" - connect-history-api-fallback "^1.3.0" - express "^4.13.3" - http-proxy-middleware "~0.17.1" - open "0.0.5" - optimist "~0.6.1" - serve-index "^1.7.2" - sockjs "^0.3.15" - sockjs-client "^1.0.3" - stream-cache "~0.0.1" - strip-ansi "^3.0.0" - supports-color "^3.1.1" - webpack-dev-middleware "^1.4.0" +watch@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" -webpack-hot-middleware@^2.2.0: - version "2.13.1" - resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.13.1.tgz#104350e044be58ba3b7ef1c39513d69562841975" +watchify-middleware@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchify-middleware/-/watchify-middleware-1.6.0.tgz#6db6e28f0279de1ca1209ae4f1a7f063745877c4" dependencies: - ansi-html "0.0.6" - html-entities "^1.2.0" - querystring "^0.2.0" + concat-stream "^1.5.0" + debounce "^1.0.0" + events "^1.0.2" + object-assign "^4.0.1" strip-ansi "^3.0.0" + watchify "^3.3.1" -webpack-visualizer-plugin@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/webpack-visualizer-plugin/-/webpack-visualizer-plugin-0.1.5.tgz#88acdba3a212f7c845f37a086ed23188ab476ea6" +watchify@^3.3.1, watchify@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.7.0.tgz#ee2f2c5c8c37312303f998b818b2b3450eefe648" dependencies: - d3 "^3.5.6" - prettysize "0.0.3" - react "^0.14.0" - react-dom "^0.14.0" + anymatch "^1.3.0" + browserify "^13.0.0" + chokidar "^1.0.0" + defined "^1.0.0" + outpipe "^1.1.0" + through2 "^2.0.0" + xtend "^4.0.0" -webpack@^1.13.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-1.13.3.tgz#e79c46fe5a37c5ca70084ba0894c595cdcb42815" - dependencies: - acorn "^3.0.0" - async "^1.3.0" - clone "^1.0.2" - enhanced-resolve "~0.9.0" - interpret "^0.6.4" - loader-utils "^0.2.11" - memory-fs "~0.3.0" - mkdirp "~0.5.0" - node-libs-browser "^0.6.0" - optimist "~0.6.0" - supports-color "^3.1.0" - tapable "~0.1.8" - uglify-js "~2.7.3" - watchpack "^0.2.1" - webpack-core "~0.6.0" +webidl-conversions@^3.0.0, webidl-conversions@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" websocket-driver@>=0.5.1: version "0.6.5" @@ -5573,6 +7294,12 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" +whatwg-encoding@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4" + dependencies: + iconv-lite "0.4.13" + whatwg-fetch@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" @@ -5587,9 +7314,26 @@ whatwg-url-compat@~0.6.5: dependencies: tr46 "~0.0.1" -whet.extend@~0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" +whatwg-url@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-3.0.0.tgz#b9033c50c7ce763e91d78777ce825a6d7f56dac5" + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +when@^3.7.5: + version "3.7.7" + resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which@^1.0.5, which@^1.0.9, which@^1.1.1, which@^1.2.10, which@^1.2.4: + version "1.2.11" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.11.tgz#c8b2eeea6b8c1659fa7c1dd4fdaabe9533dc5e8b" + dependencies: + isexe "^1.1.1" wide-align@^1.1.0: version "1.1.0" @@ -5597,55 +7341,128 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.1" -window-size@^0.1.2: +winchan@0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.1.4.tgz#88fa12411cd542eb626018c38a196bcbb17993bb" + +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" +word-wrap@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.1.0.tgz#356153d61d10610d600785c5d701288e0ae764a6" -wordwrap@~1.0.0: +wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + wordwrap@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" +worker-farm@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.3.1.tgz#4333112bb49b17aa050b87895ca6b2cacf40e5ff" + dependencies: + errno ">=0.1.1 <0.2.0-0" + xtend ">=4.0.0 <4.1.0-0" + +wrap-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.0.0.tgz#7d30f8f873f9a5bbc3a64dabc8d177e071ae426f" + dependencies: + string-width "^1.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" -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" +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" "xml-name-validator@>= 2.0.1 < 3.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" +xml2js@0.4.15: + version "0.4.15" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.15.tgz#95cd03ff2dd144ec28bc6273bf2b2890c581ad0c" + dependencies: + sax ">=0.6.0" + xmlbuilder ">=2.4.6" + +xmlbuilder@>=2.4.6: + version "8.2.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773" + +xmlbuilder@2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-2.6.2.tgz#f916f6d10d45dc171b1be2e6e673fb6e0cc35d0a" + dependencies: + lodash "~3.5.0" + "xmlhttprequest@>= 1.6.0 < 2.0.0": version "1.8.0" resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" -xtend@^4.0.0, xtend@^4.0.1: +xtend@^4.0.0, xtend@^4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" -y18n@^3.2.0: +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + dependencies: + object-keys "~0.4.0" + +y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" -yaml-loader@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.1.0.tgz#33cd4e6404c5b441005810f1537775d73cb28a66" +yamljs@^0.2.8: + version "0.2.8" + resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.2.8.tgz#ef23fb006e62f6ae07b406aa2a949561f336ea5c" + dependencies: + argparse "^1.0.7" + glob "^7.0.5" + +yargs-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-3.2.0.tgz#5081355d19d9d0c8c5d81ada908cb4e6d186664f" + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.1.0" + +yargs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e" dependencies: - js-yaml "^3.0.2" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.2.0" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^3.2.0" yargs@~3.10.0: version "3.10.0" @@ -5656,14 +7473,3 @@ yargs@~3.10.0: decamelize "^1.0.0" window-size "0.1.0" -yargs@~3.27.0: - version "3.27.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" - dependencies: - camelcase "^1.2.1" - cliui "^2.1.0" - decamelize "^1.0.0" - os-locale "^1.4.0" - window-size "^0.1.2" - y18n "^3.2.0" - From 73e2926d91d2dc8f2e318f92362417b433e518a9 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 2 Nov 2016 21:08:20 +0700 Subject: [PATCH 110/323] refactor(build): Put built files in dist folder --- .gitignore | 1 - configurations/default/settings.yml | 4 +- index.html | 4 +- package.json | 12 +- yarn.lock | 237 ++-------------------------- 5 files changed, 16 insertions(+), 242 deletions(-) diff --git a/.gitignore b/.gitignore index f77e66980..e776b729a 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,3 @@ src/main/client/config.js datatools-manager.iml config.yml config_server.yml -assets diff --git a/configurations/default/settings.yml b/configurations/default/settings.yml index 4537638a0..3c12dc39b 100644 --- a/configurations/default/settings.yml +++ b/configurations/default/settings.yml @@ -1,3 +1,3 @@ entries: - - src/main/client/main.js:assets/index.js - - src/main/client/index.css:assets/index.css + - src/main/client/main.js:dist/index.js + - src/main/client/index.css:dist/index.css diff --git a/index.html b/index.html index 7398f9bdf..fe8bbce99 100644 --- a/index.html +++ b/index.html @@ -6,10 +6,10 @@ Catalogue - +
- + diff --git a/package.json b/package.json index 80428a97d..e90831153 100644 --- a/package.json +++ b/package.json @@ -84,17 +84,7 @@ "validator": "^5.5.0" }, "devDependencies": { - "autoprefixer": "^6.0.3", - "chai": "^3.2.0", - "jsdom": "^6.5.1", - "mastarm": "^2.0.0", - "mocha": "^2.3.3", - "mocha-jsdom": "^1.0.0", - "react-addons-perf": "^15.2.1", - "react-addons-test-utils": "^0.14.3", - "rimraf": "^2.4.3", - "sinon": "^1.16.1", - "sinon-chai": "^2.8.0" + "mastarm": "^2.0.0" }, "standard": { "parser": "babel-eslint" diff --git a/yarn.lock b/yarn.lock index 912fc4461..63dbfe843 100644 --- a/yarn.lock +++ b/yarn.lock @@ -254,10 +254,6 @@ assert@~1.3.0: dependencies: util "0.10.3" -assertion-error@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.0.2.tgz#13ca515d86206da0bac66e834dd397d87581094c" - ast-types@0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" @@ -333,7 +329,7 @@ autolinker@~0.15.0: version "0.15.3" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" -autoprefixer@^6.0.2, autoprefixer@^6.0.3: +autoprefixer@^6.0.2: version "6.5.1" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.5.1.tgz#ae759a5221e709f3da17c2d656230e67c43cbb75" dependencies: @@ -1207,10 +1203,6 @@ browser-pack@^6.0.1: through2 "^2.0.0" umd "^3.0.0" -"browser-request@>= 0.3.1 < 0.4.0": - version "0.3.3" - resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" - browser-resolve@^1.11.0, browser-resolve@^1.11.2, browser-resolve@^1.7.0: version "1.11.2" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.2.tgz#8ff09b0a2c421718a1051c260b32e48f442938ce" @@ -1496,14 +1488,6 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" -chai@^3.2.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" - dependencies: - assertion-error "^1.0.1" - deep-eql "^0.1.3" - type-detect "^1.0.0" - chalk@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -1717,14 +1701,6 @@ commander@^2.5.0, commander@^2.9.0, commander@2.9.x: dependencies: graceful-readlink ">= 1.0.0" -commander@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-0.6.1.tgz#fa68a14f6a945d54dbbe50d8cdb3320e9e3b1a06" - -commander@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" - commitizen@^2.8.2: version "2.8.6" resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-2.8.6.tgz#578483abbf5b67368d1ccdb9d9d978c74972011b" @@ -1934,7 +1910,7 @@ css-color-function@^1.2.0: version "0.3.1" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" -"cssstyle@>= 0.2.29 < 0.3.0", "cssstyle@>= 0.2.36 < 0.3.0": +"cssstyle@>= 0.2.36 < 0.3.0": version "0.2.37" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" dependencies: @@ -2007,7 +1983,7 @@ debug-log@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" -debug@*, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0, debug@2.2.0: +debug@*, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" dependencies: @@ -2035,12 +2011,6 @@ deep-diff@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.4.tgz#aac5c39952236abe5f037a2349060ba01b00ae48" -deep-eql@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" - dependencies: - type-detect "0.1.1" - deep-equal@^1.0.0, deep-equal@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2135,10 +2105,6 @@ diff@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/diff/-/diff-3.0.1.tgz#a52d90cc08956994be00877bff97110062582c35" -diff@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" - diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" @@ -2175,38 +2141,10 @@ dom-helpers@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-2.4.0.tgz#9bb4b245f637367b1fa670274272aa28fe06c367" -dom-serializer@0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~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" -domelementtype@^1.3.0, domelementtype@1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - -domutils@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - dependencies: - dom-serializer "0" - domelementtype "1" - dot-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.0.tgz#4b43dd0d7403c34cb645424add397e80bfe85ca6" @@ -2271,10 +2209,6 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "~1.3.0" -entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - envify@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" @@ -2373,10 +2307,6 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^ version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escape-string-regexp@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1" - escodegen@^1.6.1, escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -2821,12 +2751,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -formatio@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9" - dependencies: - samsam "~1.1" - forwarded@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" @@ -3000,13 +2924,6 @@ glob@~4.3.0: minimatch "^2.0.1" once "^1.3.0" -glob@3.2.11: - version "3.2.11" - resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" - dependencies: - inherits "2" - minimatch "0.3" - glob@7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" @@ -3061,10 +2978,6 @@ gravatar@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/gravatar/-/gravatar-1.5.2.tgz#25b4c96790b82d1b3c714e3963999253bd2ba1cd" -growl@1.9.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f" - growly@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -3191,17 +3104,6 @@ htmlescape@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" -"htmlparser2@>= 3.7.3 < 4.0.0": - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" - dependencies: - domelementtype "^1.3.0" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^2.0.2" - http-errors@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" @@ -3674,13 +3576,6 @@ istanbul@^0.4.5: which "^1.1.1" wordwrap "^1.0.0" -jade@0.26.3: - version "0.26.3" - resolved "https://registry.yarnpkg.com/jade/-/jade-0.26.3.tgz#8f10d7977d8d79f2f6ff862a81b0513ccb25686c" - dependencies: - commander "0.6.1" - mkdirp "0.3.0" - jasmine-check@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/jasmine-check/-/jasmine-check-0.1.5.tgz#dbad7eec56261c4b3d175ada55fe59b09ac9e415" @@ -3897,27 +3792,6 @@ jsbn@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" -jsdom@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-6.5.1.tgz#b6064d6a7651081af41d576edc56bc51e00122c0" - dependencies: - acorn "^2.4.0" - acorn-globals "^1.0.4" - browser-request ">= 0.3.1 < 0.4.0" - cssom ">= 0.3.0 < 0.4.0" - cssstyle ">= 0.2.29 < 0.3.0" - escodegen "^1.6.1" - htmlparser2 ">= 3.7.3 < 4.0.0" - nwmatcher ">= 1.3.6 < 2.0.0" - parse5 "^1.4.2" - request "^2.55.0" - symbol-tree ">= 3.1.0 < 4.0.0" - tough-cookie "^2.0.0" - whatwg-url-compat "~0.6.5" - xml-name-validator ">= 2.0.1 < 3.0.0" - xmlhttprequest ">= 1.6.0 < 2.0.0" - xtend "^4.0.0" - jsdom@^9.8.0: version "9.8.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.8.3.tgz#fde29c109c32a1131e0b6c65914e64198f97c370" @@ -4404,10 +4278,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -lolex@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31" - longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" @@ -4439,7 +4309,7 @@ lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.3.tgz#c92393d976793eee5ba4edb583cf8eae35bd9bfb" -lru-cache@^2.7.0, lru-cache@2: +lru-cache@^2.7.0: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -4631,13 +4501,6 @@ minimatch@^3.0.0, minimatch@^3.0.2, "minimatch@2 || 3": dependencies: brace-expansion "^1.0.0" -minimatch@0.3: - version "0.3.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" - dependencies: - lru-cache "2" - sigmund "~1.0.0" - minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -4654,35 +4517,12 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.1, mkdirp@0.5.1, mkdirp@0.5.x: +mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.1, mkdirp@0.5.x: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - -mocha-jsdom@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mocha-jsdom/-/mocha-jsdom-1.1.0.tgz#e1576fbd0601cc89d358a213a0e5585d1b7c7a01" - -mocha@^2.3.3: - version "2.5.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-2.5.3.tgz#161be5bdeb496771eb9b35745050b622b5aefc58" - dependencies: - commander "2.3.0" - debug "2.2.0" - diff "1.4.0" - escape-string-regexp "1.0.2" - glob "3.2.11" - growl "1.9.2" - jade "0.26.3" - mkdirp "0.5.1" - supports-color "1.2.0" - to-iso-string "0.0.2" - module-deps@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.0.8.tgz#55fd70623399706c3288bef7a609ff1e8c0ed2bb" @@ -4883,7 +4723,7 @@ numeral@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62" -"nwmatcher@>= 1.3.6 < 2.0.0", "nwmatcher@>= 1.3.7 < 2.0.0": +"nwmatcher@>= 1.3.7 < 2.0.0": version "1.3.9" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.3.9.tgz#8bab486ff7fa3dfd086656bbe8b17116d3692d2a" @@ -5072,7 +4912,7 @@ parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" -parse5@^1.4.2, parse5@^1.5.1: +parse5@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" @@ -5702,7 +5542,7 @@ rc@~1.1.6: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-css-transition-group/-/react-addons-css-transition-group-15.3.2.tgz#d8fa52bec9bb61bdfde8b9e4652b80297cbff667" -react-addons-perf@^15.2.1, react-addons-perf@^15.3.2: +react-addons-perf@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.3.2.tgz#bbdbebe8649f936f9636a5750ac145bf5c620213" @@ -5710,10 +5550,6 @@ react-addons-perf@^15.2.1, react-addons-perf@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.3.2.tgz#c9edba49b9eab44d0c59024d289beb1ab97318b5" -react-addons-test-utils@^0.14.3: - version "0.14.8" - resolved "https://registry.yarnpkg.com/react-addons-test-utils/-/react-addons-test-utils-0.14.8.tgz#dcddc039e71fc3c81d80338e53a3714f14d41e1f" - "react-addons-update@^0.14.0 || ^15.0.0": version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.3.2.tgz#b6385c4db1e5df371825e0615b04360ed94430fe" @@ -6292,14 +6128,6 @@ rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" -samsam@~1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" - -samsam@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" - sane@^1.3.3, sane@~1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/sane/-/sane-1.4.1.tgz#88f763d74040f5f0c256b6163db399bf110ac715" @@ -6444,10 +6272,6 @@ shpjs@^3.3.2: parsedbf "~0.1.2" proj4 "^2.1.4" -sigmund@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" - signal-exit@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" @@ -6458,19 +6282,6 @@ simple-html-index@^1.4.0: dependencies: from2-string "^1.1.0" -sinon-chai@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.8.0.tgz#432a9bbfd51a6fc00798f4d2526a829c060687ac" - -sinon@^1.16.1: - version "1.17.6" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.6.tgz#a43116db59577c8296356afee13fafc2332e58e1" - dependencies: - formatio "1.1.1" - lolex "1.3.2" - samsam "1.1.2" - util ">=0.10.3 <1" - slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -6730,10 +6541,6 @@ supports-color@^3.1.0, supports-color@^3.1.2: dependencies: has-flag "^1.0.0" -supports-color@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" - supports-color@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" @@ -6931,17 +6738,13 @@ to-fast-properties@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" -to-iso-string@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/to-iso-string/-/to-iso-string-0.0.2.tgz#4dc19e664dfccbe25bd8db508b00c6da158255d1" - -tough-cookie@^2.0.0, tough-cookie@^2.3.1, tough-cookie@~2.3.0: +tough-cookie@^2.3.1, tough-cookie@~2.3.0: version "2.3.2" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" dependencies: punycode "^1.4.1" -tr46@~0.0.1, tr46@~0.0.3: +tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -7072,14 +6875,6 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -type-detect@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" - -type-detect@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" - type-is@~1.6.10, type-is@~1.6.13: version "1.6.13" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.13.tgz#6e83ba7bc30cd33a7bb0b7fb00737a2085bf9d08" @@ -7194,7 +6989,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -"util@>=0.10.3 <1", util@~0.10.1, util@0.10.3: +util@~0.10.1, util@0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" dependencies: @@ -7308,12 +7103,6 @@ whatwg-fetch@>=0.10.0: version "1.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.0.0.tgz#01c2ac4df40e236aaa18480e3be74bd5c8eb798e" -whatwg-url-compat@~0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" - dependencies: - tr46 "~0.0.1" - whatwg-url@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-3.0.0.tgz#b9033c50c7ce763e91d78777ce825a6d7f56dac5" @@ -7413,10 +7202,6 @@ xmlbuilder@2.6.2: dependencies: lodash "~3.5.0" -"xmlhttprequest@>= 1.6.0 < 2.0.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" - xtend@^4.0.0, xtend@^4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From 28caf813899f5bffcd721911546039060a2857d2 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Thu, 3 Nov 2016 12:42:42 +0700 Subject: [PATCH 111/323] fix(client): Fix errors caused by migration --- package.json | 3 +- src/main/client/common/user/Auth0Manager.js | 1 + .../editor/components/PatternStopCard.js | 35 +-- .../editor/components/PatternStopContainer.js | 34 +-- src/main/client/main.js | 10 +- src/main/client/manager/reducers/user.js | 18 +- yarn.lock | 231 +++++++++++++++++- 7 files changed, 277 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index e90831153..f8b8edbc8 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,13 @@ "scripts": { "build": "mastarm build --env production", "prestart": "yarn", - "start": "mastarm build --serve", + "start": "mastarm build --serve --proxy http://localhost:4000/api", "test": "mastarm lint \"src/main/client/**/*.js\"" }, "dependencies": { "@conveyal/woonerf": "^0.2.0", "array-unique": "^0.2.1", + "auth0-lock": "9", "body-parser": "^1.14.2", "bootstrap": "^3.3.7", "change-case": "^3.0.0", diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index 8d3da0926..b98491987 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -1,3 +1,4 @@ +import Auth0Lock from 'auth0-lock' import fetch from 'isomorphic-fetch' import { browserHistory } from 'react-router' diff --git a/src/main/client/editor/components/PatternStopCard.js b/src/main/client/editor/components/PatternStopCard.js index 57fc46397..40395b9b4 100644 --- a/src/main/client/editor/components/PatternStopCard.js +++ b/src/main/client/editor/components/PatternStopCard.js @@ -100,24 +100,24 @@ class PatternStopCard extends Component { if (e.keyCode === 13) { this.handleClick(stopIsActive) } - }} - > -
-

{fullStopName.length > 25 ? fullStopName.substr(0, 25) + '...' : fullStopName}

-
-
-

- {Math.round(cumulativeTravelTime / 60)} (+{Math.round(patternStop.defaultTravelTime / 60)}{patternStop.defaultDwellTime > 0 ? ` +${Math.round(patternStop.defaultDwellTime / 60)}` : ''}) - {' '} - -

-
-
+ }}> +
+

{fullStopName.length > 25 ? fullStopName.substr(0, 25) + '...' : fullStopName}

+
+
+

+ {Math.round(cumulativeTravelTime / 60)} (+{Math.round(patternStop.defaultTravelTime / 60)}{patternStop.defaultDwellTime > 0 ? ` +${Math.round(patternStop.defaultDwellTime / 60)}` : ''}) + {' '} + +

+
+
{/* Collapsible interior div */} {stopIsActive - ?
+ ? ( +
{/* Remove from pattern button */} @@ -146,7 +146,7 @@ class PatternStopCard extends Component { saveActiveEntity('trippattern') }} > - Remove + Remove @@ -186,7 +186,8 @@ class PatternStopCard extends Component {
- :
+ ) + :
}
@@ -197,4 +198,4 @@ class PatternStopCard extends Component { const dropTargetCollect = (connect) => ({connectDropTarget: connect.dropTarget()}) const dragSourceCollect = (connect, monitor) => ({connectDragSource: connect.dragSource(), isDragging: monitor.isDragging()}) -export default DropTarget('card', cardTarget, dropTargetCollect)(DragSource('card', cardSource, dragSourceConnect)(PatternStopCard)) +export default DropTarget('card', cardTarget, dropTargetCollect)(DragSource('card', cardSource, dragSourceCollect)(PatternStopCard)) diff --git a/src/main/client/editor/components/PatternStopContainer.js b/src/main/client/editor/components/PatternStopContainer.js index 64e9f0aaf..a80fa1052 100644 --- a/src/main/client/editor/components/PatternStopContainer.js +++ b/src/main/client/editor/components/PatternStopContainer.js @@ -87,22 +87,22 @@ class PatternStopContainer extends Component { cumulativeTravelTime += card.defaultDwellTime + card.defaultTravelTime return ( this.dropCard()} - activeStop={this.state.activeStop} - setActiveStop={(stopKey) => this.setState({activeStop: stopKey})} + key={card.id} + id={card.id} + index={i} + style={this.props.cardStyle} + stopIsActive={false} + cumulativeTravelTime={cumulativeTravelTime} + stop={stop} + activePattern={this.props.activePattern} + updateActiveEntity={this.props.updateActiveEntity} + saveActiveEntity={this.props.saveActiveEntity} + patternStop={card} + moveCard={this.moveCard} + findCard={this.findCard} + dropCard={() => this.dropCard()} + activeStop={this.state.activeStop} + setActiveStop={(stopKey) => this.setState({activeStop: stopKey})} /> ) })} @@ -112,4 +112,4 @@ class PatternStopContainer extends Component { } // TODO: Verify correct order -export default DrapDropContext(HTML5Backend)(DropTarget('card', cardTarget, (connect) => ({connectDropTarget: connect.dropTarget()}))(PatternStopContainer)) +export default DragDropContext(HTML5Backend)(DropTarget('card', cardTarget, (connect) => ({connectDropTarget: connect.dropTarget()}))(PatternStopContainer)) diff --git a/src/main/client/main.js b/src/main/client/main.js index 4e42d9b2d..43f21859d 100644 --- a/src/main/client/main.js +++ b/src/main/client/main.js @@ -16,11 +16,11 @@ if (config.modules.gtfsplus && config.modules.gtfsplus.enabled) { } config.modules.editor.spec = require('../../../gtfs.yml') -var lang = { - english: require('../../../i18n/english.yml'), - espanol: require('../../../i18n/espanol.yml'), - francais: require('../../../i18n/francais.yml') -} +const lang = [ + require('../../../i18n/english.yml'), + require('../../../i18n/espanol.yml'), + require('../../../i18n/francais.yml') +] // is an array containing all the matching modules config.messages = {} diff --git a/src/main/client/manager/reducers/user.js b/src/main/client/manager/reducers/user.js index ff5b56979..677101469 100644 --- a/src/main/client/manager/reducers/user.js +++ b/src/main/client/manager/reducers/user.js @@ -16,31 +16,31 @@ const user = (state = { }, action) => { switch (action.type) { case 'CHECKING_EXISTING_LOGIN': - return update(state, { isCheckingLogin: { $set: true }}) + return update(state, { isCheckingLogin: { $set: true } }) case 'NO_EXISTING_LOGIN': - return update(state, { isCheckingLogin: { $set: false }}) + return update(state, { isCheckingLogin: { $set: false } }) case 'USER_LOGGED_IN': return update(state, { isCheckingLogin: { $set: false }, token: { $set: action.token }, profile: { $set: action.profile }, - permissions: { $set: new UserPermissions(action.profile.app_metadata.datatools)}, - subscriptions: { $set: new UserSubscriptions(action.profile.app_metadata.datatools)}, + permissions: { $set: new UserPermissions(action.profile.app_metadata.datatools) }, + subscriptions: { $set: new UserSubscriptions(action.profile.app_metadata.datatools) } }) case 'USER_LOGGED_OUT': - console.log('USER_LOGGED_OUT'); + console.log('USER_LOGGED_OUT') return update(state, { isCheckingLogin: { $set: false }, token: { $set: null }, profile: { $set: null }, - permissions: { $set: null}, - subscriptions: { $set: null} + permissions: { $set: null }, + subscriptions: { $set: null } }) case 'CREATED_PUBLIC_USER': return update(state, { profile: { $set: action.profile }, - permissions: { $set: new UserPermissions(action.profile.app_metadata.datatools)}, - subscriptions: { $set: new UserSubscriptions(action.profile.app_metadata.datatools)}, + permissions: { $set: new UserPermissions(action.profile.app_metadata.datatools) }, + subscriptions: { $set: new UserSubscriptions(action.profile.app_metadata.datatools) } }) case 'RECEIVE_USER_RECENT_ACTIVITY': return update(state, { diff --git a/yarn.lock b/yarn.lock index 63dbfe843..0f567ce33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -297,6 +297,19 @@ auth0-js@^7.3.0: winchan "0.1.4" xtend "~2.1.1" +auth0-js@6.8.4: + version "6.8.4" + resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-6.8.4.tgz#430dd4cacb64d8d15d69b1e621184f9a2a640a61" + dependencies: + Base64 "~0.1.3" + json-fallback "0.0.1" + jsonp "~0.0.4" + qs "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays" + reqwest "^1.1.4" + trim "~0.0.1" + winchan "^0.1.1" + xtend "~2.1.1" + auth0-js@7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-7.3.0.tgz#2a05a5a4ca21faa28aa927bbeef3194b2a95847d" @@ -325,6 +338,24 @@ auth0-lock@^10.5.1: react-dom "^15.0.0 || ^16.0.0" trim "0.0.1" +auth0-lock@9: + version "9.2.3" + resolved "https://registry.yarnpkg.com/auth0-lock/-/auth0-lock-9.2.3.tgz#c872a9211f5421e04e3c08bf0dd8d1e7fd0f23e8" + dependencies: + auth0-js "6.8.4" + bean "~1.0.4" + blueimp-md5 "^1.1.0" + bonzo "^1.3.6" + brfs "^1.4.0" + debug "^2.2.0" + domready "~0.2.13" + ejsify "0.1.0" + packageify "^0.2.0" + password-sheriff "^0.4.0" + sizzle "^2.0.0" + trim "0.0.1" + underscore "~1.5.2" + autolinker@~0.15.0: version "0.15.3" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" @@ -1094,6 +1125,10 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bean@~1.0.4: + version "1.0.15" + resolved "https://registry.yarnpkg.com/bean/-/bean-1.0.15.tgz#b4a9fff82618b071471676c8b190868048c34775" + binary-extensions@^1.0.0: version "1.7.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.7.0.tgz#6c1610db163abfb34edfe42fa423343a1e01185d" @@ -1114,6 +1149,10 @@ bluebird@^3.0.5, bluebird@^3.3.4: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" +blueimp-md5@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-1.1.1.tgz#cf84ba18285f5c8835dae8ddae5af6468ceace17" + blueimp-md5@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.3.1.tgz#992a6737733b9da1edd641550dc3acab2e9cfc5a" @@ -1160,6 +1199,10 @@ bole@^2.0.0: individual ">=3.0.0 <3.1.0-0" json-stringify-safe ">=5.0.0 <5.1.0-0" +bonzo@^1.3.6: + version "1.4.0" + resolved "https://registry.yarnpkg.com/bonzo/-/bonzo-1.4.0.tgz#d7d2e06f6b6f67eb3b8fc18774f79058f4ab34df" + boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -1189,6 +1232,15 @@ brackets2dots@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brackets2dots/-/brackets2dots-1.1.0.tgz#3f3d40375fc660ce0fd004fa27d67b34f9469ac3" +brfs@^1.4.0: + version "1.4.3" + resolved "https://registry.yarnpkg.com/brfs/-/brfs-1.4.3.tgz#db675d6f5e923e6df087fca5859c9090aaed3216" + dependencies: + quote-stream "^1.0.1" + resolve "^1.1.5" + static-module "^1.1.0" + through2 "^2.0.0" + brorand@^1.0.1: version "1.0.6" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" @@ -1369,6 +1421,10 @@ buffer-equal-constant-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" +buffer-equal@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" + buffer-peek-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-peek-stream/-/buffer-peek-stream-1.0.1.tgz#53b47570a1347787c5bad4ca2ca3021f9d8b3cfd" @@ -1756,7 +1812,7 @@ concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@ readable-stream "~2.0.0" typedarray "~0.0.5" -concat-stream@~1.4.7: +concat-stream@~1.4.5, concat-stream@~1.4.7: version "1.4.10" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" dependencies: @@ -2145,6 +2201,10 @@ domain-browser@~1.1.0: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" +domready@~0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/domready/-/domready-0.2.13.tgz#5ec8340b5ee5f76ad72fd32007cfb5d38a2cd361" + dot-case@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.0.tgz#4b43dd0d7403c34cb645424add397e80bfe85ca6" @@ -2164,6 +2224,12 @@ duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: dependencies: readable-stream "^2.0.2" +duplexer2@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" + dependencies: + readable-stream "~1.1.9" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -2180,6 +2246,17 @@ ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" +ejs@~0.8.3: + version "0.8.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-0.8.8.tgz#ffdc56dcc35d02926dd50ad13439bbc54061d598" + +ejsify@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/ejsify/-/ejsify-0.1.0.tgz#bf910f01a5eee71b7016ef614726003a6c1fae77" + dependencies: + ejs "~0.8.3" + through "~2.3.4" + element-class@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e" @@ -2318,6 +2395,25 @@ escodegen@^1.6.1, escodegen@1.8.x: optionalDependencies: source-map "~0.2.0" +escodegen@~0.0.24: + version "0.0.28" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-0.0.28.tgz#0e4ff1715f328775d6cab51ac44a406cd7abffd3" + dependencies: + esprima "~1.0.2" + estraverse "~1.3.0" + optionalDependencies: + source-map ">= 0.1.2" + +escodegen@~1.3.2: + version "1.3.3" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.3.3.tgz#f024016f5a88e046fd12005055e939802e6c5f23" + dependencies: + esprima "~1.1.1" + estraverse "~1.5.0" + esutils "~1.0.0" + optionalDependencies: + source-map "~0.1.33" + escope@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" @@ -2414,6 +2510,14 @@ esprima@^2.6.0, esprima@^2.7.1, esprima@2.7.x: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" +esprima@~1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + +esprima@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.1.1.tgz#5b6f1547f4d102e670e140c509be6771d6aeb549" + esprima@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" @@ -2433,6 +2537,14 @@ estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" +estraverse@~1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.3.2.tgz#37c2b893ef13d723f276d878d60d8535152a6c42" + +estraverse@~1.5.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" + estraverse@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2" @@ -2441,6 +2553,10 @@ esutils@^2.0.0, esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +esutils@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" + etag@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" @@ -2572,6 +2688,15 @@ extsprintf@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" +falafel@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/falafel/-/falafel-1.2.0.tgz#c18d24ef5091174a497f318cd24b026a25cddab4" + dependencies: + acorn "^1.0.3" + foreach "^2.0.5" + isarray "0.0.1" + object-keys "^1.0.6" + fast-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-bind/-/fast-bind-1.0.0.tgz#7fa9652cb3325f5cd1e252d6cb4f160de1a76e75" @@ -2739,6 +2864,10 @@ for-own@^0.1.4: dependencies: for-in "^0.1.5" +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -4743,6 +4872,14 @@ object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@ version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" +object-inspect@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-0.4.0.tgz#f5157c116c1455b243b06ee97703392c5ad89fec" + +object-keys@^1.0.6: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + object-keys@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" @@ -4843,6 +4980,12 @@ outpipe@^1.1.0: dependencies: shell-quote "^1.4.2" +packageify@^0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/packageify/-/packageify-0.2.3.tgz#73f8f970dc7970cb6c00fb65fdd48b17bf6510f3" + dependencies: + through "^2.3.4" + pad-left@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/pad-left/-/pad-left-2.1.0.tgz#16e6a3b2d44a8e138cb0838cc7cb403a4fc9e994" @@ -4931,6 +5074,12 @@ pascal-case@^2.0.0: camel-case "^3.0.0" upper-case-first "^1.1.0" +password-sheriff@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/password-sheriff/-/password-sheriff-0.4.0.tgz#0d64abcb6c9c74ac5ec9b5c83657c9ae7e0e50e4" + dependencies: + underscore "^1.6.0" + password-sheriff@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/password-sheriff/-/password-sheriff-1.0.1.tgz#fd63fbb44714258a26419f4800c3121e0dcb40b2" @@ -5423,6 +5572,10 @@ qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" +"qs@git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays": + version "0.6.6" + resolved "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8" + query-string@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638" @@ -5448,6 +5601,21 @@ quickselect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-1.0.0.tgz#02630818f9aae4ecab26f0103f98d061c17c58f3" +quote-stream@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" + dependencies: + buffer-equal "0.0.1" + minimist "^1.1.3" + through2 "^2.0.0" + +quote-stream@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/quote-stream/-/quote-stream-0.0.0.tgz#cde29e94c409b16e19dc7098b89b6658f9721d3b" + dependencies: + minimist "0.0.8" + through2 "~0.4.1" + raf@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/raf/-/raf-3.3.0.tgz#93845eeffc773f8129039f677f80a36044eee2c3" @@ -5796,7 +5964,7 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2. string_decoder "~0.10.x" util-deprecate "~1.0.1" -"readable-stream@>=1.0.33-1 <1.1.0-0": +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1: version "1.0.34" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" dependencies: @@ -6018,6 +6186,10 @@ requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" +reqwest@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-1.1.6.tgz#4b6894d29596bf8e824a25f34975df15562ee813" + reqwest@2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-2.0.5.tgz#00fb15ac4918c419ca82b43f24c78882e66039a1" @@ -6033,7 +6205,7 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.1.7, resolve@1.1.7, resolve@1.1.x: +resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@1.1.7, resolve@1.1.x: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -6220,6 +6392,10 @@ sha.js@^2.3.6, sha.js@~2.4.4: dependencies: inherits "^2.0.1" +shallow-copy@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + shallowequal@0.2.2, shallowequal@0.2.x: version "0.2.2" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e" @@ -6282,6 +6458,10 @@ simple-html-index@^1.4.0: dependencies: from2-string "^1.1.0" +sizzle@^2.0.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sizzle/-/sizzle-2.3.3.tgz#4eb078c37231a56b52e4193f701e7ef8937e606b" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -6320,10 +6500,16 @@ source-map@^0.4.2, source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: +source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, "source-map@>= 0.1.2", source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +source-map@~0.1.33: + version "0.1.43" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" + 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" @@ -6409,6 +6595,28 @@ standard@^8.3.0: eslint-plugin-standard "~2.0.1" standard-engine "~5.1.0" +static-eval@~0.2.0: + version "0.2.4" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-0.2.4.tgz#b7d34d838937b969f9641ca07d48f8ede263ea7b" + dependencies: + escodegen "~0.0.24" + +static-module@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/static-module/-/static-module-1.3.1.tgz#79071d340e4419e4ab5ce87976a9eb67250c8493" + dependencies: + concat-stream "~1.4.5" + duplexer2 "~0.0.2" + escodegen "~1.3.2" + falafel "^1.0.0" + has "^1.0.0" + object-inspect "~0.4.0" + quote-stream "~0.0.0" + readable-stream "~1.0.27-1" + shallow-copy "~0.0.1" + static-eval "~0.2.0" + through2 "~0.4.1" + "statuses@>= 1.3.0 < 2", statuses@~1.3.0, statuses@1: version "1.3.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" @@ -6670,7 +6878,7 @@ throat@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-3.0.0.tgz#e7c64c867cbb3845f10877642f7b60055b8ec0d6" -through@^2.3.6, through@^2.3.7, "through@>=2.2.7 <3", through@~2.3.4: +through@^2.3.4, through@^2.3.6, through@^2.3.7, "through@>=2.2.7 <3", through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -6685,6 +6893,13 @@ through2@^2.0.0, through2@^2.0.1: readable-stream "~2.0.0" xtend "~4.0.0" +through2@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b" + dependencies: + readable-stream "~1.0.17" + xtend "~2.1.1" + through2@~0.6.1: version "0.6.5" resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" @@ -6939,6 +7154,10 @@ underscore@^1.6.0: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" +underscore@~1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.5.2.tgz#1335c5e4f5e6d33bbb4b006ba8c86a00f556de08" + underscore@~1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" @@ -7130,7 +7349,7 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.1" -winchan@0.1.4: +winchan@^0.1.1, winchan@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.1.4.tgz#88fa12411cd542eb626018c38a196bcbb17993bb" From 9652d70e99b953078cc83cdd76db137a9d8969f6 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Thu, 3 Nov 2016 18:30:12 +0700 Subject: [PATCH 112/323] fix(icons): Fix icon references --- package.json | 2 +- .../client/admin/components/CreateUser.js | 5 +- src/main/client/admin/components/UserAdmin.js | 16 ++- src/main/client/admin/components/UserList.js | 97 ++++++++----------- .../alerts/components/AffectedEntity.js | 26 +++-- .../client/alerts/components/AlertEditor.js | 5 +- .../client/alerts/components/AlertPreview.js | 19 ++-- .../client/alerts/components/AlertsList.js | 4 +- .../client/alerts/components/AlertsViewer.js | 4 +- .../client/common/components/ConfirmModal.js | 7 +- .../common/components/DatatoolsNavbar.js | 26 ++--- .../common/components/EditableTextField.js | 21 ++-- .../client/common/components/InfoModal.js | 6 +- .../client/common/components/JobMonitor.js | 8 +- .../common/components/LanguageSelect.js | 7 +- src/main/client/common/components/Loading.js | 4 +- .../common/components/SelectFileModal.js | 2 +- src/main/client/common/components/Sidebar.js | 4 +- .../common/components/SidebarNavItem.js | 2 +- .../common/components/SidebarPopover.js | 8 +- .../containers/FeedSourceActionButton.js | 5 +- .../client/common/containers/StarButton.js | 4 +- src/main/client/common/user/Auth0Manager.js | 2 +- .../client/editor/components/CalendarList.js | 12 +-- .../components/EditorFeedSourcePanel.js | 12 +-- .../client/editor/components/EditorMap.js | 10 +- .../client/editor/components/EditorSidebar.js | 68 ------------- .../client/editor/components/EntityDetails.js | 42 ++++---- .../client/editor/components/EntityList.js | 25 +---- .../client/editor/components/FeedInfoPanel.js | 18 ++-- .../editor/components/PatternStopCard.js | 6 +- .../client/editor/components/RouteEditor.js | 6 +- .../editor/components/TimetableEditor.js | 14 +-- .../editor/components/TripPatternList.js | 38 ++++---- src/main/client/gtfs/components/GtfsMap.js | 10 +- src/main/client/index.css | 1 + .../manager/components/FeedSourceTable.js | 18 ++-- .../manager/components/FeedSourceViewer.js | 20 ++-- .../components/FeedVersionNavigator.js | 10 +- .../manager/components/FeedVersionReport.js | 10 +- .../manager/components/FeedVersionViewer.js | 8 +- .../manager/components/ProjectSettings.js | 4 +- .../manager/components/ProjectViewer.js | 12 +-- .../client/manager/components/UserHomePage.js | 28 +++--- .../validation/GtfsValidationSummary.js | 4 +- .../validation/GtfsValidationViewer.js | 2 +- .../client/public/components/PublicHeader.js | 4 +- .../client/public/components/UserAccount.js | 4 +- src/main/client/signs/components/SignsList.js | 4 +- yarn.lock | 4 + 50 files changed, 282 insertions(+), 396 deletions(-) diff --git a/package.json b/package.json index f8b8edbc8..5be55deed 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,6 @@ "name": "datatools-manager", "version": "0.1.0", "description": "Core application for the Conveyal transit data tools suite", - "main": "server.js", "repository": { "type": "git", "url": "https://github.com/conveyal/datatools-manager.git" @@ -26,6 +25,7 @@ "cors": "^2.3.1", "express": "^4.13.3", "express-jwt": "^3.3.0", + "font-awesome": "^4.7.0", "gravatar": "^1.5.2", "history": "^2.0.1", "isomorphic-fetch": "^2.2.1", diff --git a/src/main/client/admin/components/CreateUser.js b/src/main/client/admin/components/CreateUser.js index c31725eb8..885121eea 100644 --- a/src/main/client/admin/components/CreateUser.js +++ b/src/main/client/admin/components/CreateUser.js @@ -1,5 +1,6 @@ +import {Icon} from '@conveyal/woonerf' import React, { Component, PropTypes } from 'react' -import { Button, Modal, FormControl, ControlLabel, FormGroup, Glyphicon } from 'react-bootstrap' +import { Button, Modal, FormControl, ControlLabel, FormGroup } from 'react-bootstrap' import ReactDOM from 'react-dom' import UserSettings from './UserSettings' @@ -49,7 +50,7 @@ export default class CreateUser extends Component { onClick={this.open.bind(this)} className='pull-right' > -   +   Create User diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index 814fb7053..c49b78a59 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react' import { Grid, Row, Col, Panel, ListGroup, ListGroupItem, Button } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' import Helmet from 'react-helmet' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import ManagerPage from '../../common/components/ManagerPage' import UserList from './UserList' @@ -52,7 +52,7 @@ export default class UserAdmin extends Component { Back to dashboard - {getMessage(messages, 'title')} + {getMessage(messages, 'title')} @@ -96,9 +96,8 @@ export default class UserAdmin extends Component {

: null @@ -110,11 +109,8 @@ export default class UserAdmin extends Component { ?

{getMessage(messages, 'noAccess')}

:

- + style={{marginTop: '150px'}}> +

}
diff --git a/src/main/client/admin/components/UserList.js b/src/main/client/admin/components/UserList.js index 7e944ba5d..f255a1dcb 100644 --- a/src/main/client/admin/components/UserList.js +++ b/src/main/client/admin/components/UserList.js @@ -1,7 +1,7 @@ import React, { PropTypes, Component} from 'react' import ReactDOM from 'react-dom' -import { Panel, Row, Col, Button, Label, ButtonGroup, InputGroup, FormControl, Glyphicon, Image, ListGroup, ListGroupItem } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import { Panel, Row, Col, Button, Label, ButtonGroup, InputGroup, FormControl, Image, ListGroup, ListGroupItem } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' import CreateUser from './CreateUser' import UserSettings from './UserSettings' @@ -25,9 +25,6 @@ export default class UserList extends Component { deleteUser: PropTypes.func, token: PropTypes.string } - constructor (props) { - super(props) - } userSearch () { this.props.userSearch(ReactDOM.findDOMNode(this.refs.searchInput).value) @@ -53,17 +50,15 @@ export default class UserList extends Component { disabled={this.props.page <= 0} onClick={() => { this.props.setPage(this.props.page - 1) - }} - > - + }}> + {this.props.userCount > 0 @@ -74,49 +69,46 @@ export default class UserList extends Component {
- + + { if (e.keyCode === 13) this.userSearch() - }} - /> + }} /> - { ReactDOM.findDOMNode(this.refs.searchInput).value = '' this.props.userSearch('') - }} - /> + }} /> - - - - - + + + + + } > - {this.props.isFetching + {this.props.isFetching ? - - + + : this.props.users.map((user, i) => { return
{this.props.user.email} {permissions.isApplicationAdmin() ? : null}
- + {this.state.isEditing ? + className='pull-right' + bsStyle='primary' + style={{marginRight: '5px'}} + onClick={this.save.bind(this)}> + Save + : null } {this.state.isEditing ? + className='pull-right' + bsStyle='danger' + style={{marginRight: '5px'}} + onClick={this.delete.bind(this)}> + Delete + : null } @@ -229,10 +219,9 @@ class UserRow extends Component { > { this.state.isEditing ? + projects={this.props.projects} + fetchProjectFeeds={this.props.fetchProjectFeeds} + permissions={permissions} /> : '' } diff --git a/src/main/client/alerts/components/AffectedEntity.js b/src/main/client/alerts/components/AffectedEntity.js index df2be80cf..5adb8c43d 100644 --- a/src/main/client/alerts/components/AffectedEntity.js +++ b/src/main/client/alerts/components/AffectedEntity.js @@ -1,7 +1,7 @@ import React from 'react' -import { ListGroupItem, Row, Col, ButtonGroup, Button, Glyphicon, FormControl, Label, Collapse } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import { ListGroupItem, Row, Col, Button, FormControl, Collapse } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' import GtfsSearch from '../../gtfs/components/gtfssearch' import modes from '../modes' @@ -22,8 +22,7 @@ export default class AffectedEntity extends React.Component { let agencyName = '' if (typeof entity.agency !== 'undefined' && entity.agency !== null) { agencyName = entity.agency.name - } - else if (typeof entity.stop !== 'undefined' && entity.stop !== null) { + } else if (typeof entity.stop !== 'undefined' && entity.stop !== null) { const feed = getFeed(this.props.feeds, entity.stop.feed_id) agencyName = typeof feed !== 'undefined' ? feed.name : 'Unknown agency' } @@ -37,7 +36,7 @@ export default class AffectedEntity extends React.Component { case 'AGENCY' : return ( - {agencyName}
+ {agencyName}
Note: this selection will apply to all stops and routes for {agencyName}.
) @@ -46,13 +45,13 @@ export default class AffectedEntity extends React.Component { if (routeName) { summary += ` for ${routeName}` } - return {summary} + return {summary} case 'ROUTE' : summary = routeName if (stopName) { summary += ` at ${stopName}` } - return {summary} + return {summary} case 'MODE' : summary = val.name if (stopName) { @@ -60,7 +59,7 @@ export default class AffectedEntity extends React.Component { } return ( - {type}: {summary}
+ {type}: {summary}
Note: this selection will apply to all {val.name.toLowerCase()} routes{stopName && ` stopping at ${stopName}`}.
) @@ -74,7 +73,7 @@ export default class AffectedEntity extends React.Component { >
- + {this.getEntitySummary(this.props.entity)}
@@ -83,9 +82,8 @@ export default class AffectedEntity extends React.Component { bsSize='small' className='pull-right' style={{marginTop: '5px'}} - onClick={() => this.props.onDeleteEntityClick(this.props.entity)} - > - + onClick={() => this.props.onDeleteEntityClick(this.props.entity)}> + @@ -287,8 +285,8 @@ class RouteSelector extends React.Component { this.props.route ? { route: this.props.route, - 'value': this.props.route.route_id, - 'label': `${getRouteNameAlerts(this.props.route)} (${agencyName})` + value: this.props.route.route_id, + label: `${getRouteNameAlerts(this.props.route)} (${agencyName})` } : '' } diff --git a/src/main/client/alerts/components/AlertEditor.js b/src/main/client/alerts/components/AlertEditor.js index 0d828a84c..fccca9bf2 100644 --- a/src/main/client/alerts/components/AlertEditor.js +++ b/src/main/client/alerts/components/AlertEditor.js @@ -1,7 +1,8 @@ +import {Icon} from '@conveyal/woonerf' import React from 'react' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' -import { Grid, Row, Col, Label, ButtonGroup, Button, FormControl, ControlLabel, FormGroup, Panel, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Grid, Row, Col, Label, ButtonGroup, Button, FormControl, ControlLabel, FormGroup, Panel, ListGroup, ListGroupItem } from 'react-bootstrap' import DateTimeField from 'react-bootstrap-datetimepicker' import ManagerPage from '../../common/components/ManagerPage' @@ -134,7 +135,7 @@ export default class AlertEditor extends React.Component { (evt) => { browserHistory.push('/alerts') }} - > Back + > Back diff --git a/src/main/client/alerts/components/AlertPreview.js b/src/main/client/alerts/components/AlertPreview.js index 6ab0674f2..d31b0d559 100644 --- a/src/main/client/alerts/components/AlertPreview.js +++ b/src/main/client/alerts/components/AlertPreview.js @@ -1,16 +1,11 @@ +import {Icon} from '@conveyal/woonerf' import React from 'react' import moment from 'moment' -import { Panel, Grid, Row, Col, ButtonGroup, Button, Glyphicon, Label } from 'react-bootstrap' -import { Link } from 'react-router' +import { Panel, Row, Col, ButtonGroup, Button, Label } from 'react-bootstrap' import { checkEntitiesForFeeds } from '../../common/util/permissions' export default class AlertPreview extends React.Component { - - constructor (props) { - super(props) - } - render () { const canPublish = checkEntitiesForFeeds(this.props.alert.affectedEntities, this.props.publishableFeeds) const canEdit = checkEntitiesForFeeds(this.props.alert.affectedEntities, this.props.editableFeeds) @@ -25,10 +20,10 @@ export default class AlertPreview extends React.Component { const editButtonMessage = this.props.alert.published && deleteIsDisabled ? 'Cannot edit because alert is published' : !canEdit ? 'Cannot alter alerts for other agencies' : 'Edit alert' const publishedLabel = this.props.alert.published - ? - : + ? + : const entitiesLabel = this.props.alert.affectedEntities.length - ? + ? : return ( diff --git a/src/main/client/alerts/components/AlertsList.js b/src/main/client/alerts/components/AlertsList.js index c69f2e35f..72ed3eb22 100644 --- a/src/main/client/alerts/components/AlertsList.js +++ b/src/main/client/alerts/components/AlertsList.js @@ -1,6 +1,6 @@ import React, { PropTypes, Component } from 'react' import { Row, Col, ButtonGroup, Button, FormControl, FormGroup, Badge, ControlLabel } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { sentence as toSentenceCase } from 'change-case' import AlertPreview from './AlertPreview' @@ -111,7 +111,7 @@ export default class AlertsList extends Component { {this.props.isFetching - ?

+ ?

: this.props.alerts.length ? this.props.alerts.map((alert) => { return

- Service Alerts + Service Alerts {this.props.username} + {this.props.username} } id='basic-nav-dropdown'> browserHistory.push('/account')}>{getMessage(messages, 'account')} {getMessage(messages, 'resetPassword')} @@ -51,7 +50,7 @@ export default class DatatoolsNavbar extends Component { let activeProject = this.props.projects.active projectControl = ( {activeProject.name}} + title={ {activeProject.name}} id='basic-nav-dropdown' > {this.props.projects.all.map(proj => { @@ -71,7 +70,7 @@ export default class DatatoolsNavbar extends Component { let languageControl = ( } + title={} id='basic-nav-dropdown' > {this.props.languages.all ? this.props.languages.all.map(lang => { @@ -111,19 +110,6 @@ export default class DatatoolsNavbar extends Component { {this.props.breadcrumbs} - {/* - - */}

) diff --git a/src/main/client/common/components/EditableTextField.js b/src/main/client/common/components/EditableTextField.js index 289435d14..7f3c89427 100644 --- a/src/main/client/common/components/EditableTextField.js +++ b/src/main/client/common/components/EditableTextField.js @@ -1,6 +1,7 @@ +import {Icon} from '@conveyal/woonerf' import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' -import { Form, FormControl, InputGroup, FormGroup, Glyphicon, Button } from 'react-bootstrap' +import { Form, FormControl, InputGroup, FormGroup, Button } from 'react-bootstrap' import { Link } from 'react-router' export default class EditableTextField extends Component { @@ -82,12 +83,10 @@ export default class EditableTextField extends Component { onClick={(evt) => { evt.preventDefault() this.save() - }} - > - + }}> + //feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name ) @@ -131,12 +130,10 @@ export default class EditableTextField extends Component { ref='editButton' tabIndex={this.props.tabIndex ? this.props.tabIndex : null} onClick={() => this.edit()} - disabled={this.props.disabled !== null ? this.props.disabled : false} - > - + + type='pencil' /> } diff --git a/src/main/client/common/components/InfoModal.js b/src/main/client/common/components/InfoModal.js index f7b49c3ce..0331fedb1 100644 --- a/src/main/client/common/components/InfoModal.js +++ b/src/main/client/common/components/InfoModal.js @@ -1,5 +1,5 @@ -import React from 'react' -import { Modal, Button, Glyphicon } from 'react-bootstrap' +import React from 'react' +import { Modal, Button } from 'react-bootstrap' export default class InfoModal extends React.Component { @@ -20,7 +20,7 @@ export default class InfoModal extends React.Component { this.setState({ showModal: true, title: props.title, - body: props.body, + body: props.body }) } diff --git a/src/main/client/common/components/JobMonitor.js b/src/main/client/common/components/JobMonitor.js index eaa36efbf..14bc3d5be 100644 --- a/src/main/client/common/components/JobMonitor.js +++ b/src/main/client/common/components/JobMonitor.js @@ -41,8 +41,8 @@ export default class JobMonitor extends Component {
{job.status && job.status.error - ? - : + ? + : }
@@ -52,7 +52,7 @@ export default class JobMonitor extends Component { className='pull-right' onClick={() => this.props.removeRetiredJob(job)} > - + {truncate(job.name, 25)}
@@ -66,7 +66,7 @@ export default class JobMonitor extends Component { return (
- +
diff --git a/src/main/client/common/components/LanguageSelect.js b/src/main/client/common/components/LanguageSelect.js index 4bd9140ea..7a40495f3 100644 --- a/src/main/client/common/components/LanguageSelect.js +++ b/src/main/client/common/components/LanguageSelect.js @@ -1,6 +1,7 @@ -import React, { PropTypes } from 'react' +import {Icon} from '@conveyal/woonerf' +import React from 'react' import fetch from 'isomorphic-fetch' -import { Glyphicon, Label, FormControl } from 'react-bootstrap' +import {Label, FormControl} from 'react-bootstrap' import { PureComponent, shallowEqual } from 'react-pure-render' import Select from 'react-select' @@ -28,7 +29,7 @@ export default class LanguageSelect extends React.Component { } } renderOption (option) { - return {option.region ? : } {option.label} {option.link} + return {option.region ? : } {option.label} {option.link} } onChange (value) { this.setState({value}) diff --git a/src/main/client/common/components/Loading.js b/src/main/client/common/components/Loading.js index b054515a6..39c310732 100644 --- a/src/main/client/common/components/Loading.js +++ b/src/main/client/common/components/Loading.js @@ -1,6 +1,6 @@ import React from 'react' import { Row, Col } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' export default class Loading extends React.Component { @@ -9,7 +9,7 @@ export default class Loading extends React.Component { return ( -

+

) diff --git a/src/main/client/common/components/SelectFileModal.js b/src/main/client/common/components/SelectFileModal.js index 4ab2ff4f7..41efd0d79 100644 --- a/src/main/client/common/components/SelectFileModal.js +++ b/src/main/client/common/components/SelectFileModal.js @@ -1,5 +1,5 @@ import React from 'react' -import { Modal, Button, FormControl, Glyphicon } from 'react-bootstrap' +import { Modal, Button, FormControl } from 'react-bootstrap' import ReactDOM from 'react-dom' export default class SelectFileModal extends React.Component { diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index ae3926181..1dd003c55 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react' import { Navbar, Button, ButtonToolbar, Checkbox } from 'react-bootstrap' import { browserHistory } from 'react-router' import { Link } from 'react-router' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import SidebarNavItem from './SidebarNavItem' import SidebarPopover from './SidebarPopover' @@ -153,7 +153,7 @@ export default class Sidebar extends Component { + }}> Log out diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index c1b35c81b..42211c5c6 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -70,7 +70,7 @@ export default class SidebarNavItem extends Component {
:
- +
const tooltip = {this.props.label} let containerProps = { diff --git a/src/main/client/common/components/SidebarPopover.js b/src/main/client/common/components/SidebarPopover.js index ece5cc923..f97606069 100644 --- a/src/main/client/common/components/SidebarPopover.js +++ b/src/main/client/common/components/SidebarPopover.js @@ -1,6 +1,7 @@ +import {Icon} from '@conveyal/woonerf' import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' -import { Glyphicon, Popover } from 'react-bootstrap' +import { Popover } from 'react-bootstrap' export default class SidebarPopover extends Component { @@ -74,11 +75,10 @@ export default class SidebarPopover extends Component { const title = (
{this.props.title} - this.props.close() } - /> + onClick={() => this.props.close() } />
) return ( diff --git a/src/main/client/common/containers/FeedSourceActionButton.js b/src/main/client/common/containers/FeedSourceActionButton.js index e159f2b30..c37e79a1d 100644 --- a/src/main/client/common/containers/FeedSourceActionButton.js +++ b/src/main/client/common/containers/FeedSourceActionButton.js @@ -1,3 +1,4 @@ +import {Icon} from '@conveyal/woonerf' import React, { Component } from 'react' import { Button, Glyphicon, MenuItem } from 'react-bootstrap' import { updateTargetForSubscription } from '../../manager/actions/user' @@ -6,7 +7,7 @@ import { connect } from 'react-redux' import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' import WatchButton from './WatchButton' -export default class FeedSourceActionButton extends Component { +class FeedSourceActionButton extends Component { render () { return ( View public page - Delete + Delete ) diff --git a/src/main/client/common/containers/StarButton.js b/src/main/client/common/containers/StarButton.js index 67d373a7b..e0f2de9d6 100644 --- a/src/main/client/common/containers/StarButton.js +++ b/src/main/client/common/containers/StarButton.js @@ -16,8 +16,8 @@ class StarButton extends React.Component { dispatch(updateStar(user.profile, target, !isStarred)) }}> {isStarred - ? {getMessage(messages, 'unstar')} - : {getMessage(messages, 'star')} + ? {getMessage(messages, 'unstar')} + : {getMessage(messages, 'star')} } ) diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index b98491987..421e90e75 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -25,7 +25,7 @@ export default class Auth0Manager { getToken () { // Retrieves the user token from localStorage - return localStorage.getItem('userToken') + return window.localStorage.getItem('userToken') } checkExistingLogin () { diff --git a/src/main/client/editor/components/CalendarList.js b/src/main/client/editor/components/CalendarList.js index 156b88859..25a3fd4a7 100644 --- a/src/main/client/editor/components/CalendarList.js +++ b/src/main/client/editor/components/CalendarList.js @@ -94,7 +94,7 @@ export default class CalendarList extends Component { ) }) - : + : } @@ -176,8 +176,8 @@ export default class CalendarList extends Component { }} > {!this.props.tableView - ? Switch to table view - : Switch to list view + ? Switch to table view + : Switch to list view } ) @@ -196,20 +196,20 @@ export default class CalendarList extends Component { bsSize='small' disabled={!activeEntity} > - + {/*tableViewButton*/} diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 133d8ee3d..4fa8e2bf1 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import { Panel, Row, Col, Table, ButtonToolbar, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' import { browserHistory } from 'react-router' import moment from 'moment' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import ConfirmModal from '../../common/components/ConfirmModal' import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' @@ -76,7 +76,7 @@ export default class EditorFeedSourcePanel extends Component { bsStyle='success' onClick={() => browserHistory.push(`/feed/${this.props.feedSource.id}/edit/`)} > - {getMessage(messages, 'createFromScratch')} + {getMessage(messages, 'createFromScratch')} {' '}or{' '} @@ -143,10 +143,10 @@ class SnapshotItem extends Component { - created {moment(snapshot.snapshotTime).fromNow()} + created {moment(snapshot.snapshotTime).fromNow()}

) diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 7fceea4dd..21fe59d60 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -764,7 +764,7 @@ export default class EditorMap extends Component { this.props.saveActiveEntity('trippattern') }} > - + Edit stop}> Remove from pattern}> @@ -782,7 +782,7 @@ export default class EditorMap extends Component { this.removeStopFromPattern(this.props.currentPattern, stop, index) }} > - + - + @@ -912,7 +912,7 @@ export default class EditorMap extends Component {
{stop.stop_name}
Add stop} + title={ Add stop} id={`split-button-basic-${i}`} bsStyle='success' onSelect={(key) => { diff --git a/src/main/client/editor/components/EditorSidebar.js b/src/main/client/editor/components/EditorSidebar.js index 733019b03..9070d9b1e 100644 --- a/src/main/client/editor/components/EditorSidebar.js +++ b/src/main/client/editor/components/EditorSidebar.js @@ -1,7 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Nav, NavItem, OverlayTrigger, Tooltip } from 'react-bootstrap' import { browserHistory } from 'react-router' -import {Icon} from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' import { gtfsIcons } from '../util/gtfs' @@ -48,70 +47,3 @@ export default class EditorSidebar extends Component { ) } } - -/* -export default class EditorSidebar extends Component { - - static propTypes = { - activeComponent: PropTypes.string, - feedSource: PropTypes.object, - feedInfo: PropTypes.object, - setActiveEntity: PropTypes.func - } - - constructor (props) { - super(props) - } - shouldComponentUpdate (nextProps) { - return !shallowEqual(nextProps, this.props) - } - render () { - const sidebarItems = [ - { - id: 'back', - icon: 'reply', - title: 'Back to feed source' - }, - ...gtfsIcons - ] - const { activeComponent, feedSource, setActiveEntity } = this.props - return ( - - ) - } - -}*/ diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index a37cedcbd..21c07f3ed 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import { Checkbox, Radio, Button, ButtonToolbar, Form, FormControl, FormGroup, ControlLabel, Nav, NavItem, Tooltip, OverlayTrigger, Panel } from 'react-bootstrap' -import {Icon, IconStack} from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import reactCSS from 'reactcss' import validator from 'validator' import { shallowEqual } from 'react-pure-render' @@ -812,7 +812,7 @@ export default class EntityDetails extends Component { rules.unshift({fare_id: this.props.activeEntity.fare_id}) this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) }} - > Add rule + > Add rule {this.props.activeEntity.fareRules.map((rule, index) => { let ruleEntity if (rule.route_id) { @@ -833,7 +833,7 @@ export default class EntityDetails extends Component { rules.splice(index, 1) this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) }} - > + > + > {isNotValid ? {moment(+date).format('MM/DD/YYYY')} appears in another schedule exception. Please choose another date. @@ -1160,7 +1160,7 @@ export default class EntityDetails extends Component { dates.push(0) this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {dates: dates}) }} - > Add date + > Add date
@@ -1269,7 +1269,7 @@ export default class EntityDetails extends Component { } }} > - + : null @@ -1292,7 +1292,7 @@ export default class EntityDetails extends Component { this.setState(update(this.state, stateUpdate)) }} > - + Save changes}> @@ -1308,25 +1308,25 @@ export default class EntityDetails extends Component { } }} > - + {this.props.activeComponent === 'route' && entity - ? - - - + ?
+ + +
: iconName - ? - - - + ?
+ + +
// schedule exception icon if no icon founds - : - - - + :
+ + +
} {' '} @@ -1349,7 +1349,7 @@ export default class EntityDetails extends Component { marginTop: '150px' }} > - +
: [ diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index 90b064d3e..58ff4545d 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -155,7 +155,7 @@ export default class EntityList extends Component { this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) }} > - Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} + Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent}
@@ -218,7 +218,7 @@ export default class EntityList extends Component { // this.props.cloneEntity(this.props.feedSource.id, this.props.activeComponent, activeEntity.id) }} > - + : null @@ -231,7 +231,7 @@ export default class EntityList extends Component { this.props.cloneEntity(this.props.feedSource.id, this.props.activeComponent, activeEntity.id) }} > - + Delete {this.props.activeComponent}}> @@ -270,7 +270,7 @@ export default class EntityList extends Component { } }} > - + @@ -284,26 +284,11 @@ export default class EntityList extends Component { this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) }} > - New {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} + New {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} }
{/* Table view button */} - { - // - } -
{this.props.activeComponent === 'calendar' || this.props.activeComponent === 'scheduleexception' ?
diff --git a/src/main/client/editor/components/TripPatternList.js b/src/main/client/editor/components/TripPatternList.js index da3408b27..b8b0e54eb 100644 --- a/src/main/client/editor/components/TripPatternList.js +++ b/src/main/client/editor/components/TripPatternList.js @@ -139,8 +139,8 @@ export default class TripPatternList extends Component { } const isActive = activePatternId && pattern.id === activePatternId const timetableOptions = [ - Use timetables, - Use frequencies + Use timetables, + Use frequencies ] const patternName = `${`${pattern.name.length > 35 ? pattern.name.substr(0, 35) + '...' : pattern.name}`} ${pattern.patternStops ? `(${pattern.patternStops.length} stops)` : ''}` return ( @@ -163,7 +163,7 @@ export default class TripPatternList extends Component { else this.props.setActiveEntity(feedSource.id, 'route', activeEntity, 'trippattern', pattern) }} > - + {' '} {pattern.name ? patternName : '[Unnamed]'}

@@ -198,7 +198,7 @@ export default class TripPatternList extends Component { }) }} > - Save + Save , , ] : [ @@ -232,7 +232,7 @@ export default class TripPatternList extends Component { this.props.updateEditSetting('editGeometry', !this.props.editSettings.editGeometry) }} > - Edit + Edit , , ] } @@ -401,8 +401,8 @@ export default class TripPatternList extends Component { > { this.props.editSettings.addStops - ? Cancel - : Add stop + ? Cancel + : Add stop } @@ -490,7 +490,7 @@ export default class TripPatternList extends Component { this.props.updateEditSetting('addStops', !this.props.editSettings.addStops) }} > - Cancel + Cancel
@@ -502,7 +502,7 @@ export default class TripPatternList extends Component { }} className='small' > - Add stop + Add stop

} @@ -538,7 +538,7 @@ export default class TripPatternList extends Component { }} bsStyle='default' > - Calc. times + Calc. times @@ -551,7 +551,7 @@ export default class TripPatternList extends Component { ) }) - : + : } @@ -588,7 +588,7 @@ export default class TripPatternList extends Component { }) }} > - + Duplicate trip pattern}> @@ -599,7 +599,7 @@ export default class TripPatternList extends Component { this.props.cloneEntity(feedSource.id, 'trippattern', activePattern.id, true) }} > - + Delete trip pattern}> @@ -620,7 +620,7 @@ export default class TripPatternList extends Component { }) }} > - + @@ -631,7 +631,7 @@ export default class TripPatternList extends Component { this.props.newGtfsEntity(feedSource.id, 'trippattern', {routeId: activeEntity.id, patternStops: [], name: 'New Pattern', feedId: this.props.feedSource.id, id: 'new'}, true) }} > - New pattern + New pattern
diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index 780849ff5..5d11ec060 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -5,7 +5,7 @@ import { Button, FormControl, ControlLabel } from 'react-bootstrap' import { shallowEqual } from 'react-pure-render' import { divIcon, Browser } from 'leaflet' import { Map, Marker, Popup, TileLayer, GeoJson, FeatureGroup, Rectangle } from 'react-leaflet' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { getFeed, getFeedId } from '../../common/util/modules' import { getFeedsBounds } from '../../common/util/geo' @@ -180,7 +180,7 @@ export default class GtfsMap extends Component { >
-

{stop.stop_name} ({stop.stop_id})

+

{stop.stop_name} ({stop.stop_id})

{this.props.renderTransferPerformance && this.renderTransferPerformance(stop)} {this.props.onStopClick ? : null } @@ -207,7 +207,7 @@ export default class GtfsMap extends Component { const popup = (
-

{getRouteName(route)}

+

{getRouteName(route)}

  • ID: {route.route_id}
  • Agency:{' '} @@ -223,7 +223,7 @@ export default class GtfsMap extends Component { block onClick={() => this.props.onRouteClick(route, feed, this.props.newEntityId)} > - {this.props.popupAction} route + {this.props.popupAction} route :

    [Must add stops first]

    } diff --git a/src/main/client/index.css b/src/main/client/index.css index ce2b0d249..79e555c19 100644 --- a/src/main/client/index.css +++ b/src/main/client/index.css @@ -1,4 +1,5 @@ +@import url(node_modules/font-awesome/css/font-awesome.css); @import url(node_modules/leaflet/dist/leaflet.css); @import url(node_modules/leaflet-draw/dist/leaflet.draw.css); diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index b56f80499..c48aebc30 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -4,7 +4,7 @@ import moment from 'moment' import { Button, Table, Checkbox, Glyphicon, Dropdown, MenuItem, Panel, ListGroupItem, ListGroup } from 'react-bootstrap' import { browserHistory, Link } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' -import { Icon, IconStack } from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' import EditableTextField from '../../common/components/EditableTextField' @@ -57,7 +57,7 @@ export default class FeedSourceTable extends Component { return ( {this.props.isFetching - ? + ? : this.props.feedSources.length ? this.props.feedSources.map((feedSource) => { return }) : - + } @@ -163,25 +163,25 @@ class FeedSourceTableRow extends Component { :
  • No versions exist yet.
  • } {fs.latestValidation && fs.latestValidation.errorCount > 0 - ?
  • {fs.latestValidation.errorCount}
  • + ?
  • {fs.latestValidation.errorCount}
  • : fs.latestValidation ?
  • :
  • } {fs.latestValidation && fs.latestValidation.endDate < +moment() - ?
  • + ?
  • : fs.latestValidation ?
  • :
  • } {isModuleEnabled('deployment') && fs.deployable - ?
  • + ?
  • : isModuleEnabled('deployment') - ?
  • + ?
  • : null } {fs.url - ?
  • + ?
  • : null }
@@ -360,7 +360,7 @@ class FeedSourceDropdown extends Component { } View public page - Delete + Delete
diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index 9db3cf6f3..d05ff033c 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' import { LinkContainer } from 'react-router-bootstrap' @@ -108,7 +108,7 @@ export default class FeedSourceViewer extends Component { // alignItems: 'center', }} > - + @@ -213,7 +213,7 @@ export default class FeedSourceViewer extends Component {

This feed source is currently {fs.isPublic ? 'public' : 'private'}.

- +

Delete this feed source.

Once you delete a feed source, it cannot be recovered.

@@ -282,16 +282,16 @@ export default class FeedSourceViewer extends Component { target={fs.id} subscriptionType='feed-updated' /> - +
    -
  • {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
  • -
  • {fs.url ? fs.url : '(none)'} +
  • {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
  • +
  • {fs.url ? fs.url : '(none)'}
  • - {
  • {this.getAverageFileSize(fs.feedVersions)}
  • } + {
  • {this.getAverageFileSize(fs.feedVersions)}
  • }
- {/*
  • {fs.feedVersionCount}
  • {fs.url}*/} + {/*
  • {fs.feedVersionCount}
  • {fs.url}*/} @@ -394,7 +394,7 @@ export default class FeedSourceViewer extends Component { {/* New version} + title={ New version} > Upload Fetch @@ -420,7 +420,7 @@ export default class FeedSourceViewer extends Component { /> {/* - Snapshots}> + Snapshots}> Snapshot 1 diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index 63770253a..2b7ead1c7 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -1,7 +1,7 @@ import React, {Component, PropTypes} from 'react' import { Row, Col, ButtonGroup, ButtonToolbar, DropdownButton, MenuItem, Button, Glyphicon } from 'react-bootstrap' import { browserHistory } from 'react-router' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { isModuleEnabled, getComponentMessages, getMessage } from '../../common/util/config' import { isValidZipFile } from '../../common/util/util' @@ -95,8 +95,8 @@ export default class FeedVersionNavigator extends Component { - - + + {this.state.listView ? null @@ -137,7 +137,7 @@ export default class FeedVersionNavigator extends Component { {isModuleEnabled('editor') ?
    ) const tableOptions = { @@ -122,7 +122,7 @@ export default class FeedVersionReport extends Component { return {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} + footer={ {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} > } > - {getMessage(messages, 'validDates')} + {getMessage(messages, 'validDates')} this.props.publishFeedVersion(version)} > {this.props.isPublished - ? Published + ? Published : Publish to MTC } diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index d7ed6c2ed..ca8f5e0c0 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import { Row, Col, Button, Panel, Label, Glyphicon, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' import { LinkContainer } from 'react-router-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import GtfsValidationViewer from './validation/GtfsValidationViewer' import GtfsValidationExplorer from './validation/GtfsValidationExplorer' @@ -201,14 +201,14 @@ class VersionSectionSelector extends Component { - Version summary + Version summary - Validation issues {this.renderIssuesLabel(version)} + Validation issues {this.renderIssuesLabel(version)} {isModuleEnabled('gtfsplus') ? - GTFS+ for this version + GTFS+ for this version : null } diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index d6775c68a..7be314150 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -6,7 +6,7 @@ import DateTimeField from 'react-bootstrap-datetimepicker' import update from 'react-addons-update' import { shallowEqual } from 'react-pure-render' import { LinkContainer } from 'react-router-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { Row, Col, Button, Panel, Glyphicon, Form, Tabs, Tab, Radio, Checkbox, FormGroup, InputGroup, ControlLabel, FormControl, ListGroup, ListGroupItem } from 'react-bootstrap' import TimezoneSelect from '../../common/components/TimezoneSelect' @@ -253,7 +253,7 @@ export default class ProjectSettings extends Component { Danger zone}> - +

    Delete this organization.

    Once you delete an organization, the organization and all feed sources it contains cannot be recovered.

    diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index 9f7139062..3986845ea 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -3,7 +3,7 @@ import Helmet from 'react-helmet' import moment from 'moment' import { Tabs, Tab, Grid, Row, Label, Col, Button, InputGroup, Table, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' import { sentence as toSentenceCase } from 'change-case' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' import { shallowEqual } from 'react-pure-render' @@ -128,7 +128,7 @@ export default class ProjectViewer extends Component { {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') - ? Sync}> + ? Sync}> {isExtensionEnabled('transitland') ? - {getMessage(messages, 'feeds.update')} + {getMessage(messages, 'feeds.update')} - + +

    {/* Recent Activity List */}

    - Recent Activity + Recent Activity

    {this.props.user.recentActivity && this.props.user.recentActivity.length ? this.props.user.recentActivity.sort(sortByDate).map(item => renderRecentActivity(item)) @@ -115,7 +115,7 @@ export default class UserHomePage extends Component {

    - + Hello, {this.props.user.profile.nickname}.

    @@ -125,7 +125,7 @@ export default class UserHomePage extends Component { -
    {this.props.user.profile.email}
    +
    {this.props.user.profile.email}
    {this.props.user.permissions.isApplicationAdmin() ? 'Application admin' @@ -136,13 +136,13 @@ export default class UserHomePage extends Component { {this.props.user.permissions.isApplicationAdmin() ? : null @@ -160,7 +160,7 @@ export default class UserHomePage extends Component { {activeProject.name} + ? {activeProject.name} : {this.props.user.profile.nickname} } // onSelect={(eventKey) => { @@ -186,7 +186,7 @@ export default class UserHomePage extends Component { return ( - {project.name} + {project.name} ) @@ -194,9 +194,9 @@ export default class UserHomePage extends Component { : null } {activeProject && visibleProjects.length > 1 || !activeProject ? : null} - Manage organizations + Manage organizations - Create organization + Create organization
    {/* Starred Feeds Panel */} @@ -282,7 +282,7 @@ function renderRecentActivity (item) { return (
    - +
    {moment(item.date).fromNow()}
    diff --git a/src/main/client/manager/components/validation/GtfsValidationSummary.js b/src/main/client/manager/components/validation/GtfsValidationSummary.js index c3fb4734c..00dbdb2f1 100644 --- a/src/main/client/manager/components/validation/GtfsValidationSummary.js +++ b/src/main/client/manager/components/validation/GtfsValidationSummary.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react' import { Table, Glyphicon, Button } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' // import { connect } from 'react-redux' import { LinkContainer } from 'react-router-bootstrap' @@ -89,7 +89,7 @@ export default class GtfsValidationSummary extends Component { bsSize='large' style={{marginTop: '20px'}} > - View full validation report + View full validation report
    diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index 80c257b64..d896c1c51 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -1,7 +1,7 @@ import React from 'react' import { Panel, Table, Glyphicon, Button, Badge } from 'react-bootstrap' import { browserHistory } from 'react-router' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { isModuleEnabled, isExtensionEnabled, getComponentMessages, getMessage } from '../../../common/util/config' diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index 088e10cf6..ac84a0ba8 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { Grid, Row, Col, Button, Glyphicon, ButtonToolbar, DropdownButton, MenuItem } from 'react-bootstrap' import { browserHistory } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' @@ -60,7 +60,7 @@ export default class PublicHeader extends Component { {/* "Log out" Button */} {this.props.username - ? + ? : null } diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 0f64dca44..8d21b1ab3 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -1,7 +1,7 @@ import React, { Component, PropTypes } from 'react' import { Grid, Row, Col, Button, Panel, Checkbox, ListGroup, ListGroupItem, ControlLabel } from 'react-bootstrap' import { Link } from 'react-router' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { LinkContainer } from 'react-router-bootstrap' import EditableTextField from '../../common/components/EditableTextField' @@ -160,7 +160,7 @@ export default class UserAccount extends Component { - My settings + My settings diff --git a/src/main/client/signs/components/SignsList.js b/src/main/client/signs/components/SignsList.js index 50b160f3c..6913d42b9 100644 --- a/src/main/client/signs/components/SignsList.js +++ b/src/main/client/signs/components/SignsList.js @@ -1,6 +1,6 @@ import React, { Component, PropTypes } from 'react' import { Row, ButtonGroup, Button, FormControl, FormGroup, Badge } from 'react-bootstrap' -import Icon from '@conveyal/woonerf' +import {Icon} from '@conveyal/woonerf' import { sentence as toSentenceCase } from 'change-case' import SignPreview from './SignPreview' @@ -61,7 +61,7 @@ export default class SignsList extends Component { {this.props.isFetching - ?

    + ?

    : sortedSigns.length ? sortedSigns.map((sign) => { return Date: Mon, 7 Nov 2016 13:26:49 +0700 Subject: [PATCH 113/323] fix(sidebar): Fix the icon in the sidebar and refactor to use CSS. --- .../client/common/components/JobMonitor.js | 48 ++- src/main/client/common/components/Sidebar.js | 274 ++++++++---------- .../common/components/SidebarPopover.js | 55 ++-- src/main/client/common/style.css | 70 +++-- 4 files changed, 221 insertions(+), 226 deletions(-) diff --git a/src/main/client/common/components/JobMonitor.js b/src/main/client/common/components/JobMonitor.js index 14bc3d5be..82297752d 100644 --- a/src/main/client/common/components/JobMonitor.js +++ b/src/main/client/common/components/JobMonitor.js @@ -1,17 +1,16 @@ -import React, { PropTypes, Component } from 'react' -import { ProgressBar, Button } from 'react-bootstrap' -import { Icon } from '@conveyal/woonerf' +import React, {PropTypes} from 'react' +import {ProgressBar, Button} from 'react-bootstrap' +import {Icon, Pure} from '@conveyal/woonerf' import truncate from 'truncate' import SidebarPopover from './SidebarPopover' -export default class JobMonitor extends Component { - +export default class JobMonitor extends Pure { static propTypes = { expanded: PropTypes.bool, jobMonitor: PropTypes.object, target: PropTypes.object, - visible: PropTypes.func, + visible: PropTypes.bool.isRequired, close: PropTypes.func, removeRetiredJob: PropTypes.func } @@ -34,15 +33,14 @@ export default class JobMonitor extends Component { + {...this.props}> {this.props.jobMonitor.retired.map(job => { return (
    {job.status && job.status.error - ? - : + ? + : }
    @@ -52,7 +50,7 @@ export default class JobMonitor extends Component { className='pull-right' onClick={() => this.props.removeRetiredJob(job)} > - + {truncate(job.name, 25)}
    @@ -62,22 +60,20 @@ export default class JobMonitor extends Component { ) })} {this.props.jobMonitor.jobs.length - ? this.props.jobMonitor.jobs.map(job => { - return ( -
    -
    - -
    -
    -
    - {job.name}{/* */} -
    - -
    {job.status ? job.status.message : 'waiting'}
    -
    + ? this.props.jobMonitor.jobs.map(job => ( +
    +
    + +
    +
    +
    + {job.name}
    - ) - }) + +
    {job.status ? job.status.message : 'waiting'}
    +
    +
    + )) :

    No active jobs.

    } diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 1dd003c55..d9a8ab4f9 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -1,26 +1,18 @@ -import React, { Component, PropTypes } from 'react' -import { Navbar, Button, ButtonToolbar, Checkbox } from 'react-bootstrap' -import { browserHistory } from 'react-router' -import { Link } from 'react-router' -import {Icon} from '@conveyal/woonerf' +import React, {PropTypes} from 'react' +import {Navbar, Button, ButtonToolbar, Checkbox} from 'react-bootstrap' +import {Link} from 'react-router' +import {Icon, Pure} from '@conveyal/woonerf' import SidebarNavItem from './SidebarNavItem' import SidebarPopover from './SidebarPopover' import JobMonitor from './JobMonitor' -import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' - -// TODO: Fix PNG import -const icon = "" // import icon from '../../assets/application_icon.png' -const longIcon = "" // import longIcon from '../../assets/application_logo.png' - -export default class Sidebar extends Component { +export default class Sidebar extends Pure { static propTypes = { expanded: PropTypes.bool, jobMonitor: PropTypes.object, username: PropTypes.string, userPicture: PropTypes.string, - loginHandler: PropTypes.func, logoutHandler: PropTypes.func, resetPassword: PropTypes.func, @@ -29,11 +21,8 @@ export default class Sidebar extends Component { setSidebarExpanded: PropTypes.func } - constructor (props) { - super(props) - this.state = { - visiblePopover: null - } + state = { + visiblePopover: null } componentWillReceiveProps (nextProps) { @@ -42,145 +31,138 @@ export default class Sidebar extends Component { } } - navSelected (key) { - this.setState({visiblePopover: (key === this.state.visiblePopover) ? null : key}) + _clickChangePassword = () => { + this.setState({visiblePopover: null}) + this.props.resetPassword() } - render () { - const messages = getComponentMessages('DatatoolsNavbar') + _clickLogOut = () => { + this.setState({visiblePopover: null}) + this.props.logoutHandler() + } + + _closePopover = () => { + this.setState({visiblePopover: null}) + } + + _select = (key) => this.setState({visiblePopover: (key === this.state.visiblePopover) ? null : key}) + _selectHelp = () => this._select('help') + _selectJob = () => this._select('job') + _selectUser = () => this._select('user') + _toggleLabels = () => { + this.props.setSidebarExpanded(!this.props.expanded) + } + + _toggleTutorial = () => { + this.props.setTutorialHidden(!this.props.hideTutorial) + } + + render () { + const {children, expanded, userPicture} = this.props const navbarStyle = { - width: this.props.expanded ? 130 : 50, - height: '100%', - position: 'fixed', - borderRadius: 0, + width: expanded ? 130 : 50 } + return ( +
    + + +
    {children}
    +
    + + + +
    +
    + s + + + + + + + +
    + + Show Sidebar Labels + + + Hide editor tutorial + +
    +
    +
    + ) + } +} - const logoContainerStyle = { - position: 'fixed', - top: 10, - left: 10, - // left: 0, - cursor: 'pointer' - } - const LOGO_SIZE = 30 - const logoIconStyle = { - float: 'left', - // background: '#bbb', - width: LOGO_SIZE, - height: LOGO_SIZE, - fontSize: 24, - fontWeight: 'bold', - textAlign: 'center', - lineHeight: '120%', - } +class Brand extends Pure { + static propTypes = { + expanded: PropTypes.bool.isRequired + } - const logoLabelStyle = { - marginLeft: 40, - marginTop: 6, - lineHeight: '95%', - color: '#bbb', - fontSize: 13, - fontWeight: 'bold' - } - const expandedIcon =
    - const closePopover = () => this.setState({visiblePopover: null}) - const brand = ( + render () { + const {expanded} = this.props + return (
    -
    - {this.props.expanded - ?
    GTFS Data
    Manager
    // TODO: replace with long icon - : null - } -
    + className='LogoContainer'> +
    + {expanded &&
    GTFS Data
    Manager
    } +
    ) - return
    - - {brand} - - {/* Top nav */} -
    - {this.props.children} -
    - - {/* Bottom nav */} -
    - this.navSelected('job')} /> - this.navSelected('user')} /> - this.navSelected('help')} /> -
    -
    - - {/* Job Monitor Popover */} - this.state.visiblePopover === 'job' } - close={() => closePopover()} - removeRetiredJob={this.props.removeRetiredJob} - /> - - {/* User Popover */} - this.state.visiblePopover === 'user' } - close={() => closePopover()} - > - - - - - - - {/* Settings Popover */} - this.state.visiblePopover === 'help' } - close={() => closePopover()} - > -
    - { this.props.setSidebarExpanded(!this.props.expanded) }} - > - Show Sidebar Labels - - { this.props.setTutorialHidden(!this.props.hideTutorial) }} - > - Hide editor tutorial - -
    -
    - -
    } } diff --git a/src/main/client/common/components/SidebarPopover.js b/src/main/client/common/components/SidebarPopover.js index f97606069..e76707f8c 100644 --- a/src/main/client/common/components/SidebarPopover.js +++ b/src/main/client/common/components/SidebarPopover.js @@ -1,26 +1,21 @@ -import {Icon} from '@conveyal/woonerf' -import React, {Component, PropTypes} from 'react' +import {Icon, Pure} from '@conveyal/woonerf' +import React, {PropTypes} from 'react' import ReactDOM from 'react-dom' -import { Popover } from 'react-bootstrap' - -export default class SidebarPopover extends Component { +import {Popover} from 'react-bootstrap' +export default class SidebarPopover extends Pure { static propTypes = { children: PropTypes.node, expanded: PropTypes.bool, target: PropTypes.object, title: PropTypes.string, - close: PropTypes.func, - visible: PropTypes.func + visible: PropTypes.bool.isRequired } - constructor (props) { - super(props) - this.state = { - top: 0, - arrowOffset: 0 - } + state = { + top: 0, + arrowOffset: 0 } componentDidMount () { @@ -28,11 +23,8 @@ export default class SidebarPopover extends Component { this.reposition() } - componentDidUpdate () { - } - componentWillReceiveProps (nextProps) { - if (nextProps.visible()) this.reposition() + if (nextProps.visible) this.reposition() } handleResize () { @@ -64,30 +56,37 @@ export default class SidebarPopover extends Component { this.setState({ top, arrowOffset }) } + _close = () => { + this.props.close() + } + render () { const style = { position: 'fixed', marginLeft: this.props.expanded ? 160 : 60, width: 276, // max from bootstrap top: this.state.top, - visibility: this.props.visible() ? 'visible' : 'hidden' + visibility: this.props.visible ? 'visible' : 'hidden' } - const title = (
    - {this.props.title} - this.props.close() } /> -
    ) + const title = ( +
    + {this.props.title} + +
    + ) return ( - + arrowOffsetTop={this.state.arrowOffset}> {this.props.children} ) diff --git a/src/main/client/common/style.css b/src/main/client/common/style.css index b3c71b811..b712aa59b 100644 --- a/src/main/client/common/style.css +++ b/src/main/client/common/style.css @@ -16,7 +16,6 @@ outline: 0; } - /* EDITOR ENTITY LIST */ .entity-list-row:hover { @@ -38,9 +37,6 @@ .bus-stop-icon:hover { color: yellow; } -/*.bus-stop-icon:active { - background-color: blue; -}*/ .bus-stop-icon-bg:hover { color: yellow; @@ -52,9 +48,6 @@ .bus-stop-icon-fg:hover { color: yellow; } -/*.bus-stop-icon-fg:active { - background-color: blue; -}*/ /*Sticky footer styles*/ @@ -62,32 +55,57 @@ margin: 20px 0; } - - /* ICON HELPER CLASSES */ .icon-link { color: #777; margin-right: 10px; } +/* SIDEBAR */ +.Sidebar { + +} +.Sidebar > nav { + height: 100%; + position: fixed; + border-radius: 0; +} -/* NOTES VIEWER */ +.Sidebar .TopNav { + position: absolute; + top: 60px; +} -/*.comment-panel:after { - content: ""; - border: 10px solid transparent; - height: 0; - border-color: rgba(136, 183, 213, 0); - border-right-color: #f5f5f5; - width: 0; - position: relative; - left: -10px; - top: 0px; -}*/ +.Sidebar .BottomNav { + position: absolute; + bottom: 10px; +} -/* LEAFLET MAP */ -/*.leaflet-overlay-pane path, -.leaflet-marker-icon { - cursor: crosshair; -}*/ +.Sidebar .LogoContainer { + position: fixed; + top: 10px; + left: 10px; + cursor: pointer; +} + +.Sidebar .LogoContainer > .Logo { + float: left; + width: 30px; + height: 30px; + font-size: 24px; + font-weight: bold; + text-align: center; + line-height: 120%; + background-image: url(../assets/application_icon.png); + background-size: contain; +} + +.Sidebar .LogoContainer > .LogoLabel { + margin-left: 40px; + margin-top: 6px; + line-height: 95%; + color: #bbb; + font-size: 13px; + font-weight: bold; +} From 0ad56c74286969674444170c89d8903f712d1f2d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 14 Nov 2016 14:45:40 -0500 Subject: [PATCH 114/323] update eslint, virtualized-select, dom-helpers --- package.json | 5 +++-- .../conveyal/datatools/common/persistence/DataStore.java | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/conveyal/datatools/common/persistence/DataStore.java diff --git a/package.json b/package.json index 59dea609b..7e98fa09d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "clone": "^1.0.2", "cors": "^2.3.1", "css-loader": "^0.23.1", + "dom-helpers": "^3.0.0", "express": "^4.13.3", "express-jwt": "^3.3.0", "file-loader": "^0.8.5", @@ -68,7 +69,7 @@ "react-select": "^1.0.0-beta14", "react-sidebar": "^2.1.2", "react-virtualized": "^7.11.2", - "react-virtualized-select": "^1.0.0", + "react-virtualized-select": "^2.1.0", "reactcss": "^1.0.4", "redux": "^3.3.1", "redux-logger": "^2.6.1", @@ -94,7 +95,7 @@ "autoprefixer": "^6.0.3", "babel-cli": "^6.4.0", "babel-core": "^6.3.26", - "babel-eslint": "^4.1.6", + "babel-eslint": "^7.1.0", "babel-loader": "^6.2.1", "babel-plugin-react-transform": "^2.0.0", "babel-plugin-transform-decorators-legacy": "^1.3.4", diff --git a/src/main/java/com/conveyal/datatools/common/persistence/DataStore.java b/src/main/java/com/conveyal/datatools/common/persistence/DataStore.java new file mode 100644 index 000000000..dca0b9840 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/common/persistence/DataStore.java @@ -0,0 +1,7 @@ +package com.conveyal.datatools.common.persistence; + +/** + * Created by landon on 11/3/16. + */ +public class DataStore { +} From 41f80396ec4af0e14ad98aa14e2198220bc39c7a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 14 Nov 2016 14:48:05 -0500 Subject: [PATCH 115/323] update sidebar loading... --- src/main/client/common/components/Sidebar.js | 6 +++++- .../client/common/components/SidebarNavItem.js | 14 +++++++++++++- .../client/common/components/SidebarPopover.js | 17 ++++++++++------- src/main/client/common/style.css | 5 +++-- 4 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 55856c5b0..474fcbb0d 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -84,6 +84,7 @@ export default class Sidebar extends Component { } const expandedIcon =
    const closePopover = () => this.setState({visiblePopover: null}) + const hasActiveJobs = this.props.jobMonitor.jobs.length > 0 const brand = (
    0} + loading={hasActiveJobs} + finished={this.props.jobMonitor.jobs.length === 0 && this.props.jobMonitor.retired.length > 0} onClick={() => this.navSelected('job')} />
    :
    - +
    const tooltip = {this.props.label} let containerProps = { diff --git a/src/main/client/common/components/SidebarPopover.js b/src/main/client/common/components/SidebarPopover.js index ece5cc923..acfa14ea0 100644 --- a/src/main/client/common/components/SidebarPopover.js +++ b/src/main/client/common/components/SidebarPopover.js @@ -21,20 +21,23 @@ export default class SidebarPopover extends Component { arrowOffset: 0 } } - + _onResize = () => { + this.setState({width: window.innerWidth, height: window.innerHeight}) + } + componentWillMount () { + this._onResize() + } componentDidMount () { - window.addEventListener('resize', () => this.handleResize()) - this.reposition() + window.addEventListener('resize', this._onResize) } - - componentDidUpdate () { + componentWillUnmount () { + window.removeEventListener('resize', this._onResize) } - componentWillReceiveProps (nextProps) { if (nextProps.visible()) this.reposition() } - handleResize () { + _onResize = () => { this.reposition() } diff --git a/src/main/client/common/style.css b/src/main/client/common/style.css index b3c71b811..566d86a1a 100644 --- a/src/main/client/common/style.css +++ b/src/main/client/common/style.css @@ -19,8 +19,9 @@ /* EDITOR ENTITY LIST */ -.entity-list-row:hover { - background-color: '#f5f5f5' +.entity-list-row { + /*background-color: '#f5f5f5';*/ + outline: 'none' } .noselect { From 0e42ae9f131b9a91315e48c7fb135e4269db4fc3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 14 Nov 2016 14:48:31 -0500 Subject: [PATCH 116/323] fix duplicate log in prompt --- src/main/client/common/user/Auth0Manager.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index 3ec79fa40..13fa87ff9 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -62,16 +62,11 @@ export default class Auth0Manager { loginFromToken (token) { return fetch('https://' + this.props.domain + '/tokeninfo?id_token=' + token, { method: 'post' - }).then((res) => { - if(res.status >= 400) { // check for bad response, generally an expired token - // TODO: better handling of expired tokens - return this.loginViaLock({closable: false}) - } - return res.json() - }).then((profile) => { + }).then(res => res.json()).then((profile) => { return constructUserObj(token, profile) }).catch((err) => { console.log('error getting profile', err) + return this.loginViaLock({closable: false}) }) } @@ -99,7 +94,7 @@ export default class Auth0Manager { // initialize auto log out check // this.setupSingleLogout() - + console.log(token, profile) //this.userLoggedIn(token, profile) resolve(constructUserObj(token, profile)) }) From 595b83dd434738e622bb005e0058e01fa0a710ba Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 14 Nov 2016 14:49:13 -0500 Subject: [PATCH 117/323] update timetable client --- .../client/common/components/EditableCell.js | 224 +++--- .../client/editor/components/Timetable.js | 463 ++++++++++++ .../editor/components/TimetableEditor.js | 667 +++--------------- .../editor/components/TimetableHeader.js | 318 +++++++++ 4 files changed, 1003 insertions(+), 669 deletions(-) create mode 100644 src/main/client/editor/components/Timetable.js create mode 100644 src/main/client/editor/components/TimetableHeader.js diff --git a/src/main/client/common/components/EditableCell.js b/src/main/client/common/components/EditableCell.js index eba2f5022..68e32ddd3 100644 --- a/src/main/client/common/components/EditableCell.js +++ b/src/main/client/common/components/EditableCell.js @@ -3,6 +3,9 @@ import ReactDOM from 'react-dom' import moment from 'moment' export default class EditableCell extends Component { + static propTypes = { + isEditing: PropTypes.bool + } constructor (props) { super(props) this.state = { @@ -21,15 +24,15 @@ export default class EditableCell extends Component { }) } componentWillReceiveProps (nextProps) { - if (this.state.data !== nextProps.data) { + if (this.props.data !== nextProps.data) { + // console.log('setting data...', nextProps.data) this.setState({ - data: nextProps.data, + data: nextProps.data // edited: true // originalData: nextProps.data }) } if (this.state.isEditing !== nextProps.isEditing) this.setState({isEditing: nextProps.isEditing}) - if (this.state.isFocused !== nextProps.isFocused) this.setState({isFocused: nextProps.isFocused}) } cancel () { this.setState({ @@ -37,17 +40,17 @@ export default class EditableCell extends Component { isFocused: false, data: this.props.data }) + this.props.onStopEditing() + } + beginEditing () { + this.setState({isEditing: true}) } handleClick (evt) { - // if (this.state.isFocused) - this.setState({isEditing: true}) - let input = ReactDOM.findDOMNode(this.refs.cellInput) - if (input) { - input.select() - } - // else { - // this.setState({isFocused: true}) - // } + if (this.props.isFocused) { + this.beginEditing() + } else { + this.props.onClick() + } } handleDoubleClick (evt) { // this.setState({isEditing: true, isFocused: false}) @@ -66,8 +69,7 @@ export default class EditableCell extends Component { evt.preventDefault() this.props.onRowSelect(evt) break - } - else { + } else { return true } case 79: // o @@ -75,39 +77,37 @@ export default class EditableCell extends Component { evt.preventDefault() this.props.onRowSelect(evt) break - } - else { + } else { return true } case 222: // single quote - if (evt.shiftKey){ + if (evt.shiftKey) { console.log('dupe left') this.props.duplicateLeft(evt) this.props.onRight(evt) return false } + break case 13: // Enter evt.preventDefault() + if (this.props.isFocused) { + this.beginEditing() + } // handle shift - if (evt.shiftKey){ + if (evt.shiftKey) { this.save(evt) this.props.onUp(evt) - } - else { + } else { this.save(evt) - this.props.onDown(evt) } break case 9: // Tab evt.preventDefault() // handle shift - if (evt.shiftKey){ + if (evt.shiftKey) { this.save(evt) - this.props.onLeft(evt) - } - else { + } else { this.save(evt) - this.props.onRight(evt) } break case 27: // Esc @@ -116,28 +116,22 @@ export default class EditableCell extends Component { case 39: // right if (input.selectionStart === input.value.length) { this.save(evt) - this.props.onRight(evt) - break - } - else { + return false + } else { return true } case 37: // left - if (input.selectionStart === 0) { + if (input.selectionStart === 0 && input.selectionEnd === input.value.length) { this.save(evt) - this.props.onLeft(evt) - break - } - else { + return false + } else { return true } case 38: // up this.save(evt) - this.props.onUp(evt) break case 40: // down this.save(evt) - this.props.onDown(evt) break default: return true @@ -145,29 +139,25 @@ export default class EditableCell extends Component { } save () { if (!this.props.renderTime) { - console.log(this.state.data) if (this.state.data !== this.state.originalData) { - console.log('saving data... ' + this.state.data) this.setState({isEditing: false}) this.props.onChange(this.state.data) - } - else { + this.props.onStopEditing() + } else { this.cancel() } - } - else { - console.log('render time' + this.state.data) + } else { let date = moment().startOf('day').format('YYYY-MM-DD') let momentTime = moment(date + 'T' + this.state.data, ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a'], true) let value = momentTime.diff(date, 'seconds') + console.log(value) // check for valid time and new value if (momentTime.isValid() && value !== this.state.data) { - console.log('saving data... ' + value) this.setState({data: value, isEditing: false}) this.props.onChange(value) - } - else { + this.props.onStopEditing() + } else { this.cancel() } } @@ -175,8 +165,7 @@ export default class EditableCell extends Component { cellRenderer (value) { if (this.props.cellRenderer) { return this.props.cellRenderer(value) - } - else { + } else { return value } } @@ -185,74 +174,95 @@ export default class EditableCell extends Component { } handlePaste (evt) { let text = evt.clipboardData.getData('Text') - let rows = text.split(String.fromCharCode(13)); + let rows = text.split(String.fromCharCode(13)) for (var i = 0; i < rows.length; i++) { - rows[i] = rows[i].split(String.fromCharCode(9)) + rows[i] = rows[i].split(String.fromCharCode(9)) } - if (rows.length > 1 || rows[0].length > 1) { this.cancel() this.props.handlePastedRows(rows) evt.preventDefault() } } + _onInputFocus (evt) { + evt.target.select() + } render () { - var cellHtml - var cellStyle = { - fontWeight: this.state.edited ? 'bold' : 'normal' - } - if (this.state.isEditing) { - cellHtml = ( - this.handlePaste(evt)} - style={{ - width: '100%', - height: '100%' - }} - autoFocus='true' - onKeyDown={(evt) => this.handleKeyDown(evt)} - onChange={(evt) => this.handleChange(evt)} - onBlur={(evt) => this.cancel(evt)} - /> - ) - } - else { - cellHtml = -
    - {this.cellRenderer(this.state.data)} -
    - } - return ( - { - this.handleClick(evt) - }} - > - {cellHtml} - - ) + const rowCheckedColor = '#F3FAF6' + const focusedNotEditing = this.props.isFocused && !this.state.isEditing + const edgeDiff = this.props.isFocused ? 0 : 0.5 + const divStyle = { + height: '100%', + display: 'inline-block', + paddingTop: `${3 + edgeDiff}px`, + paddingLeft: `${3 + edgeDiff}px`, + UserSelect: 'none', + userSelect: 'none', + fontWeight: this.state.edited ? 'bold' : 'normal' + } + const cellStyle = { + backgroundColor: this.props.invalidData && !this.state.isEditing + ? 'pink' + : focusedNotEditing + ? '#F4F4F4' + : this.state.isEditing + ? '#fff' + : this.props.isSelected + ? rowCheckedColor + : '#fff', + border: this.props.invalidData && focusedNotEditing + ? '2px solid red' + : this.props.isFocused + ? `2px solid #66AFA2` + : '1px solid #ddd', + margin: `${-0.5 + edgeDiff}px`, + padding: `${-edgeDiff}px`, + whiteSpace: 'nowrap', + cursor: 'default', + fontWeight: '400', + // fontFamily: '"Courier New", Courier, monospace', + color: this.props.lightText ? '#aaa' : '#000', + ...this.props.style + } + const cellHtml = this.state.isEditing + ? this.handlePaste(evt)} + style={{ + width: '100%', + height: '100%', + outline: 'none', + margin: '1px', + marginLeft: '2px', + padding: '1px', + border: 0, + backgroundColor: 'rgba(0,0,0,0)' + }} + autoFocus='true' + onKeyDown={(evt) => this.handleKeyDown(evt)} + onChange={(evt) => this.handleChange(evt)} + onFocus={(evt) => this._onInputFocus(evt)} + onBlur={(evt) => this.cancel(evt)} + /> + :
    + {this.cellRenderer(this.state.data)} +
    + return ( +
    this.handleClick(evt)} + > + {cellHtml} +
    + ) } } diff --git a/src/main/client/editor/components/Timetable.js b/src/main/client/editor/components/Timetable.js new file mode 100644 index 000000000..d608b4650 --- /dev/null +++ b/src/main/client/editor/components/Timetable.js @@ -0,0 +1,463 @@ +import React, {Component, PropTypes} from 'react' +import { ArrowKeyStepper, Grid, AutoSizer, ScrollSync } from 'react-virtualized' +import ReactDOM from 'react-dom' +import moment from 'moment' +import update from 'react-addons-update' +import objectPath from 'object-path' +import styles from 'react-virtualized/styles.css' +import scrollbarSize from 'dom-helpers/util/scrollbarSize' + +import EditableCell from '../../common/components/EditableCell' +import Loading from '../../common/components/Loading' +import { isTimeFormat } from '../util' + +const DT_FORMATS = ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a'] + +export default class Timetable extends Component { + static propTypes = { + columns: PropTypes.array, + data: PropTypes.array + } + constructor (props) { + super(props) + this.state = { + activeCell: null, // 'rowNum-colNum', e.g. 0-1 + edited: [], + offsetSeconds: 0, + data: this.props.data, + columns: this.props.columns, + + // scrollsync + columnWidth: 30, + overscanColumnCount: 10, + overscanRowCount: 5, + rowHeight: 25, + scrollToRow: this.props.scrollToRow, + scrollToColumn: this.props.scrollToColumn + } + this._getColumnWidth = this._getColumnWidth.bind(this) + this._getColumnHeaderWidth = this._getColumnHeaderWidth.bind(this) + this._renderHeaderCell = this._renderHeaderCell.bind(this) + this._renderLeftHeaderCell = this._renderLeftHeaderCell.bind(this) + this._renderLeftColumnCell = this._renderLeftColumnCell.bind(this) + } + componentWillReceiveProps (nextProps) { + // handle scrolling to new location based on props + const { scrollToRow, scrollToColumn } = nextProps + if (scrollToRow !== this.props.scrollToRow || scrollToColumn !== this.props.scrollToColumn) { + this.setState({scrollToRow, scrollToColumn}) + } + } + shouldComponentUpdate (nextProps) { + return true + } + parseTime (timeString) { + const date = moment().startOf('day').format('YYYY-MM-DD') + return moment(date + 'T' + timeString, DT_FORMATS).diff(date, 'seconds') + } + handlePastedRows (pastedRows, rowIndex, colIndex) { + let newRows = [...this.props.data] + let editedRows = [] + + // iterate over rows in pasted selection + for (var i = 0; i < pastedRows.length; i++) { + editedRows.push(i) + if (typeof this.props.data[i + rowIndex] === 'undefined') { + console.log('adding row' + i + rowIndex) + this.props.addNewRow() + } + // iterate over number of this.props.columns in pasted selection + for (var j = 0; j < pastedRows[0].length; j++) { + let path = `${rowIndex + i}.${this.props.columns[colIndex + j].key}` + + // construct new row if it doesn't exist + if (typeof newRows[i + rowIndex] === 'undefined' || typeof objectPath.get(newRows, path) === 'undefined') { + // newRows.push(this.props.constructNewRow()) + // this.props.addNewRow() + } + let value = this.parseTime(pastedRows[i][j]) + // objectPath.set(newRows, path, value) + this.props.updateCellValue(value, rowIndex + i, path) + // if departure times are hidden, paste into adjacent time column + let adjacentPath = `${rowIndex + i}.${this.props.columns[colIndex + j + 2].key}` + if (this.props.timetable.hideDepartureTimes && isTimeFormat(this.props.columns[colIndex + j].type) && typeof objectPath.get(newRows, adjacentPath) !== 'undefined') { + // objectPath.set(newRows, adjacentPath, value) + this.props.updateCellValue(value, rowIndex + i, adjacentPath) + } + } + } + let stateUpdate = {activeCell: {$set: `${rowIndex}-${colIndex}`}, data: {$set: newRows}, edited: { $push: editedRows }} + this.setState(update(this.state, stateUpdate)) + } + _getColumnWidth ({ index }) { + const col = this.props.columns[index] + const width = col ? col.width : 90 + return width + } + _getColumnHeaderWidth ({ index }) { + const col = this.getHeaderColumns()[index] + const width = col.type === 'ARRIVAL_TIME' && !this.props.timetable.hideDepartureTimes + ? col.width * 2 + : col + ? col.width + : 90 + return width + } + handleCellClick (rowIndex, columnIndex) { + this.setState({scrollToColumn: columnIndex, scrollToRow: rowIndex}) + } + _cellRenderer ({ columnIndex, key, rowIndex, scrollToColumn, scrollToRow, style }) { + const isFocused = columnIndex === scrollToColumn && rowIndex === scrollToRow + const isEditing = this.state.activeCell === `${rowIndex}-${columnIndex}` + const col = this.props.columns[columnIndex] + const previousCol = this.props.columns[columnIndex - 1] + const row = this.props.data[rowIndex] + + let rowIsChecked = this.props.selected[0] === '*' && + this.props.selected.indexOf(rowIndex) === -1 || this.props.selected[0] !== '*' && + this.props.selected.indexOf(rowIndex) !== -1 + + let val = objectPath.get(this.props.data[rowIndex], col.key) + if (col.key === 'gtfsTripId' && val === null) { + val = objectPath.get(this.props.data[rowIndex], 'id') !== 'new' ? objectPath.get(this.props.data[rowIndex], 'id') : null + } + let previousValue = previousCol && row && objectPath.get(row, previousCol.key) + const isInvalid = isTimeFormat(col.type) && val >= 0 && val < previousValue + return ( + this.handleCellClick(rowIndex, columnIndex)} + duplicateLeft={(evt) => this.props.updateCellValue(previousValue, rowIndex, `${rowIndex}.${col.key}`)} + handlePastedRows={(rows) => this.handlePastedRows(rows, rowIndex, columnIndex, this.props.columns)} + invalidData={isInvalid} + isEditing={isEditing} + isSelected={rowIsChecked} + isFocused={isFocused} + lightText={col.type === 'DEPARTURE_TIME'} + placeholder={col.placeholder} + renderTime={isTimeFormat(col.type)} + cellRenderer={(value) => this.getCellRenderer(col, value)} + data={val} + style={style} + onStopEditing={() => this.handleEndEditing()} + onChange={(value) => { + this.props.updateCellValue(value, rowIndex, `${rowIndex}.${col.key}`) + // this.setState({activeCell: null}) + + // TODO: add below back in + // // set departure time value if departure times are hidden + // if (this.props.timetable.hideDepartureTimes && this.props.timetable.columns[colIndex + 1] && this.props.timetable.columns[colIndex + 1].type === 'DEPARTURE_TIME') { + // this.updateCellValue(value, rowIndex, `${rowIndex}.${this.props.timetable.columns[columnIndex + 1].key}`) + // } + }} + /> + ) + } + getHeaderColumns () { + return this.props.columns.filter(c => c.type !== 'DEPARTURE_TIME') + } + handleEndEditing () { + this.setState({activeCell: null}) + // refocus on grid after editing is done + ReactDOM.findDOMNode(this.grid).focus() + } + _renderHeaderCell ({ columnIndex, key, rowIndex, style }) { + const col = this.getHeaderColumns()[columnIndex] + return ( + 0 && this.props.selected.length === this.props.data.length} // render column headers as active if all rows selected + title={col.title ? col.title : col.name} + label={col.name} + style={{ + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + ...style + }} + /> + ) + } + _renderLeftHeaderCell ({ columnIndex, key, rowIndex, style }) { + // Select all checkbox + return ( + this.props.toggleAllRows(value)} + style={style} + selectable + /> + ) + } + _renderLeftColumnCell ({ columnIndex, key, rowIndex, style }) { + let rowSelected = this.props.selected.indexOf(rowIndex) !== -1 + // Select row checkbox + return ( + this.props.toggleRowSelection(rowIndex)} + style={style} + selectable + /> + ) + } + handleKeyPress (evt, scrollToColumn, scrollToRow) { + switch (evt.keyCode) { + case 13: // Enter + if (!this.state.activeCell) { + this.setState({activeCell: `${scrollToRow}-${scrollToColumn}`}) + } else { + this.setState({activeCell: null}) + } + break + case 67: + // handle copy + if (evt.ctrlKey) { + console.log('copy pasta') + } + return false + default: + break + } + // input was 0-9 + if (!this.state.activeCell && evt.keyCode >= 48 && evt.keyCode <= 57 && !evt.ctrlKey) { + this.setState({activeCell: `${scrollToRow}-${scrollToColumn}`}) + } + // input was a-z + if (!this.state.activeCell && evt.keyCode >= 65 && evt.keyCode <= 90 && !evt.ctrlKey) { + this.setState({activeCell: `${scrollToRow}-${scrollToColumn}`}) + } + } + getCellRenderer (col, value) { + if (!isTimeFormat(col.type)) { + return value + } else { + if (value === 0) { + return moment().startOf('day').seconds(value).format('HH:mm:ss') + } else if (value && value > 0) { + return moment().startOf('day').seconds(value).format('HH:mm:ss') + } else { + return '' + } + } + } + render () { + // console.log(this.props, this.state) + if (this.props.columns.length === 0 && this.props.data.length === 0) { + return ( +
    + +
    + ) + } else if (this.props.data.length === 0) { + return ( + + ) + } + const { + columnWidth, + overscanColumnCount, + overscanRowCount, + rowHeight + } = this.state + const columnHeaderCount = this.getHeaderColumns().length + return ( + + {({ clientHeight, clientWidth, onScroll, scrollHeight, scrollLeft, scrollTop, scrollWidth }) => { + return ( +
    + + {({ onSectionRendered, scrollToColumn, scrollToRow }) => ( +
    this.handleKeyPress(evt, scrollToColumn, scrollToRow)} + > +
    + {/* Top Left Cell */} + +
    +
    + + {({ width, height }) => { + return ( +
    +
    + {/* Left Side Column */} + +
    +
    + {/* Top Header Row */} + +
    +
    + {/* Primary timetable grid */} + { this.grid = Grid }} + style={{ + outline: 'none' + }} + columnWidth={this._getColumnWidth} + columnCount={this.props.columns.length} + height={height} + onScroll={onScroll} + overscanColumnCount={overscanColumnCount} + overscanRowCount={overscanRowCount} + onSectionRendered={onSectionRendered} + cellRenderer={({ columnIndex, key, rowIndex, style }) => this._cellRenderer({ columnIndex, key, rowIndex, scrollToColumn, scrollToRow, style })} + rowHeight={rowHeight} + rowCount={this.props.data.length} + scrollToColumn={scrollToColumn} + scrollToRow={scrollToRow} + width={width - scrollbarSize() - columnWidth} + /> +
    +
    + ) }} +
    +
    +
    + )} +
    +
    + ) + }} +
    + ) + } +} + +class HeaderCell extends Component { + constructor (props) { + super(props) + this.state = { + active: this.props.active + } + } + componentWillReceiveProps (nextProps) { + const { active } = nextProps + if (this.props.active !== active) { + this.setState({active}) + } + } + _handleClick () { + if (this.props.selectable) { + this.setState({active: !this.state.active}) + this.props.onChange(!this.state.active) + } + } + render () { + const edgeDiff = 0.5 + const style = { + backgroundColor: this.state.active ? '#A8D4BB' : '#eee', + border: '1px solid #ddd', + margin: `${-0.5 + edgeDiff}px`, + padding: `${-edgeDiff}px`, + UserSelect: 'none', + userSelect: 'none', + paddingTop: '6px', + cursor: this.props.selectable ? 'pointer' : 'default', + ...this.props.style + } + return ( +
    this._handleClick()} + > + {this.props.label} +
    + ) + } +} diff --git a/src/main/client/editor/components/TimetableEditor.js b/src/main/client/editor/components/TimetableEditor.js index f46833c65..979359e85 100644 --- a/src/main/client/editor/components/TimetableEditor.js +++ b/src/main/client/editor/components/TimetableEditor.js @@ -1,17 +1,11 @@ import React, {Component, PropTypes} from 'react' -import { InputGroup, Checkbox, Nav, NavItem, NavDropdown, MenuItem, Button, Form, FormControl } from 'react-bootstrap' -import Icon from 'react-fa' import clone from 'clone' -import ReactDOM from 'react-dom' -import moment from 'moment' -import truncate from 'truncate' import update from 'react-addons-update' import objectPath from 'object-path' -import EditableCell from '../../common/components/EditableCell' -import HourMinuteInput from './HourMinuteInput' - -const DT_FORMATS = ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a'] +import { isTimeFormat } from '../util' +import TimetableHeader from './TimetableHeader' +import Timetable from './Timetable' export default class TimetableEditor extends Component { static propTypes = { @@ -32,38 +26,19 @@ export default class TimetableEditor extends Component { offsetSeconds: 0 } } - updateDimensions () { + _onResize = () => { this.setState({width: window.innerWidth, height: window.innerHeight}) } componentWillMount () { - this.updateDimensions() + this._onResize() } componentDidMount () { - window.addEventListener('resize', () => this.updateDimensions()) + window.addEventListener('resize', this._onResize) } componentWillUnmount () { - window.removeEventListener('resize', () => this.updateDimensions()) - } - toggleRowSelection (rowIndex) { - let selectIndex = this.state.selected.indexOf(rowIndex) - console.log(selectIndex) - if (selectIndex === -1) { - let stateUpdate = { selected: { $push: [rowIndex] } } - this.setState(update(this.state, stateUpdate)) - } - else { - let stateUpdate = { selected: { $splice: [[selectIndex, 1]] } } - this.setState(update(this.state, stateUpdate)) - } + window.removeEventListener('resize', this._onResize) } - setCellValue (value, rowIndex, key) { - let newRows = [...this.state.data] - objectPath.set(newRows, key, value) - // newRows[rowIndex][col.key] = value - let stateUpdate = { edited: { $push: [rowIndex] }, data: {$set: newRows} } - this.setState(update(this.state, stateUpdate)) - } - constructNewRow (columns, toClone = null) { + constructNewRow (toClone = null) { const activePattern = this.props.route && this.props.route.tripPatterns ? this.props.route.tripPatterns.find(p => p.id === this.props.activePatternId) : null let newRow = toClone ? clone(toClone) || {} : {} @@ -83,9 +58,9 @@ export default class TimetableEditor extends Component { cumulativeTravelTime += +stop.defaultDwellTime objectPath.set(newRow, `stopTimes.${i}.departureTime`, cumulativeTravelTime) } - for (let i = 0; i < columns.length; i++) { - let col = columns[i] - if (this.isTimeFormat(col.type)) { + for (let i = 0; i < this.props.timetable.columns.length; i++) { + let col = this.props.timetable.columns[i] + if (isTimeFormat(col.type)) { // TODO: add default travel/dwell times to new rows // objectPath.ensureExists(newRow, col.key, 0) } else { @@ -102,34 +77,35 @@ export default class TimetableEditor extends Component { return newRow } - addNewRow (columns, blank = false) { + addNewRow (blank = false, scroll = false) { // set blank to true if there are no rows to clone - blank = blank || this.state.data.length === 0 + blank = blank || this.props.timetable.trips.length === 0 - let clone = blank ? null : this.state.data[this.state.data.length - 1] - let newRow = this.constructNewRow(columns, clone) + let clone = blank ? null : this.props.timetable.trips[this.props.timetable.trips.length - 1] + let newRow = this.constructNewRow(clone) - let newRows = [...this.state.data, newRow] let stateUpdate = { - data: {$set: newRows}, - activeCell: {$set: `${newRows.length - 1}-${0}`}, - edited: { $push: [this.state.data.length] } + activeCell: {$set: null}, + scrollToRow: {$set: this.props.timetable.trips.length}, + scrollToColumn: {$set: 0} + } + this.props.addNewTrip(newRow) + if (scroll) { + this.setState(update(this.state, stateUpdate)) } - this.setState(update(this.state, stateUpdate)) } - removeSelectedRows (feedSourceId, pattern, scheduleId) { + removeSelectedRows () { let splice = [] - let removed = [] let tripsToDelete = [] - let newRows = [...this.state.data] - let selectedDescending = this.state.selected.sort((a,b) => { + let newRows = [...this.props.timetable.trips] + let selectedDescending = this.props.timetable.selected.sort((a, b) => { return b - a }) // loop over selected array in descending order to ensure that splice operates on indexes in reverse for (var i = 0; i < selectedDescending.length; i++) { let rowIndex = selectedDescending[i] - // removed.push([this.state.selected[i], 1]) + // removed.push([this.props.selected[i], 1]) let row = newRows[rowIndex] if (row.id === 'new') { splice.push([rowIndex, 1]) @@ -138,109 +114,44 @@ export default class TimetableEditor extends Component { } } if (tripsToDelete.length > 0) { - this.props.deleteTripsForCalendar(feedSourceId, pattern, scheduleId, tripsToDelete) - } - console.log(splice) - let stateUpdate = { - data: {$splice: splice}, - selected: {$set: []} + this.props.deleteTripsForCalendar(this.props.feedSource.id, this.props.activePattern, this.props.activeScheduleId, tripsToDelete) } - this.setState(update(this.state, stateUpdate)) + this.props.toggleAllRows(false) } componentWillReceiveProps (nextProps) { - console.log('receiving props') const activePattern = nextProps.route && nextProps.route.tripPatterns ? nextProps.route.tripPatterns.find(p => p.id === nextProps.activePatternId) : null const activeSchedule = nextProps.tableData.calendar ? nextProps.tableData.calendar.find(c => c.id === nextProps.activeScheduleId) : null const trips = activePattern && activeSchedule ? activePattern[nextProps.activeScheduleId] : [] // add unsaved trips to list of trips received - if (this.state.edited.length > 0) { - for (var i = 0; i < this.state.edited.length; i++) { - let rowIndex = this.state.edited[i] - let trip = this.state.data[rowIndex] + if (nextProps.timetable.edited.length > 0) { + console.log('changes found', nextProps.timetable.edited.length) + for (var i = 0; i < nextProps.timetable.edited.length; i++) { + let rowIndex = nextProps.timetable.edited[i] + let trip = nextProps.timetable.trips[rowIndex] if (trip) { trips.push(trip) } } } - const sortedTrips = trips ? trips - .filter(t => t.useFrequency === activePattern.useFrequency) // filter out based on useFrequency - .sort((a, b) => { - // if(a.isCreating && !b.isCreating) return -1 - // if(!a.isCreating && b.isCreating) return 1 - if(a.stopTimes[0].departureTime < b.stopTimes[0].departureTime) return -1 - if(a.stopTimes[0].departureTime > b.stopTimes[0].departureTime) return 1 - return 0 - }) : [] - // console.log(activeSchedule) - const calendars = nextProps.tableData.calendar - this.setState({ - data: sortedTrips, // tripRows - // edited: [], - hideDepartureTimes: false - }) } shouldComponentUpdate (nextProps) { return true } - // rowGetter (rowIdx) { - // return this.state ? this.state.rows[rowIdx] : {block: 0, gtfsTripId: 'trp', tripHeadsign: 'trip'} - // } - // handleRowUpdated (e) { - // //merge updated row with current row and rerender by setting state - // var rows = this.state.rows; - // Object.assign(rows[e.rowIdx], e.updated); - // this.setState({rows:rows}); - // } - _onDown (evt, rowIndex, colIndex) { - if (rowIndex + 1 <= this.state.data.length - 1) { - this.setState({activeCell: `${rowIndex + 1}-${colIndex}`}) - return true - } - else { - return false - } - } - _onUp (evt, rowIndex, colIndex) { - if (rowIndex - 1 >= 0) { - this.setState({activeCell: `${rowIndex - 1}-${colIndex}`}) - return true - } - else { - return false - } - } - _onRight (evt, rowIndex, colIndex, columns) { - if (colIndex + 1 <= columns.length - 1) { - this.setState({activeCell: `${rowIndex}-${colIndex + 1}`}) - return true - } - else { - return false - } - } - _onLeft (evt, rowIndex, colIndex) { - if (colIndex - 1 >= 0) { - this.setState({activeCell: `${rowIndex}-${colIndex - 1}`}) - return true - } - else { - return false - } - } - offsetRows (rowIndexes, offsetAmount, columns) { - let newRows = [...this.state.data] + offsetRows (rowIndexes, offsetAmount) { + let newRows = [...this.props.timetable.trips] let editedRows = [] console.log(`Offsetting ${rowIndexes.length} rows by ${offsetAmount} seconds`) for (var i = 0; i < rowIndexes.length; i++) { let row = newRows[rowIndexes[i]] editedRows.push(rowIndexes[i]) - for (var j = 0; j < columns.length; j++) { - let col = columns[j] + for (var j = 0; j < this.props.timetable.columns.length; j++) { + let col = this.props.timetable.columns[j] let path = `${rowIndexes[i]}.${col.key}` - if (this.isTimeFormat(col.type)) { + if (isTimeFormat(col.type)) { let currentVal = objectPath.get(newRows, path) - let newVal = currentVal + offsetAmount % 86399 // ensure seconds does not exceed 24 hours - objectPath.set(newRows, path, newVal) + let value = currentVal + offsetAmount % 86399 // ensure seconds does not exceed 24 hours + objectPath.set(newRows, path, value) + // this.props.updateCellValue(value, i, path) } } } @@ -250,470 +161,102 @@ export default class TimetableEditor extends Component { } this.setState(update(this.state, stateUpdate)) } - parseTime (timeString) { - const date = moment().startOf('day').format('YYYY-MM-DD') - return moment(date + 'T' + timeString, DT_FORMATS).diff(date, 'seconds') - } - handlePastedRows (pastedRows, rowIndex, colIndex, columns) { - let newRows = [...this.state.data] - let editedRows = [] - - // iterate over rows in pasted selection - for (var i = 0; i < pastedRows.length; i++) { - editedRows.push(i) - - // iterate over number of columns in pasted selection - for (var j = 0; j < pastedRows[0].length; j++) { - let path = `${rowIndex + i}.${columns[colIndex + j].key}` - - // construct new row if it doesn't exist - if (typeof newRows[i + rowIndex] === 'undefined' || typeof objectPath.get(newRows, path) === 'undefined') { - newRows.push(this.constructNewRow(columns)) - } - let newValue = this.parseTime(pastedRows[i][j]) - objectPath.set(newRows, path, newValue) - - // if departure times are hidden, paste into adjacent time column - let adjacentPath = `${rowIndex + i}.${columns[colIndex + j + 2].key}` - if (this.state.hideDepartureTimes && this.isTimeFormat(columns[colIndex + j].type) && typeof objectPath.get(newRows, adjacentPath) !== 'undefined') { - objectPath.set(newRows, adjacentPath, newValue) - } - } - } - let stateUpdate = {activeCell: {$set: `${rowIndex}-${colIndex}`}, data: {$set: newRows}, edited: { $push: editedRows }} - this.setState(update(this.state, stateUpdate)) - } - saveEditedTrips (activePattern, activeScheduleId) { + saveEditedTrips (pattern, activeScheduleId) { let trips = [] let tripIndexes = [] - for (var i = 0; i < this.state.edited.length; i++) { - let rowIndex = this.state.edited[i] + for (var i = 0; i < this.props.timetable.edited.length; i++) { + let rowIndex = this.props.timetable.edited[i] if (tripIndexes.indexOf(rowIndex) === -1) { - let trip = this.state.data[rowIndex] + let trip = this.props.timetable.trips[rowIndex] if (trip) { trips.push(trip) tripIndexes.push(rowIndex) } } } - - this.props.saveTripsForCalendar(this.props.feedSource.id, activePattern, activeScheduleId, trips) - .then((errorIndexes) => { - console.log(errorIndexes) - let edited = [] - for (var i = 0; i < errorIndexes.length; i++) { - edited.push(this.state.edited[errorIndexes[i]]) - } - let stateUpdate = { - edited: {$set: edited} - } - this.setState(update(this.state, stateUpdate)) - }) + this.props.saveTripsForCalendar(this.props.feedSource.id, pattern, activeScheduleId, trips) + .then((errorIndexes) => { + console.log('errors for trips', errorIndexes) + let edited = [] + for (var i = 0; i < errorIndexes.length; i++) { + edited.push(this.props.timetable.edited[errorIndexes[i]]) + } + console.log(edited) + let stateUpdate = { + edited: {$set: edited} + } + this.setState(update(this.state, stateUpdate)) + }) } - isTimeFormat (type) { - return /TIME/.test(type) + _activePattern () { + } isDataValid (col, value, previousValue) { - if (this.isTimeFormat(col.type)) { + if (isTimeFormat(col.type)) { return value && value >= 0 && value < previousValue } else { return true } } - getCellRenderer (col, value) { - if (!this.isTimeFormat(col.type)) { - return value - } - else { - if (value === 0) { - return moment().startOf('day').seconds(value).format('HH:mm:ss') - } - else if (value && value > 0) - return moment().startOf('day').seconds(value).format('HH:mm:ss') - else { - return '' - } - } - } render () { - console.log(this.state) - const { feedSource, route, activePatternId, tableData, activeScheduleId } = this.props - // if (!feedSource) { - // return null - // } - const activePattern = route && route.tripPatterns ? route.tripPatterns.find(p => p.id === activePatternId) : null - const activeSchedule = tableData.calendar ? tableData.calendar.find(c => c.id === activeScheduleId) : null - const trips = activePattern && activeSchedule ? activePattern[activeScheduleId] : [] + const { feedSource, activePattern, activeSchedule } = this.props - // console.log(activeSchedule) - const calendars = tableData.calendar const panelStyle = { - width: '100%', - height: '100%', backgroundColor: 'white', - overflowX: 'scroll', paddingRight: '5px', - paddingLeft: '5px', - } - - const headerWidth = this.state.width - (this.props.sidebarExpanded ? 130 : 50) - const headerStyle = { - position: 'fixed', - zIndex: 1000, - backgroundColor: 'white', - width: `${headerWidth}px`, - paddingRight: '10px' + paddingLeft: '5px' } - const columns = [ - { - name: 'Block ID', - width: 30, - key: 'blockId', - type: 'TEXT', - placeholder: '300' - }, - { - name: 'Trip ID', - width: 300, - key: 'gtfsTripId', - type: 'TEXT', - placeholder: '12345678' - }, - { - name: 'Trip Headsign', - width: 300, - key: 'tripHeadsign', - type: 'TEXT', - placeholder: 'Destination via Transfer Center' - }, - ] - if (activePattern && activePattern.patternStops) { - if (!activePattern.useFrequency) { - activePattern.patternStops.map((ps, index) => { - let stop = tableData.stop ? tableData.stop.find(st => st.id === ps.stopId) : null - let stopName = stop ? stop.stop_name : ps.stopId - columns.push({ - name: stopName && stopName.length > 15 ? stopName.substr(0, 15) + '...' : stopName, - title: stopName, - width: 100, - key: `stopTimes.${index}.arrivalTime`, - colSpan: '2', - hidden: false, - type: 'ARRIVAL_TIME', - placeholder: 'HH:MM:SS' - }) - columns.push({ - key: `stopTimes.${index}.departureTime`, - hidden: this.state.hideDepartureTimes, - type: 'DEPARTURE_TIME', - placeholder: 'HH:MM:SS' - }) - }) - } - // columns added if using freqency schedule type - else { - columns.push({ - name: 'Start time', - width: 100, - key: 'startTime', - type: 'TIME', - placeholder: 'HH:MM:SS' - }) - columns.push({ - name: 'End time', - width: 100, - key: 'endTime', - type: 'TIME', - placeholder: 'HH:MM:SS' - }) - columns.push({ - name: 'Headway', - width: 60, - key: 'headway', - type: 'MINUTES', - placeholder: '15 (min)' - }) - } - } - const tableType = !activePattern - ? '' - : activePattern.useFrequency - ? 'Frequencies for' - : 'Timetables for' - const headerText = {tableType} {activePattern ? {truncate(activePattern.name, 20)} : } + const HEADER_HEIGHT = 118 return (
    -
    -

    -
    - {activePattern && !activePattern.useFrequency - ? { - this.setState({hideDepartureTimes: !this.state.hideDepartureTimes}) - }} - > - Hide departure times - - : null - } - {' '} - - { - this.setState({offsetSeconds: seconds}) - }} - /> - - - - - {' '} - - {' '} - - {' '} - - {' '} - - {' '} - -
    - - {' '} - {headerText} -

    - -
    -
    + this.removeSelectedRows()} + offsetRows={(rowIndexes, offsetAmount) => this.offsetRows(rowIndexes, offsetAmount)} + hideDepartureTimes={this.props.timetable.hideDepartureTimes} + addNewRow={(blank, scroll) => this.addNewRow(blank, scroll)} + edited={this.props.timetable.edited} + data={this.props.timetable.trips} + saveEditedTrips={(pattern, scheduleId) => this.saveEditedTrips(pattern, scheduleId)} + {...this.props} + /> {activeSchedule - ? - - - - {columns.map(c => { - if (!c.name) return null - return ( - - ) - })} - - - - {this.state.data - ? this.state.data.map((row, rowIndex) => { - let rowValues = [] - let rowCheckedColor = '#F3FAF6' - let rowIsChecked = this.state.selected[0] === '*' && this.state.selected.indexOf(rowIndex) === -1 || this.state.selected[0] !== '*' && this.state.selected.indexOf(rowIndex) !== -1 - return ( - - - {columns.map((col, colIndex) => { - let val = objectPath.get(row, col.key) - if (col.key === 'gtfsTripId' && val === null) { - val = objectPath.get(row, 'id') !== 'new' ? objectPath.get(row, 'id') : null - } - rowValues.push(val) - let cellStyle = { - width: '60px', - color: col.type === 'DEPARTURE_TIME' ? '#aaa' : '#000' - } - if (rowIsChecked) { - cellStyle.backgroundColor = rowCheckedColor - } - let previousValue = rowValues[colIndex - 1] - - // if departure times are hidden do not display cell - if (col.hidden) return null - - return ( - { - this.setCellValue(value, rowIndex, `${rowIndex}.${col.key}`) - - // set departure time value if departure times are hidden - if (this.state.hideDepartureTimes && columns[colIndex + 1] && columns[colIndex + 1].type === 'DEPARTURE_TIME') { - this.setCellValue(value, rowIndex, `${rowIndex}.${columns[colIndex + 1].key}`) - } - }} - key={`cell-${rowIndex}-${colIndex}`} - onRowSelect={(evt) => this.toggleRowSelection(rowIndex)} - onLeft={(evt) => this._onLeft(evt, rowIndex, colIndex)} - onRight={(evt) => this._onRight(evt, rowIndex, colIndex, columns)} - onUp={(evt) => this._onUp(evt, rowIndex, colIndex)} - onDown={(evt) => this._onDown(evt, rowIndex, colIndex)} - duplicateLeft={(evt) => this.setCellValue(previousValue, rowIndex, `${rowIndex}.${col.key}`)} - handlePastedRows={(rows) => this.handlePastedRows(rows, rowIndex, colIndex, columns)} - invalidData={this.isTimeFormat(col.type) && val >= 0 && val < previousValue} - isEditing={this.state.activeCell === `${rowIndex}-${colIndex}` } - isFocused={false} - placeholder={col.placeholder} - renderTime={this.isTimeFormat(col.type)} - cellRenderer={(value) => this.getCellRenderer(col, value)} - data={val} - style={cellStyle} - /> - ) - })} - - ) - }) - : null - } - -
    - {/* Select all checkbox */} - { - let selected = [] - if (this.state.selected.length !== this.state.data.length) { - for (let i = 0; i < this.state.data.length; i++) { - selected.push(i) - } - } - this.setState({selected}) - }} - /> - - {c.name} -
    - { - this.toggleRowSelection(rowIndex) - }} - /> -
    + ? this.constructNewRow(clone)} + addNewRow={(blank, scroll) => this.addNewRow(blank, scroll)} + toggleRowSelection={(rowIndex) => this.props.toggleRowSelection(rowIndex)} + toggleAllRows={(select) => this.props.toggleAllRows(select)} + selected={this.props.timetable.selected} + scrollToRow={this.state.scrollToRow} + scrollToColumn={this.state.scrollToColumn} + activePattern={activePattern} + data={this.props.timetable.trips} + columns={this.props.timetable.columns} + {...this.props} + /> :

    - Choose a calendar to edit timetables or - {' '} - { - e.preventDefault() - this.props.setActiveEntity(feedSource.id, 'calendar', {id: 'new'}) - }} - >create a new one. -

    + {activePattern + ? + Choose a calendar to edit timetables or + {' '} + { + e.preventDefault() + this.props.setActiveEntity(feedSource.id, 'calendar', {id: 'new'}) + }} + >create a new one. + + : Choose a trip pattern. + } +

    } -
    {/* End timetable body */}
    ) } diff --git a/src/main/client/editor/components/TimetableHeader.js b/src/main/client/editor/components/TimetableHeader.js new file mode 100644 index 000000000..2085e64da --- /dev/null +++ b/src/main/client/editor/components/TimetableHeader.js @@ -0,0 +1,318 @@ +import React, {Component, PropTypes} from 'react' +import { InputGroup, Col, Row, Checkbox, Label, Button, Form, OverlayTrigger, Tooltip, ButtonGroup } from 'react-bootstrap' +import Icon from 'react-fa' +import truncate from 'truncate' +import Select from 'react-select' + +import HourMinuteInput from './HourMinuteInput' +import { getEntityName } from '../util/gtfs' + +export default class TimetableHeader extends Component { + static propTypes = { + feedSource: PropTypes.object + } + constructor (props) { + super(props) + this.state = { + selected: this.props.selected, + edited: this.props.edited + } + } + render () { + const { feedSource, route, tableData, activeScheduleId, activePattern, setActiveEntity, fetchTripsForCalendar } = this.props + const calendars = tableData.calendar || [] + const activeCalendar = calendars.find(c => c.id === activeScheduleId) + const headerStyle = { + backgroundColor: 'white' + } + const tableType = activePattern && activePattern.useFrequency + ? 'Frequency editor' + : 'Timetable editor' + const patternName = activePattern && activePattern.name + const calendarName = activeCalendar && activeCalendar.service_id + return ( +
    + + +

    + Back to route}> + + + {tableType} +

    + + +

    + {this.props.data.length} trips for {truncate(patternName, 15)} on {truncate(calendarName, 13)} calendar +

    + + +
    + {activePattern && !activePattern.useFrequency + ? { + this.props.toggleDepartureTimes() + }} + > + Hide departures + + : null + } + {' '} + + { + this.props.setOffset(seconds) + }} + /> + + + + +
    + +
    + + + + + + + + + + + + + Add new trip}> + + + Duplicate trips}> + + + Delete trips}> + + + Undo changes}> + + + Save changes}> + + + + + +
    + ) + } +} + +class RouteSelect extends Component { + _render = (option) => { + return ( + + {truncate(option.label, 23)} + {' '} + + + ) + } + render () { + const { route, routes, feedSource, setActiveEntity } = this.props + return ( + Select pattern...} + options={patterns.map(pattern => ({value: pattern.id, label: `${getEntityName('pattern', pattern)}` || '[Unnamed]', pattern}))} + onChange={(value) => { + const pattern = value && value.pattern || {id: 'new'} + setActiveEntity(feedSource.id, 'route', route, 'trippattern', pattern, 'timetable', null) + }} + /> + ) + } +} + +class CalendarSelect extends Component { + _render = (option) => { + const patternTrips = this.props.activePattern && this.props.activePattern.tripCountByCalendar[option.value] || 0 + const routeCount = Object.keys(option.calendar.routes).length + return ( + + {option.label} + {' '} + + {' '} + + {' '} + + + ) + } + render () { + const { activePattern, route, feedSource, activeCalendar, calendars, setActiveEntity, trips } = this.props + const options = calendars && route + ? calendars.sort((a, b) => { + if (route.id in a.routes && !(route.id in b.routes)) return -1 + else if (route.id in b.routes && !(route.id in a.routes)) return 1 + else return b.numberOfTrips - a.numberOfTrips + }).map(calendar => { + return { + label: calendar.description, + value: calendar.id, + service_id: calendar.service_id, + calendar, + totalTrips: calendar.numberOfTrips, + routeTrips: calendar.routes[route.id] || 0, + calendarTrips: trips.length + } + }) + : [] + return ( + Select calendar...} + placeholder={ Select calendar...} valueRenderer={this._render} optionRenderer={this._render} disabled={!activePattern || activePattern.id === 'new'} From 9b0af4186556da884d9be634afffdc4cf316b56e Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 16 Nov 2016 10:16:58 +0800 Subject: [PATCH 137/323] fix(html): Fix index.html to load assets with absolute paths. --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index fe8bbce99..3acf1d168 100644 --- a/index.html +++ b/index.html @@ -6,10 +6,10 @@ Catalogue - +
    - + From 04fd252619453acb29c7d7ccadbf4573c84df70d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 16 Nov 2016 13:29:35 -0500 Subject: [PATCH 138/323] final fixes for mastarmify --- .../client/common/components/JobMonitor.js | 16 +++++++++++++--- src/main/client/common/components/Sidebar.js | 15 +++++++++++---- .../common/components/SidebarNavItem.js | 14 +++++--------- .../common/components/SidebarPopover.js | 6 +----- .../client/common/containers/ActiveSidebar.js | 5 +++-- .../client/editor/components/CalendarList.js | 2 +- .../components/EditorFeedSourcePanel.js | 8 ++++---- .../editor/components/TripPatternList.js | 4 ++-- src/main/client/gtfs/components/GtfsMap.js | 2 +- src/main/client/manager/actions/status.js | 9 +++++++-- .../manager/components/FeedSourceTable.js | 14 +++++++------- .../manager/components/FeedSourceViewer.js | 18 +++++++++--------- .../components/FeedVersionNavigator.js | 2 +- .../manager/components/FeedVersionReport.js | 19 +++++++++++-------- .../manager/components/ProjectViewer.js | 2 +- .../client/manager/components/UserHomePage.js | 2 +- .../validation/GtfsValidationViewer.js | 4 ++-- src/main/client/manager/reducers/status.js | 6 ++---- .../client/public/components/UserAccount.js | 4 ++-- 19 files changed, 84 insertions(+), 68 deletions(-) diff --git a/src/main/client/common/components/JobMonitor.js b/src/main/client/common/components/JobMonitor.js index 82297752d..744278163 100644 --- a/src/main/client/common/components/JobMonitor.js +++ b/src/main/client/common/components/JobMonitor.js @@ -14,7 +14,17 @@ export default class JobMonitor extends Pure { close: PropTypes.func, removeRetiredJob: PropTypes.func } - + removeJob (job) { + this.props.removeRetiredJob(job) + } + componentWillReceiveProps (nextProps) { + // TODO: fix resizing when jobs are removed (height of popover appears to be incorrect) + // if (nextProps.jobMonitor.retired.length !== this.props.jobMonitor.retired.length) { + // this.popover._onResize() + // } else if (nextProps.jobMonitor.jobs.length !== this.props.jobMonitor.jobs.length) { + // this.popover._onResize() + // } + } render () { const jobContainerStyle = { marginBottom: 20 @@ -31,7 +41,7 @@ export default class JobMonitor extends Pure { return ( this.popover = SidebarPopover} title='Server Jobs' {...this.props}> {this.props.jobMonitor.retired.map(job => { @@ -48,7 +58,7 @@ export default class JobMonitor extends Pure { diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index d9a8ab4f9..94602b98a 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -18,7 +18,8 @@ export default class Sidebar extends Pure { resetPassword: PropTypes.func, setJobMonitorVisible: PropTypes.func, removeRetiredJob: PropTypes.func, - setSidebarExpanded: PropTypes.func + setSidebarExpanded: PropTypes.func, + startJobMonitor: PropTypes.func } state = { @@ -30,7 +31,10 @@ export default class Sidebar extends Pure { this.setState({ visiblePopover: 'job' }) } } - + componentWillMount () { + const showMonitor = false + // this.props.startJobMonitor(showMonitor) + } _clickChangePassword = () => { this.setState({visiblePopover: null}) this.props.resetPassword() @@ -63,6 +67,7 @@ export default class Sidebar extends Pure { const navbarStyle = { width: expanded ? 130 : 50 } + const hasActiveJobs = this.props.jobMonitor.jobs.length > 0 return (
    0} label='Job Monitor' onClick={this._selectJob} /> @@ -101,7 +108,7 @@ export default class Sidebar extends Pure { expanded={this.props.expanded} visible={this.state.visiblePopover === 'job'} close={this._closePopover} - removeRetiredJob={this.props.removeRetiredJob} />s + removeRetiredJob={this.props.removeRetiredJob} />
    :
    -
    const tooltip = {this.props.label} diff --git a/src/main/client/common/components/SidebarPopover.js b/src/main/client/common/components/SidebarPopover.js index 368b7fb29..1b9da4da5 100644 --- a/src/main/client/common/components/SidebarPopover.js +++ b/src/main/client/common/components/SidebarPopover.js @@ -19,6 +19,7 @@ export default class SidebarPopover extends Pure { } _onResize = () => { this.setState({width: window.innerWidth, height: window.innerHeight}) + this.reposition() } componentWillMount () { this._onResize() @@ -32,11 +33,6 @@ export default class SidebarPopover extends Pure { componentWillReceiveProps (nextProps) { if (nextProps.visible) this.reposition() } - - _onResize = () => { - this.reposition() - } - reposition () { const padding = 10 // minimum space between popover and top/bottom of screen diff --git a/src/main/client/common/containers/ActiveSidebar.js b/src/main/client/common/containers/ActiveSidebar.js index 9cdabbb2e..72071033b 100644 --- a/src/main/client/common/containers/ActiveSidebar.js +++ b/src/main/client/common/containers/ActiveSidebar.js @@ -5,7 +5,7 @@ import Sidebar from '../components/Sidebar' import { login, logout, resetPassword } from '../../manager/actions/user' import { setActiveProject } from '../../manager/actions/projects' import { setActiveLanguage } from '../../manager/actions/languages' -import { setJobMonitorVisible, removeRetiredJob } from '../../manager/actions/status' +import { setJobMonitorVisible, removeRetiredJob, startJobMonitor } from '../../manager/actions/status' import { setSidebarExpanded, setTutorialHidden } from '../../manager/actions/ui' const mapStateToProps = (state, ownProps) => { @@ -30,9 +30,10 @@ const mapDispatchToProps = (dispatch, ownProps) => { setActiveProject: (project) => { dispatch(setActiveProject(project)) }, setActiveLanguage: (language) => { dispatch(setActiveLanguage(language)) }, setJobMonitorVisible: (visible) => { dispatch(setJobMonitorVisible(visible)) }, + startJobMonitor: (showMonitor) => { dispatch(startJobMonitor(showMonitor)) }, removeRetiredJob: (job) => { dispatch(removeRetiredJob(job)) }, setSidebarExpanded: (value) => { dispatch(setSidebarExpanded(value)) }, - setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) }, + setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) } } } diff --git a/src/main/client/editor/components/CalendarList.js b/src/main/client/editor/components/CalendarList.js index 25a3fd4a7..ebe344209 100644 --- a/src/main/client/editor/components/CalendarList.js +++ b/src/main/client/editor/components/CalendarList.js @@ -94,7 +94,7 @@ export default class CalendarList extends Component { ) }) - : + : } diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index ad9745819..4a25da875 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -63,18 +63,18 @@ export default class EditorFeedSourcePanel extends Component { ? No other snapshots : inactiveSnapshots.map(s => { return ( - + ) }) } - {activeSnapshots.length === 0 + {/* activeSnapshots.length === 0 ? No other snapshots : activeSnapshots.map(s => { return ( ) }) - } + */}
    @@ -84,7 +84,7 @@ export default class EditorFeedSourcePanel extends Component { bsStyle='success' onClick={() => browserHistory.push(`/feed/${this.props.feedSource.id}/edit/`)} > - {getMessage(messages, 'createFromScratch')} + {getMessage(messages, 'createFromScratch')} {' '}or{' '} -
    +

    -

    {routeName}

    +

    {routeName}

    {getRouteName(route)}

    • ID: {routeId}
    • diff --git a/src/main/client/manager/actions/status.js b/src/main/client/manager/actions/status.js index 2a5206021..762cdfdc7 100644 --- a/src/main/client/manager/actions/status.js +++ b/src/main/client/manager/actions/status.js @@ -92,7 +92,7 @@ function stopCurrentTimer (state) { if (timer) clearInterval(timer) } -export function startJobMonitor () { +export function startJobMonitor (showMonitor = true) { return function (dispatch, getState) { stopCurrentTimer(getState()) @@ -102,7 +102,12 @@ export function startJobMonitor () { timerFunction() // make an initial call right now const timer = setInterval(timerFunction, 2000) - + if (showMonitor) { + console.log('showing monitor') + dispatch(setJobMonitorVisible(true)) + } else { + console.log('not showing monitor') + } dispatch(setJobMonitorTimer(timer)) } } diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index d96bd3893..682bf985c 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -131,11 +131,11 @@ class FeedSourceTableRow extends Component { link={`/feed/${fs.id}`} /> {' '} - {!fs.isPublic ? : null} + {!fs.isPublic ? : null} {' '} {fs.editedSinceSnapshot - ? - : + ? + : }

    } @@ -165,14 +165,14 @@ class FeedSourceTableRow extends Component { {fs.latestValidation && fs.latestValidation.errorCount > 0 ?
  • {fs.latestValidation.errorCount}
  • : fs.latestValidation - ?
  • - :
  • + ?
  • + :
  • } {fs.latestValidation && fs.latestValidation.endDate < +moment() ?
  • : fs.latestValidation - ?
  • - :
  • + ?
  • + :
  • } {isModuleEnabled('deployment') && fs.deployable ?
  • diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index d05ff033c..e3096d40c 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -144,7 +144,7 @@ export default class FeedSourceViewer extends Component { Settings}> - + Feed source name Automatic fetch}> - + Feed source fetch URL {/* Title + Shortcut Buttons Row */}

    - + {this.props.project.name} {' '}/{' '} {fs.name}{' '} - {fs.isPublic ? null : } + {fs.isPublic ? null : } {' '} {fs.editedSinceSnapshot - ? - : + ? + : } browserHistory.push(`/feed/${fs.id}/${eventKey}`))} > - {getMessage(messages, 'gtfs')}}> + {getMessage(messages, 'gtfs')}}> {/* @@ -433,7 +433,7 @@ export default class FeedSourceViewer extends Component { {isModuleEnabled('editor') ? {getComponentMessages('EditorFeedSourcePanel').title} {fs.editorSnapshots ? fs.editorSnapshots.length : 0}} + title={{getComponentMessages('EditorFeedSourcePanel').title} {fs.editorSnapshots ? fs.editorSnapshots.length : 0}} > @@ -465,7 +465,7 @@ export default class FeedSourceViewer extends Component { {Object.keys(fs.externalProperties || {}).map(resourceType => { const resourceLowerCase = resourceType.toLowerCase() return ( - {toSentenceCase(resourceType)} properties + {toSentenceCase(resourceType)} properties ) })} diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index 007009a00..e483d5749 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -140,7 +140,7 @@ export default class FeedVersionNavigator extends Component { // disabled={editGtfsDisabled} // || !fs.latestValidation} // bsStyle='info' onClick={() => { this.props.createDeploymentFromFeedSource(fs) }}> - Deploy feed + Deploy feed : null } diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index b887e38a2..d86278590 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -8,6 +8,7 @@ import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' import { getComponentMessages, getMessage, getConfigProperty, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' +import { getProfileLink } from '../../common/util/util' // import Feed from './reporter/containers/Feed' import Patterns from './reporter/containers/Patterns' @@ -88,10 +89,10 @@ export default class FeedVersionReport extends Component { > {/* Name Display / Editor */} {version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0 - ? + ? : version.validationSummary.errorCount > 0 - ? - : + ? + : } {this.props.isPublic ? {version.name} @@ -104,7 +105,9 @@ export default class FeedVersionReport extends Component { {...this.props} />

    - Version published {moment(version.updated).fromNow()} + + Version published {moment(version.updated).fromNow()} by {version.user ? {version.user} : '[unknown]'} +
    ) const tableOptions = { @@ -160,19 +163,19 @@ export default class FeedVersionReport extends Component { onClick={() => this.props.publishFeedVersion(version)} > {this.props.isPublished - ? Published + ? Published : Publish to MTC } : null } - {`Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} + {`Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} {' '} {this.getVersionDateLabel(version)}

    {version.validationSummary && version.validationSummary.avgDailyRevenueTime - ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) + ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) : null }

    @@ -239,7 +242,7 @@ export default class FeedVersionReport extends Component { {isModuleEnabled('validator') ? [ - +

    - {this.props.project.name} + {this.props.project.name} diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index 7816abab2..495311e72 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -66,7 +66,7 @@ export default class UserHomePage extends Component { return ( - + {feedName.length > 33 ? `${feedName.substr(0, 33)}...` : feedName} diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index d896c1c51..2386eeca8 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -85,7 +85,7 @@ class ResultTable extends React.Component { if (!this.props.invalidValues) { return ( {this.props.title} 0

    )} + header={(
    {this.props.title} 0
    )} > No issues found. @@ -93,7 +93,7 @@ class ResultTable extends React.Component { } return ( {this.props.title} {this.props.invalidValues.length})} + header={(
    {this.props.title} {this.props.invalidValues.length}
    )} > diff --git a/src/main/client/manager/reducers/status.js b/src/main/client/manager/reducers/status.js index 39418a7e8..f78ee2824 100644 --- a/src/main/client/manager/reducers/status.js +++ b/src/main/client/manager/reducers/status.js @@ -126,16 +126,14 @@ const config = (state = { } return update(state, { jobMonitor: { retired: { $splice: [[jobIndex, 1]] } } }) case 'RECEIVE_JOBS': - let visible = state.jobMonitor.visible + // let visible = state.jobMonitor.visible let jobs = action.jobs || [] let retired = state.jobMonitor.retired - // make monitor visible if jobs are being received for the first time - if (state.jobMonitor.jobs.length === 0 && jobs.length > 0) visible = true for (var i = 0; i < state.jobMonitor.jobs.length; i++) { let jobIndex = jobs.findIndex(j => j.jobId === state.jobMonitor.jobs[i].jobId) if (jobIndex === -1) retired.push(state.jobMonitor.jobs[i]) } - return update(state, { jobMonitor: { $merge: { jobs, visible, retired } } }) + return update(state, { jobMonitor: { $merge: { jobs, retired } } }) // case 'DEPLOYING_TO_TARGET': // return update(state, { popover: { jobs: { $push: [{name: `Processing deployment`, percent_complete: 5, status: 'processing'}] } } }) diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 8d21b1ab3..d27bbfbcb 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -104,7 +104,7 @@ export default class UserAccount extends Component {
  • {sub.type.replace('-', ' ')}   { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} @@ -125,7 +125,7 @@ export default class UserAccount extends Component { : {target} } {' '} { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} From 1bbce2baac981e37535cb5828c1759cd59c88bab Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 16 Nov 2016 13:30:58 -0500 Subject: [PATCH 139/323] add yarn.lcok --- yarn.lock | 640 +++++++++++++++++++++++++----------------------------- 1 file changed, 293 insertions(+), 347 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8a1cd7d05..2cc6d943b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,5 +1,7 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + "@conveyal/woonerf": version "0.2.0" resolved "https://registry.yarnpkg.com/@conveyal/woonerf/-/woonerf-0.2.0.tgz#53d75d549152082b8ac7d1ab1e980b1e583c72a5" @@ -19,6 +21,17 @@ redux-actions "^0.13.0" redux-logger "^2.7.4" +Base64@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.1.4.tgz#e9f6c6bef567fd635ea4162ab14dd329e74aa6de" + +JSONStream@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.2.1.tgz#32aa5790e799481083b49b4b7fa94e23bae69bf9" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abab@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" @@ -103,6 +116,10 @@ ansi-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.0.0.tgz#c5061b6e0ef8a81775e50f5d66151bf6bf371107" +ansi-styles@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.0.1.tgz#b033f57f93e2d28adeb8bc11138fa13da0fd20a3" + ansi-styles@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" @@ -111,10 +128,6 @@ ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" -ansi-styles@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.0.1.tgz#b033f57f93e2d28adeb8bc11138fa13da0fd20a3" - ansicolors@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" @@ -268,7 +281,7 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@^1.4.0, async@^1.4.2, async@^1.5.0, async@1.x: +async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -284,19 +297,6 @@ attr-accept@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.0.tgz#b5cd35227f163935a8f1de10ed3eba16941f6be6" -auth0-js@^7.3.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-7.4.0.tgz#a2f1c472a4db261912afd20e1ed9c2773e251cdf" - dependencies: - Base64 "~0.1.3" - json-fallback "0.0.1" - jsonp "~0.0.4" - qs "^6.2.1" - reqwest "2.0.5" - trim "~0.0.1" - winchan "0.1.4" - xtend "~2.1.1" - auth0-js@6.8.4: version "6.8.4" resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-6.8.4.tgz#430dd4cacb64d8d15d69b1e621184f9a2a640a61" @@ -323,20 +323,18 @@ auth0-js@7.3.0: winchan "0.1.4" xtend "~2.1.1" -auth0-lock@^10.5.1: - version "10.5.1" - resolved "https://registry.yarnpkg.com/auth0-lock/-/auth0-lock-10.5.1.tgz#ae0a935ed05f6ff3c2abde29018decb302f9ec0b" +auth0-js@^7.3.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-7.4.0.tgz#a2f1c472a4db261912afd20e1ed9c2773e251cdf" dependencies: - auth0-js "7.3.0" - blueimp-md5 "2.3.1" - fbjs "^0.3.1" - immutable "^3.7.3" - jsonp "^0.2.0" - password-sheriff "^1.0.0" - react "^15.0.0 || ^16.0.0" - react-addons-css-transition-group "^15.0.0 || ^16.0.0" - react-dom "^15.0.0 || ^16.0.0" - trim "0.0.1" + Base64 "~0.1.3" + json-fallback "0.0.1" + jsonp "~0.0.4" + qs "^6.2.1" + reqwest "2.0.5" + trim "~0.0.1" + winchan "0.1.4" + xtend "~2.1.1" auth0-lock@9: version "9.2.3" @@ -356,6 +354,21 @@ auth0-lock@9: trim "0.0.1" underscore "~1.5.2" +auth0-lock@^10.5.1: + version "10.5.1" + resolved "https://registry.yarnpkg.com/auth0-lock/-/auth0-lock-10.5.1.tgz#ae0a935ed05f6ff3c2abde29018decb302f9ec0b" + dependencies: + auth0-js "7.3.0" + blueimp-md5 "2.3.1" + fbjs "^0.3.1" + immutable "^3.7.3" + jsonp "^0.2.0" + password-sheriff "^1.0.0" + react "^15.0.0 || ^16.0.0" + react-addons-css-transition-group "^15.0.0 || ^16.0.0" + react-dom "^15.0.0 || ^16.0.0" + trim "0.0.1" + autolinker@~0.15.0: version "0.15.3" resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" @@ -1027,19 +1040,19 @@ babel-register@^6.18.0: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^5.6.18: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" - dependencies: - core-js "^1.0.0" - -babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.9.0, babel-runtime@^6.9.1, babel-runtime@6.x: +babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.9.0, babel-runtime@^6.9.1: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" dependencies: core-js "^2.4.0" regenerator-runtime "^0.9.5" +babel-runtime@^5.6.18: + version "5.8.38" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19" + dependencies: + core-js "^1.0.0" + babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.8.0, babel-template@^6.9.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" @@ -1084,6 +1097,10 @@ babylon@^6.11.0, babylon@^6.11.2, babylon@^6.13.0: version "6.13.1" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.13.1.tgz#adca350e088f0467647157652bafead6ddb8dfdb" +balanced-match@0.1.0, balanced-match@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" + balanced-match@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.2.1.tgz#7bc658b4bed61eee424ad74f75f5c3e2c4df3cc7" @@ -1092,10 +1109,6 @@ balanced-match@^0.4.1, balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" -balanced-match@~0.1.0, balanced-match@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" - base62@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/base62/-/base62-1.1.1.tgz#974e82c11bd5e00816b508a7ed9c7b9086c9db6b" @@ -1108,10 +1121,6 @@ base64-url@^1.2.1: version "1.3.3" resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f" -Base64@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.1.4.tgz#e9f6c6bef567fd635ea4162ab14dd329e74aa6de" - base64url@~1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/base64url/-/base64url-1.0.6.tgz#d64d375d68a7c640d912e2358d170dca5bb54681" @@ -1149,14 +1158,14 @@ bluebird@^3.0.5, bluebird@^3.3.4: version "3.4.6" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" -blueimp-md5@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-1.1.1.tgz#cf84ba18285f5c8835dae8ddae5af6468ceace17" - blueimp-md5@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.3.1.tgz#992a6737733b9da1edd641550dc3acab2e9cfc5a" +blueimp-md5@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-1.1.1.tgz#cf84ba18285f5c8835dae8ddae5af6468ceace17" + bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.6" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" @@ -1249,9 +1258,9 @@ browser-pack@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.1.tgz#779887c792eaa1f64a46a22c8f1051cdcd96755f" dependencies: + JSONStream "^1.0.3" combine-source-map "~0.7.1" defined "^1.0.0" - JSONStream "^1.0.3" through2 "^2.0.0" umd "^3.0.0" @@ -1325,6 +1334,7 @@ browserify@^13.0.0, browserify@^13.0.1: version "13.1.1" resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.1.1.tgz#72a2310e2f706ed87db929cf0ee73a5e195d9bb0" dependencies: + JSONStream "^1.0.3" assert "~1.3.0" browser-pack "^6.0.1" browser-resolve "^1.11.0" @@ -1346,7 +1356,6 @@ browserify@^13.0.0, browserify@^13.0.1: https-browserify "~0.0.0" inherits "~2.0.1" insert-module-globals "^7.0.0" - JSONStream "^1.0.3" labeled-stream-splicer "^2.0.0" module-deps "^4.0.8" os-browserify "~0.1.1" @@ -1437,7 +1446,7 @@ buffer-xor@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" -buffer@^4.1.0, buffer@4.9.1: +buffer@4.9.1, buffer@^4.1.0: version "4.9.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" dependencies: @@ -1544,6 +1553,16 @@ center-align@^0.1.1: align-text "^0.1.3" lazy-cache "^1.0.3" +chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.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" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + chalk@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.5.1.tgz#663b3a648b68b55d04690d49167aa837858f2174" @@ -1554,16 +1573,6 @@ chalk@^0.5.1: strip-ansi "^0.3.0" supports-color "^0.2.0" -chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3, chalk@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - change-case@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.0.0.tgz#6c9c8e35f8790870a82b6b0745be8c3cbef9b081" @@ -1616,7 +1625,7 @@ circular-json@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" -classnames@^2.1.2, classnames@^2.2.0, classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5, classnames@2.x: +classnames@2.x, classnames@^2.1.2, classnames@^2.2.0, classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -1673,7 +1682,7 @@ code-point-at@^1.0.0: dependencies: number-is-nan "^1.0.0" -color-convert@^0.5.3, color-convert@0.5.x: +color-convert@0.5.x, color-convert@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" @@ -1683,19 +1692,13 @@ color-convert@^1.3.0: dependencies: color-name "^1.1.1" -color-name@^1.0.0, color-name@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" - -color-name@1.0.x: +color-name@1.0.x, color-name@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.0.1.tgz#6b34b2b29b7716013972b0b9d5bedcfbb6718df8" -color-string@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" - dependencies: - color-name "^1.0.0" +color-name@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" color-string@0.2.x: version "0.2.4" @@ -1703,6 +1706,12 @@ color-string@0.2.x: dependencies: color-name "1.0.x" +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + color@^0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/color/-/color-0.10.1.tgz#c04188df82a209ddebccecdacd3ec320f193739f" @@ -1751,7 +1760,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.5.0, commander@^2.9.0, commander@2.9.x: +commander@2.9.x, commander@^2.5.0, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: @@ -1928,6 +1937,10 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" +crypto-browserify@1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0" + crypto-browserify@^3.0.0: version "3.11.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.11.0.tgz#3652a0906ab9b2a7e0c3ce66a408e957a2485522" @@ -1943,10 +1956,6 @@ crypto-browserify@^3.0.0: public-encrypt "^4.0.0" randombytes "^2.0.0" -crypto-browserify@1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-1.0.9.tgz#cc5449685dfb85eb11c9828acc7cb87ab5bbfcc0" - css-animation@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.3.0.tgz#315d9742bbe282c23b9951ceac2aa53cee05b708" @@ -1962,7 +1971,7 @@ css-color-function@^1.2.0: debug "~0.7.4" rgb "~0.1.0" -"cssom@>= 0.3.0 < 0.4.0", cssom@0.3.x: +cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": version "0.3.1" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" @@ -1988,7 +1997,7 @@ curry2@^1.0.0, curry2@^1.0.1: dependencies: fast-bind "^1.0.0" -cz-conventional-changelog@^1.1.6, cz-conventional-changelog@1.2.0: +cz-conventional-changelog@1.2.0, cz-conventional-changelog@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/cz-conventional-changelog/-/cz-conventional-changelog-1.2.0.tgz#2bca04964c8919b23f3fd6a89ef5e6008b31b3f8" dependencies: @@ -1999,18 +2008,18 @@ cz-conventional-changelog@^1.1.6, cz-conventional-changelog@1.2.0: right-pad "^1.0.1" word-wrap "^1.0.3" -d@^0.1.1, d@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" - dependencies: - es5-ext "~0.10.2" - d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" dependencies: es5-ext "^0.10.9" +d@^0.1.1, d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + dependencies: + es5-ext "~0.10.2" + dashdash@^1.12.0: version "1.14.0" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.0.tgz#29e486c5418bf0f356034a993d51686a33e84141" @@ -2021,14 +2030,14 @@ data-uri-to-buffer@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - date-now@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c" +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + debounce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.0.tgz#0948af513d2e4ce407916f8506a423d3f9cf72d8" @@ -2045,16 +2054,16 @@ debug@*, debug@^2.1.1, debug@^2.2.0, debug@~2.2.0: dependencies: ms "0.7.1" -debug@~0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" - debug@2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/debug/-/debug-2.1.3.tgz#ce8ab1b5ee8fbee2bfa3b633cab93d366b63418e" dependencies: ms "0.7.0" +debug@~0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" + 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" @@ -2067,7 +2076,7 @@ deep-diff@0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.4.tgz#aac5c39952236abe5f037a2349060ba01b00ae48" -deep-equal@^1.0.0, deep-equal@1.0.1: +deep-equal@1.0.1, deep-equal@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" @@ -2144,7 +2153,7 @@ detect-file@^0.1.0: dependencies: fs-exists-sync "^0.1.0" -detect-indent@^4.0.0, detect-indent@4.0.0: +detect-indent@4.0.0, detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" dependencies: @@ -2353,7 +2362,7 @@ es6-set@~0.1.3: es6-symbol "3" event-emitter "~0.3.4" -es6-symbol@~3.1, es6-symbol@~3.1.0, es6-symbol@3: +es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.0.tgz#94481c655e7a7cad82eba832d97d5433496d7ffa" dependencies: @@ -2384,7 +2393,7 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^ version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escodegen@^1.6.1, escodegen@1.8.x: +escodegen@1.8.x, escodegen@^1.6.1: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" dependencies: @@ -2506,7 +2515,7 @@ esprima-fb@~15001.1001.0-dev-harmony-fb: version "15001.1001.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" -esprima@^2.6.0, esprima@^2.7.1, esprima@2.7.x: +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" @@ -2717,6 +2726,14 @@ fb-watchman@^1.8.0, fb-watchman@^1.9.0: dependencies: bser "^1.0.2" +fbjs@0.1.0-alpha.10: + version "0.1.0-alpha.10" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.1.0-alpha.10.tgz#46e457c09cbefb51fc752a3e030e7b67fcc384c8" + dependencies: + core-js "^1.0.0" + promise "^7.0.3" + whatwg-fetch "^0.9.0" + fbjs@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.3.2.tgz#033a540595084b5de3509a405d06f1a2a8e5b9fb" @@ -2739,14 +2756,6 @@ fbjs@^0.8.4: promise "^7.1.1" ua-parser-js "^0.7.9" -fbjs@0.1.0-alpha.10: - version "0.1.0-alpha.10" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.1.0-alpha.10.tgz#46e457c09cbefb51fc752a3e030e7b67fcc384c8" - dependencies: - core-js "^1.0.0" - promise "^7.0.3" - whatwg-fetch "^0.9.0" - figures@^1.3.5: version "1.7.0" resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" @@ -2799,7 +2808,7 @@ find-node-modules@1.0.3: findup-sync "^0.2.1" merge "^1.2.0" -find-root@^1.0.0, find-root@1.0.0: +find-root@1.0.0, find-root@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.0.0.tgz#962ff211aab25c6520feeeb8d6287f8f6e95807a" @@ -3017,7 +3026,7 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^5.0.15, glob@5.0.x, glob@5.x: +glob@5.0.x, glob@5.x, glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: @@ -3027,24 +3036,24 @@ glob@^5.0.15, glob@5.0.x, glob@5.x: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" +glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" dependencies: + fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "2 || 3" + minimatch "^3.0.2" once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: - version "7.1.1" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" dependencies: - fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.2" + minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" @@ -3057,17 +3066,6 @@ glob@~4.3.0: minimatch "^2.0.1" once "^1.3.0" -glob@7.0.5: - version "7.0.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.2" - once "^1.3.0" - path-is-absolute "^1.0.0" - global-modules@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" @@ -3216,7 +3214,7 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -home-or-tmp@^2.0.0, home-or-tmp@2.0.0: +home-or-tmp@2.0.0, home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" dependencies: @@ -3271,7 +3269,7 @@ https-browserify@~0.0.0: version "0.0.1" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" -iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13, iconv-lite@0.4.13: +iconv-lite@0.4.13, iconv-lite@^0.4.13, iconv-lite@^0.4.5, iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" @@ -3328,11 +3326,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1, inherits@2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -inherits@2.0.1: +inherits@2, inherits@2.0.1, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" @@ -3352,39 +3346,39 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" +inquirer@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.1.2.tgz#ac3ba5f06b8e7291abd9f22912c03f09cfe2dd1f" dependencies: ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" chalk "^1.0.0" cli-cursor "^1.0.1" cli-width "^2.0.0" + external-editor "^1.0.1" figures "^1.3.5" lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" + mute-stream "0.0.6" + pinkie-promise "^2.0.0" + run-async "^2.2.0" + rx "^4.1.0" string-width "^1.0.1" strip-ansi "^3.0.0" through "^2.3.6" -inquirer@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.1.2.tgz#ac3ba5f06b8e7291abd9f22912c03f09cfe2dd1f" +inquirer@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" dependencies: ansi-escapes "^1.1.0" + ansi-regex "^2.0.0" chalk "^1.0.0" cli-cursor "^1.0.1" cli-width "^2.0.0" - external-editor "^1.0.1" figures "^1.3.5" lodash "^4.3.0" - mute-stream "0.0.6" - pinkie-promise "^2.0.0" - run-async "^2.2.0" - rx "^4.1.0" + readline2 "^1.0.1" + run-async "^0.1.0" + rx-lite "^3.1.2" string-width "^1.0.1" strip-ansi "^3.0.0" through "^2.3.6" @@ -3393,10 +3387,10 @@ 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" dependencies: + JSONStream "^1.0.3" combine-source-map "~0.7.1" concat-stream "~1.5.1" is-buffer "^1.1.0" - JSONStream "^1.0.3" lexical-scope "^1.2.0" process "~0.11.0" through2 "^2.0.0" @@ -3596,14 +3590,14 @@ is@~0.2.6: version "0.2.7" resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" -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" - -isarray@~0.0.1, isarray@0.0.1: +isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" +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" + isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" @@ -3914,7 +3908,7 @@ js-tokens@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" -js-yaml@^3.5.1, js-yaml@3.x: +js-yaml@3.x, js-yaml@^3.5.1: version "3.6.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" dependencies: @@ -4010,13 +4004,6 @@ jsonpointer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" -JSONStream@^1.0.3: - version "1.2.1" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.2.1.tgz#32aa5790e799481083b49b4b7fa94e23bae69bf9" - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - jsonwebtoken@^5.0.0: version "5.7.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz#1c90f9a86ce5b748f5f979c12b70402b4afcddb4" @@ -4393,6 +4380,10 @@ lodash.uniq@^4.3.0, lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" +lodash@4.15.0: + version "4.15.0" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.15.0.tgz#3162391d8f0140aa22cf8f6b3c34d6b7f63d3aa9" + lodash@^4.0.0, lodash@^4.0.1, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.16.1, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.16.6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" @@ -4401,10 +4392,6 @@ lodash@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.5.0.tgz#19bb3f4d51278f0b8c818ed145c74ecf9fe40e6d" -lodash@4.15.0: - version "4.15.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.15.0.tgz#3162391d8f0140aa22cf8f6b3c34d6b7f63d3aa9" - log-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" @@ -4614,7 +4601,7 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.7: dependencies: mime-db "~1.24.0" -mime@^1.3.4, mime@1.3.4: +mime@1.3.4, mime@^1.3.4: version "1.3.4" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" @@ -4622,35 +4609,31 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -minimatch@^2.0.1, minimatch@2.x: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - dependencies: - brace-expansion "^1.0.0" - -minimatch@^3.0.0, minimatch@^3.0.2, "minimatch@2 || 3": +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" +minimatch@2.x, minimatch@^2.0.1: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" minimist@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.5.tgz#d7aa327bcecf518f9106ac6b8f003fa3bcea8566" -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" -mkdirp@^0.5.0, mkdirp@^0.5.1, "mkdirp@>=0.5 0", mkdirp@~0.5.1, mkdirp@0.5.x: +minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -4660,6 +4643,7 @@ module-deps@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/module-deps/-/module-deps-4.0.8.tgz#55fd70623399706c3288bef7a609ff1e8c0ed2bb" dependencies: + JSONStream "^1.0.3" browser-resolve "^1.7.0" cached-path-relative "^1.0.0" concat-stream "~1.5.0" @@ -4667,7 +4651,6 @@ module-deps@^4.0.8: detective "^4.0.0" duplexer2 "^0.1.2" inherits "^2.0.1" - JSONStream "^1.0.3" parents "^1.0.0" readable-stream "^2.0.2" resolve "^1.1.3" @@ -4689,19 +4672,15 @@ moment-timezone@^0.5.3: dependencies: moment ">= 2.6.0" -moment@^2.11.2, moment@^2.8.2, "moment@>= 2.6.0": +"moment@>= 2.6.0", moment@^2.11.2, moment@^2.8.2: version "2.15.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.2.tgz#1bfdedf6a6e345f322fe956d5df5bd08a8ce84dc" -ms@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - ms@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" -ms@0.7.1: +ms@0.7.1, ms@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -4812,7 +4791,7 @@ node.flow@1.2.3: dependencies: node.extend "1.0.8" -nopt@~3.0.6, nopt@3.x: +nopt@3.x, nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -4864,6 +4843,10 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" +object-assign@4.x, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" + object-assign@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-1.0.0.tgz#e65dc8766d3b47b4b8307465c8311da030b070a6" @@ -4872,10 +4855,6 @@ object-assign@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" -object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@4.x: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - object-inspect@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-0.4.0.tgz#f5157c116c1455b243b06ee97703392c5ad89fec" @@ -4905,7 +4884,7 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@1.x: +once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -5100,7 +5079,7 @@ path-case@^2.1.0: dependencies: no-case "^2.2.0" -path-exists@^2.0.0, path-exists@2.1.0: +path-exists@2.1.0, path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" dependencies: @@ -5157,10 +5136,6 @@ pem@^1.8.3: os-tmpdir "^1.0.1" which "^1.2.4" -performance-now@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5548,26 +5523,18 @@ pump@^1.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^1.3.2, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" +punycode@^1.3.2, punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + q@^1.1.2: version "1.4.1" resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" -qs@^6.2.1, qs@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" - -qs@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" - qs@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" @@ -5576,10 +5543,18 @@ qs@6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.0.tgz#3b7848c03c2dece69a9522b0fae8c4126d745f3b" +qs@^6.2.1, qs@~6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442" + "qs@git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays": version "0.6.6" resolved "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8" +qs@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" + query-string@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-3.0.3.tgz#ae2e14b4d05071d4e9b9eb4873c35b0dcd42e638" @@ -5620,12 +5595,6 @@ quote-stream@~0.0.0: minimist "0.0.8" through2 "~0.4.1" -raf@^3.1.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.3.0.tgz#93845eeffc773f8129039f677f80a36044eee2c3" - dependencies: - performance-now "~0.2.0" - randomatic@^1.1.3: version "1.1.5" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.5.tgz#5e9ef5f2d573c67bd2b8124ae90b5156e457840b" @@ -5693,7 +5662,7 @@ rc-trigger@1.x: rc-animate "2.x" rc-util "^3.3.x" -rc-util@^3.2.1, rc-util@^3.3.x, rc-util@3.x: +rc-util@3.x, rc-util@^3.2.1, rc-util@^3.3.x: version "3.4.1" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-3.4.1.tgz#4b7e0b0c7593bdbcff8ed045d88fbbc773a7b061" dependencies: @@ -5722,11 +5691,7 @@ react-addons-perf@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.3.2.tgz#c9edba49b9eab44d0c59024d289beb1ab97318b5" -"react-addons-update@^0.14.0 || ^15.0.0": - version "15.3.2" - resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.3.2.tgz#b6385c4db1e5df371825e0615b04360ed94430fe" - -react-addons-update@^0.14.7: +"react-addons-update@^0.14.0 || ^15.0.0", react-addons-update@^0.14.7: version "0.14.8" resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-0.14.8.tgz#486f1fb71e09ef8ad09c0e95fbe59d173782fe1a" @@ -5810,12 +5775,6 @@ react-input-autosize@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-1.1.0.tgz#3fe1ac832387d8abab85f6051ceab1c9e5570853" -"react-leaflet-draw@github:alex3165/react-leaflet-draw": - version "0.10.1" - resolved "https://codeload.github.com/alex3165/react-leaflet-draw/tar.gz/c6422e0205782d84bec913cd9fe5014c92178f8c" - dependencies: - react-leaflet "^0.12.1" - react-leaflet@^0.12.1: version "0.12.3" resolved "https://registry.yarnpkg.com/react-leaflet/-/react-leaflet-0.12.3.tgz#db37372e2f9bca7c45cc6d8c2e720bee7788845b" @@ -5896,21 +5855,21 @@ react-toastr@^2.8.0: react-addons-update "^0.14.0 || ^15.0.0" react-dom "^0.14.0 || ^15.0.0" -react-virtualized-select@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-1.4.0.tgz#6a7adededbcbf0421280f0a106a920dbff2d5f38" +react-virtualized-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-2.1.0.tgz#dbb05a700e198fcf0f99e59166065750f63c5685" dependencies: + babel-runtime "^6.11.6" react-select "^1.0.0-beta14" - react-virtualized "^7.0.0" + react-virtualized "^8.0.5" -react-virtualized@^7.0.0, react-virtualized@^7.11.2: - version "7.24.3" - resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-7.24.3.tgz#b9b1b20568339e66e7b6908e92ff6b55309253b7" +react-virtualized@^8.0.5, react-virtualized@^8.5.0: + version "8.5.1" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-8.5.1.tgz#52e38f9382d46fecd6b016c6935eb2c5cf9b3e5f" dependencies: babel-runtime "^6.11.6" classnames "^2.2.3" dom-helpers "^2.4.0" - raf "^3.1.0" "react@^15.0.0 || ^16.0.0", react@^15.2.1, react@^15.3.2: version "15.3.2" @@ -5956,7 +5915,16 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@~2.1.4: +"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.1.0, readable-stream@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" dependencies: @@ -5968,14 +5936,16 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2. string_decoder "~0.10.x" util-deprecate "~1.0.1" -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.17, readable-stream@~1.0.27-1: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" +readable-stream@^2.0.2, 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" dependencies: core-util-is "~1.0.0" inherits "~2.0.1" - isarray "0.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" string_decoder "~0.10.x" + util-deprecate "~1.0.1" readable-stream@~1.1.9: version "1.1.14" @@ -5986,17 +5956,6 @@ readable-stream@~1.1.9: isarray "0.0.1" string_decoder "~0.10.x" -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" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -6190,14 +6149,14 @@ requires-port@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" -reqwest@^1.1.4: - version "1.1.6" - resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-1.1.6.tgz#4b6894d29596bf8e824a25f34975df15562ee813" - reqwest@2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-2.0.5.tgz#00fb15ac4918c419ca82b43f24c78882e66039a1" +reqwest@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/reqwest/-/reqwest-1.1.6.tgz#4b6894d29596bf8e824a25f34975df15562ee813" + resolve-dir@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" @@ -6209,7 +6168,7 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@1.1.7, resolve@1.1.x: +resolve@1.1.7, resolve@1.1.x, resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -6253,7 +6212,7 @@ right-pad@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" -rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4, rimraf@2: +rimraf@2, rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -6315,11 +6274,7 @@ sane@^1.3.3, sane@~1.4.1: walker "~1.0.5" watch "~0.10.0" -sax@^1.1.4, sax@>=0.6.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" - -sax@1.1.5: +sax@1.1.5, sax@>=0.6.0, sax@^1.1.4: version "1.1.5" resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.5.tgz#1da50a8d00cdecd59405659f5ff85349fe773743" @@ -6336,14 +6291,14 @@ selectn@^1.0.20, selectn@^1.0.5: debug "^2.2.0" dotsplit.js "^1.0.3" +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + semver@^4.3.3: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" -semver@^5.0.1, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0, "semver@2 || 3 || 4 || 5": - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - send@0.14.1: version "0.14.1" resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" @@ -6422,6 +6377,10 @@ shell-quote@^1.4.2, shell-quote@^1.4.3: array-reduce "~0.0.0" jsonify "~0.0.0" +shelljs@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.5.3.tgz#c54982b996c76ef0c1e6b59fbdc5825f5b713113" + shelljs@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" @@ -6434,10 +6393,6 @@ shelljs@^0.7.0: interpret "^1.0.0" rechoir "^0.6.2" -shelljs@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.5.3.tgz#c54982b996c76ef0c1e6b59fbdc5825f5b713113" - shellwords@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" @@ -6498,16 +6453,22 @@ source-map-support@~0.2.8: dependencies: source-map "0.1.32" -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" +source-map@0.1.32: + version "0.1.32" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" dependencies: amdefine ">=0.0.4" -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, "source-map@>= 0.1.2", source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: +"source-map@>= 0.1.2", source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" +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" + dependencies: + amdefine ">=0.0.4" + source-map@~0.1.33: version "0.1.43" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" @@ -6520,12 +6481,6 @@ source-map@~0.2.0: dependencies: amdefine ">=0.0.4" -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" - dependencies: - amdefine ">=0.0.4" - spawn-sync@^1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" @@ -6621,7 +6576,7 @@ static-module@^1.1.0: static-eval "~0.2.0" through2 "~0.4.1" -"statuses@>= 1.3.0 < 2", statuses@~1.3.0, statuses@1: +statuses@1, "statuses@>= 1.3.0 < 2", statuses@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" @@ -6666,10 +6621,6 @@ 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" -string_decoder@~0.10.0, string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - string-to-js@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/string-to-js/-/string-to-js-0.0.1.tgz#bf153c760636faa30769b804a0195552ba7ad80f" @@ -6693,6 +6644,10 @@ string.prototype.codepointat@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.0.tgz#6b26e9bd3afcaa7be3b4269b526de1b82000ac78" +string_decoder@~0.10.0, string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + stringstream@~0.0.4: version "0.0.5" resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" @@ -6725,20 +6680,24 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-json-comments@~1.0.1, strip-json-comments@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" - 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" +strip-json-comments@~1.0.1, strip-json-comments@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" + subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" dependencies: minimist "^1.1.0" +supports-color@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" + supports-color@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-0.2.0.tgz#d92de2694eb3f67323973d7ae3d8b55b4c22190a" @@ -6753,10 +6712,6 @@ supports-color@^3.1.0, supports-color@^3.1.2: dependencies: has-flag "^1.0.0" -supports-color@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.3.1.tgz#15758df09d8ff3b4acc307539fabe27095e1042d" - swap-case@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" @@ -6778,7 +6733,7 @@ syntax-error@^1.1.1: dependencies: acorn "^2.7.0" -systemjs-builder@^0.15.33, systemjs-builder@0.15.33: +systemjs-builder@0.15.33, systemjs-builder@^0.15.33: version "0.15.33" resolved "https://registry.yarnpkg.com/systemjs-builder/-/systemjs-builder-0.15.33.tgz#7bd4d045769a67b52f9596141ba21cd94b49910c" dependencies: @@ -6798,7 +6753,7 @@ systemjs-builder@^0.15.33, systemjs-builder@0.15.33: traceur "0.0.105" uglify-js "^2.6.1" -systemjs@^0.19.39, systemjs@0.19.40: +systemjs@0.19.40, systemjs@^0.19.39: version "0.19.40" resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-0.19.40.tgz#158f64a9f4ef541a7fda6b40e527ee46b6c54cd0" dependencies: @@ -6882,14 +6837,6 @@ throat@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-3.0.0.tgz#e7c64c867cbb3845f10877642f7b60055b8ec0d6" -through@^2.3.4, through@^2.3.6, through@^2.3.7, "through@>=2.2.7 <3", through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -through@~2.2.7: - version "2.2.7" - resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" - through2@^2.0.0, through2@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" @@ -6911,6 +6858,14 @@ through2@~0.6.1: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" +"through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.7, through@~2.3.4: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +through@~2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/through/-/through-2.2.7.tgz#6e8e21200191d4eb6a99f6f010df46aa1c6eb2bd" + timers-browserify@^1.0.1: version "1.4.2" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" @@ -6981,7 +6936,7 @@ trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" -trim@~0.0.1, trim@0.0.1: +trim@0.0.1, trim@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" @@ -7109,7 +7064,7 @@ ua-parser-js@^0.7.9: version "0.7.10" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f" -uglify-js@^2.6, uglify-js@^2.6.1, uglify-js@2.x.x: +uglify-js@2.x.x, uglify-js@^2.6, uglify-js@^2.6.1: version "2.7.4" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.4.tgz#a295a0de12b6a650c031c40deb0dc40b14568bd2" dependencies: @@ -7170,7 +7125,7 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -unpipe@~1.0.0, unpipe@1.0.0: +unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -7188,16 +7143,16 @@ url-trim@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/url-trim/-/url-trim-1.0.0.tgz#40057e2f164b88e5daca7269da47e6d1dd837adc" -url@~0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" dependencies: punycode "1.3.2" querystring "0.2.0" -url@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" +url@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: punycode "1.3.2" querystring "0.2.0" @@ -7212,7 +7167,7 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" -util@~0.10.1, 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" dependencies: @@ -7259,7 +7214,7 @@ walker@~1.0.5: dependencies: makeerror "1.0.x" -warning@^2.0.0, warning@2.1.0: +warning@2.1.0, warning@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" dependencies: @@ -7318,14 +7273,14 @@ whatwg-encoding@^1.0.1: dependencies: iconv-lite "0.4.13" -whatwg-fetch@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" - whatwg-fetch@>=0.10.0: version "1.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.0.0.tgz#01c2ac4df40e236aaa18480e3be74bd5c8eb798e" +whatwg-fetch@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" + whatwg-url@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-3.0.0.tgz#b9033c50c7ce763e91d78777ce825a6d7f56dac5" @@ -7353,34 +7308,30 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.1" -winchan@^0.1.1, winchan@0.1.4: +winchan@0.1.4, winchan@^0.1.1: version "0.1.4" resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.1.4.tgz#88fa12411cd542eb626018c38a196bcbb17993bb" -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +window-size@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" + word-wrap@^1.0.3: version "1.1.0" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.1.0.tgz#356153d61d10610d600785c5d701288e0ae764a6" +wordwrap@0.0.2, wordwrap@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - worker-farm@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.3.1.tgz#4333112bb49b17aa050b87895ca6b2cacf40e5ff" @@ -7415,17 +7366,13 @@ xml2js@0.4.15: sax ">=0.6.0" xmlbuilder ">=2.4.6" -xmlbuilder@>=2.4.6: - version "8.2.2" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773" - -xmlbuilder@2.6.2: +xmlbuilder@2.6.2, xmlbuilder@>=2.4.6: version "2.6.2" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-2.6.2.tgz#f916f6d10d45dc171b1be2e6e673fb6e0cc35d0a" dependencies: lodash "~3.5.0" -xtend@^4.0.0, xtend@^4.0.1, "xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" @@ -7480,4 +7427,3 @@ yargs@~3.10.0: cliui "^2.1.0" decamelize "^1.0.0" window-size "0.1.0" - From 4a791db3839d36d3d5d2f3f44ecbf31103226088 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 16 Nov 2016 17:11:46 -0500 Subject: [PATCH 140/323] fix timestamp and user email for feed version --- .../common/components/SelectFileModal.js | 18 ++---- src/main/client/manager/actions/feeds.js | 57 ++++++++--------- .../controllers/api/FeedSourceController.java | 2 +- .../api/FeedVersionController.java | 19 ++++-- .../datatools/manager/models/FeedSource.java | 43 +++++-------- .../datatools/manager/models/FeedVersion.java | 64 +++++++------------ .../datatools/manager/models/Model.java | 8 ++- 7 files changed, 91 insertions(+), 120 deletions(-) diff --git a/src/main/client/common/components/SelectFileModal.js b/src/main/client/common/components/SelectFileModal.js index 41efd0d79..1be3d892f 100644 --- a/src/main/client/common/components/SelectFileModal.js +++ b/src/main/client/common/components/SelectFileModal.js @@ -1,8 +1,8 @@ -import React from 'react' +import React, { Component } from 'react' import { Modal, Button, FormControl } from 'react-bootstrap' import ReactDOM from 'react-dom' -export default class SelectFileModal extends React.Component { +export default class SelectFileModal extends Component { constructor (props) { super(props) @@ -34,23 +34,19 @@ export default class SelectFileModal extends React.Component { } ok () { - if(this.props.onConfirm) { + if (this.props.onConfirm) { if (this.props.onConfirm(ReactDOM.findDOMNode(this.refs.fileInput).files)) { this.close() - } - else { + } else { this.setState({error: this.props.errorMessage || this.state.errorMessage}) } - } - else if(this.state.onConfirm) { + } else if (this.state.onConfirm) { if (this.state.onConfirm(ReactDOM.findDOMNode(this.refs.fileInput).files)) { this.close() - } - else { + } else { this.setState({error: this.props.errorMessage || this.state.errorMessage}) } - } - else { + } else { this.close() } } diff --git a/src/main/client/manager/actions/feeds.js b/src/main/client/manager/actions/feeds.js index 6beda6e0b..8323bbcfd 100644 --- a/src/main/client/manager/actions/feeds.js +++ b/src/main/client/manager/actions/feeds.js @@ -1,4 +1,5 @@ import qs from 'qs' +import fetch from 'isomorphic-fetch' import { secureFetch } from '../../common/util/util' import { fetchProject, fetchProjectWithFeeds } from './projects' @@ -45,12 +46,6 @@ export function fetchUserFeeds (userId) { } } -function requestingPublicFeeds () { - return { - type: 'REQUESTING_PUBLIC_FEEDS' - } -} - function receivePublicFeeds (feeds) { return { type: 'RECEIVE_PUBLIC_FEEDS', @@ -92,7 +87,7 @@ export function updateFeedSource (feedSource, changes) { console.log(res.json()) dispatch(setErrorMessage('Error updating feed source.')) } - //return dispatch(fetchProjectFeeds(feedSource.projectId)) + // return dispatch(fetchProjectFeeds(feedSource.projectId)) return dispatch(fetchFeedSource(feedSource.id, true)) }) } @@ -100,7 +95,7 @@ export function updateFeedSource (feedSource, changes) { export function updateExternalFeedResource (feedSource, resourceType, properties) { return function (dispatch, getState) { - console.log('updateExternalFeedResource', feedSource, resourceType, properties); + console.log('updateExternalFeedResource', feedSource, resourceType, properties) dispatch(savingFeedSource()) const url = `/api/manager/secure/feedsource/${feedSource.id}/updateExternal?resourceType=${resourceType}` return secureFetch(url, getState(), 'put', properties) @@ -110,7 +105,6 @@ export function updateExternalFeedResource (feedSource, resourceType, properties } } - export function deletingFeedSource (feedSource) { return { type: 'DELETING_FEEDSOURCE', @@ -235,11 +229,9 @@ export function runFetchFeed (feedSource) { .then(res => { if (res.status === 304) { dispatch(feedNotModified(feedSource, 'Feed fetch cancelled because it matches latest feed version.')) - } - else if (res.status >= 400) { + } else if (res.status >= 400) { dispatch(setErrorMessage('Error fetching feed source')) - } - else { + } else { dispatch(receivedFetchFeed(feedSource)) dispatch(startJobMonitor()) return res.json() @@ -253,7 +245,7 @@ export function runFetchFeed (feedSource) { } } -//** FEED VERSION ACTIONS **// +//* FEED VERSION ACTIONS *// // Get all FeedVersions for FeedSource @@ -292,7 +284,6 @@ export function fetchFeedVersions (feedSource, unsecured) { } } - export function requestingFeedVersion () { return { type: 'REQUESTING_FEEDVERSION' @@ -358,9 +349,11 @@ export function fetchPublicFeedVersions (feedSource) { // Upload a GTFS File as a new FeedVersion -export function uploadingFeed () { +export function uploadingFeed (feedSource, file) { return { - type: 'UPLOADING_FEED' + type: 'UPLOADING_FEED', + feedSource, + file } } @@ -381,10 +374,10 @@ export function feedNotModified (feedSource, message) { export function uploadFeed (feedSource, file) { return function (dispatch, getState) { - dispatch(uploadingFeed()) - const url = `/api/manager/secure/feedversion?feedSourceId=${feedSource.id}` + dispatch(uploadingFeed(feedSource, file)) + const url = `/api/manager/secure/feedversion?feedSourceId=${feedSource.id}&lastModified=${file.lastModified}` - var data = new FormData() + var data = new window.FormData() data.append('file', file) return fetch(url, { @@ -394,11 +387,9 @@ export function uploadFeed (feedSource, file) { }).then(res => { if (res.status === 304) { dispatch(feedNotModified(feedSource, 'Feed upload cancelled because it matches latest feed version.')) - } - else if (res.status >= 400) { + } else if (res.status >= 400) { dispatch(setErrorMessage('Error uploading feed source')) - } - else { + } else { dispatch(uploadedFeed(feedSource)) dispatch(startJobMonitor()) } @@ -412,15 +403,16 @@ export function uploadFeed (feedSource, file) { // Delete an existing FeedVersion -export function deletingFeedVersion () { +export function deletingFeedVersion (feedVersion) { return { - type: 'DELETING_FEEDVERSION' + type: 'DELETING_FEEDVERSION', + feedVersion } } export function deleteFeedVersion (feedVersion, changes) { return function (dispatch, getState) { - dispatch(deletingFeedVersion()) + dispatch(deletingFeedVersion(feedVersion)) const url = '/api/manager/secure/feedversion/' + feedVersion.id return secureFetch(url, getState(), 'delete') .then((res) => { @@ -432,9 +424,10 @@ export function deleteFeedVersion (feedVersion, changes) { // Get GTFS validation results for a FeedVersion -export function requestingValidationResult () { +export function requestingValidationResult (feedVersion) { return { - type: 'REQUESTING_VALIDATION_RESULT' + type: 'REQUESTING_VALIDATION_RESULT', + feedVersion } } @@ -448,7 +441,7 @@ export function receiveValidationResult (feedVersion, validationResult) { export function fetchValidationResult (feedVersion, isPublic) { return function (dispatch, getState) { - dispatch(requestingValidationResult()) + dispatch(requestingValidationResult(feedVersion)) const route = isPublic ? 'public' : 'secure' const url = `/api/manager/${route}/feedversion/${feedVersion.id}/validation` return secureFetch(url, getState()) @@ -491,7 +484,7 @@ export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat console.log(res.status) if (res.status === 202) { // dispatch(setStatus) - console.log("building network") + console.log('building network') return [] } return res.json() @@ -556,7 +549,7 @@ export function renameFeedVersion (feedVersion, name) { } } -//** NOTES ACTIONS **// +//* NOTES ACTIONS *// export function requestingNotes () { return { diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index c6cc8f440..9ea902849 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -246,7 +246,7 @@ public static FeedVersion fetch (Request req, Response res) throws JsonProcessin * Helper function returns feed source if user has permission for specified action. * @param req spark Request object from API request * @param action action type (either "view" or "manage") - * @return + * @return feedsource object for ID */ private static FeedSource requestFeedSourceById(Request req, String action) { String id = req.params("id"); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index 6153a05db..1832993d2 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -97,26 +97,30 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc FeedSource s = requestFeedSourceById(req, "manage"); if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { - halt(400, "Feed is autofetched! Cannot upload."); + halt(400, "Feed is auto-fetched! Cannot upload."); } FeedVersion latest = s.getLatest(); FeedVersion v = new FeedVersion(s); v.setUser(userProfile); - +// String lastModified = req.raw().getPart("lastModified").toString(); +// System.out.println("last modified: " + lastModified); if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) { MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement); } Part part = req.raw().getPart("file"); - +// System.out.println(lastModified); LOG.info("Saving feed from upload {}", s); InputStream uploadStream; try { uploadStream = part.getInputStream(); - v.newGtfsFile(uploadStream); + + // set last modified based on value of query param + File file = v.newGtfsFile(uploadStream, Long.valueOf(req.queryParams("lastModified"))); + System.out.println(file.lastModified()); } catch (Exception e) { e.printStackTrace(); LOG.error("Unable to open input stream from upload"); @@ -135,7 +139,8 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc } v.name = v.getFormattedTimestamp() + " Upload"; - +// v.fileTimestamp + v.userId = userProfile.getUser_id(); v.save(); new ProcessSingleFeedJob(v, userProfile.getUser_id()).run(); @@ -151,7 +156,7 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc public static Boolean createFeedVersionFromSnapshot (Request req, Response res) throws IOException, ServletException { Auth0UserProfile userProfile = req.attribute("user"); - // TODO: should this be edit privelege? + // TODO: should this be edit privilege? FeedSource s = requestFeedSourceById(req, "manage"); FeedVersion v = new FeedVersion(s); CreateFeedVersionFromSnapshotJob createFromSnapshotJob = @@ -188,7 +193,7 @@ private static FeedVersion requestFeedVersion(Request req, String action) { halt(404, "Version ID does not exist"); } // performs permissions checks for at feed source level and halts if any issues - FeedSource s = requestFeedSource(req, version.getFeedSource(), "manage"); + requestFeedSource(req, version.getFeedSource(), action); return version; } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index 2bd9df647..b00f8d762 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -15,6 +15,7 @@ import org.mapdb.Fun; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import spark.HaltException; import java.io.File; import java.io.IOException; @@ -203,7 +204,7 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { statusMap.put("percentComplete", 75.0); statusMap.put("error", false); eventBus.post(statusMap); - File out = version.newGtfsFile(conn.getInputStream()); + version.newGtfsFile(conn.getInputStream()); } else { @@ -226,7 +227,8 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { e.printStackTrace(); halt(400, message); return null; - } catch (Exception e) { + } catch (HaltException e) { + LOG.warn("Halt thrown", e); throw e; } @@ -262,6 +264,7 @@ else if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { statusMap.put("error", false); eventBus.post(statusMap); version.setUserById(fetchUser); + version.fileTimestamp = conn.getLastModified(); return version; } } @@ -354,11 +357,9 @@ public Map> getExternalProperties() { for(String resourceType : DataManager.feedResources.keySet()) { Map propTable = new HashMap<>(); - for (ExternalFeedSourceProperty prop : ExternalFeedSourceProperty.getAll()) { - if (prop.getFeedSourceId().equals(this.id)) { - propTable.put(prop.name, prop.value); - } - } + ExternalFeedSourceProperty.getAll().stream() + .filter(prop -> prop.getFeedSourceId().equals(this.id)) + .forEach(prop -> propTable.put(prop.name, prop.value)); resourceTable.put(resourceType, propTable); } @@ -375,20 +376,14 @@ public static Collection getAll() { /** * Get all of the feed versions for this source - * @return + * @return collection of feed versions */ @JsonIgnore public Collection getFeedVersions() { // TODO Indices - ArrayList ret = new ArrayList(); - - for (FeedVersion v : FeedVersion.getAll()) { - if (this.id.equals(v.feedSourceId)) { - ret.add(v); - } - } - - return ret; + return FeedVersion.getAll().stream() + .filter(v -> this.id.equals(v.feedSourceId)) + .collect(Collectors.toCollection(ArrayList::new)); } @JsonView(JsonViews.UserInterface.class) @@ -404,7 +399,7 @@ public int getNoteCount() { /** * Represents ways feeds can be retrieved */ - public static enum FeedRetrievalMethod { + public enum FeedRetrievalMethod { FETCHED_AUTOMATICALLY, // automatically retrieved over HTTP on some regular basis MANUALLY_UPLOADED, // manually uploaded by someone, perhaps the agency, or perhaps an internal user PRODUCED_IN_HOUSE // produced in-house in a GTFS Editor instance @@ -418,9 +413,7 @@ public static void commit() { * Delete this feed source and everything that it contains. */ public void delete() { - for (FeedVersion v : getFeedVersions()) { - v.delete(); - } + getFeedVersions().forEach(FeedVersion::delete); // Delete editor feed mapdb // TODO: does the mapdb folder need to be deleted separately? @@ -433,11 +426,9 @@ public void delete() { gtx.commit(); } - for (ExternalFeedSourceProperty prop : ExternalFeedSourceProperty.getAll()) { - if(prop.getFeedSourceId().equals(this.id)) { - prop.delete(); - } - } + ExternalFeedSourceProperty.getAll().stream() + .filter(prop -> prop.getFeedSourceId().equals(this.id)) + .forEach(ExternalFeedSourceProperty::delete); // TODO: add delete for osm extract and r5 network (maybe that goes with version) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index 0e978ba9d..dfec29438 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -3,9 +3,7 @@ import java.awt.geom.Rectangle2D; import java.io.File; -import java.io.FileFilter; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -20,42 +18,30 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import com.amazonaws.AmazonServiceException; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.GetObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.model.S3Object; -import com.conveyal.datatools.common.status.StatusEvent; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.controllers.api.GtfsApiController; import com.conveyal.datatools.manager.persistence.DataStore; import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.HashUtils; -import com.conveyal.gtfs.GTFSCache; import com.conveyal.gtfs.GTFSFeed; -import com.conveyal.gtfs.api.ApiMain; import com.conveyal.gtfs.validator.json.LoadStatus; import com.conveyal.gtfs.stats.FeedStats; import com.conveyal.r5.point_to_point.builder.TNBuilderConfig; import com.conveyal.r5.transit.TransportNetwork; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.core.JsonGenerationException; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.eventbus.EventBus; import com.vividsolutions.jts.geom.Geometry; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -import org.apache.commons.io.filefilter.WildcardFileFilter; import org.geotools.geojson.geom.GeometryJSON; -import org.mapdb.Fun.Function2; import org.mapdb.Fun.Tuple2; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -81,17 +67,12 @@ public class FeedVersion extends Model implements Serializable { private static ObjectMapper mapper = new ObjectMapper(); public static final Logger LOG = LoggerFactory.getLogger(FeedVersion.class); public static final String validationSubdir = "validation/"; - static DataStore versionStore = new DataStore("feedversions"); + static DataStore versionStore = new DataStore<>("feedversions"); private static FeedStore feedStore = new FeedStore(); static { // set up indexing on feed versions by feed source, indexed by - versionStore.secondaryKey("version", new Function2, String, FeedVersion>() { - @Override - public Tuple2 run(String key, FeedVersion fv) { - return new Tuple2(fv.feedSourceId, fv.version); - } - }); + versionStore.secondaryKey("version", (key, fv) -> new Tuple2(fv.feedSourceId, fv.version)); } /** @@ -174,15 +155,23 @@ public File getGtfsFile() { } public File newGtfsFile(InputStream inputStream) { - File file = feedStore.newFeed(id, inputStream, getFeedSource()); this.fileSize = file.length(); - this.fileTimestamp = file.lastModified(); - this.save(); LOG.info("New GTFS file saved: {}", id); return file; } - + public File newGtfsFile(InputStream inputStream, Long lastModified) { + File file = newGtfsFile(inputStream); + if (lastModified != null) { + this.fileTimestamp = lastModified; + file.setLastModified(lastModified); + } + else { + this.fileTimestamp = file.lastModified(); + } + this.save(); + return file; + } @JsonIgnore public GTFSFeed getGtfsFeed() { String apiId = id.replace(".zip", ""); @@ -224,7 +213,6 @@ public String getFormattedTimestamp() { } public static FeedVersion get(String id) { - // TODO Auto-generated method stub return versionStore.getById(id); } @@ -268,7 +256,7 @@ public void validate(EventBus eventBus) { return; } - Map tripsPerDate = null; + Map tripsPerDate; try { // eventBus.post(new StatusEvent("Validating feed...", 30, false)); @@ -331,10 +319,6 @@ public void validate(EventBus eventBus) { Geometry buffers = gtfsFeed.getMergedBuffers(); validation.put("mergedBuffers", buffers != null ? g.toString(buffers) : null); mapper.writeValue(tempFile, validation); - } catch (JsonGenerationException e) { - e.printStackTrace(); - } catch (JsonMappingException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -410,7 +394,6 @@ private void saveValidationResult(File file) { // upload to S3, if we have bucket name and use s3 storage if(DataManager.feedBucket != null && DataManager.useS3) { - AWSCredentials creds; try { LOG.info("Uploading validation json to S3"); FeedStore.s3Client.putObject(new PutObjectRequest( @@ -486,13 +469,11 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { File osmExtract = getOSMFile(bounds); if (!osmExtract.exists()) { InputStream is = getOsmExtract(this.validationResult.bounds); - OutputStream out = null; + OutputStream out; try { out = new FileOutputStream(osmExtract); IOUtils.copy(is, out); is.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -506,7 +487,7 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { List feedList = new ArrayList<>(); feedList.add(getGtfsFeed()); - TransportNetwork tn = null; + TransportNetwork tn; try { tn = TransportNetwork.fromFeeds(osmExtract.getAbsolutePath(), feedList, TNBuilderConfig.defaultConfig()); } catch (Exception e) { @@ -521,13 +502,11 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { } this.transportNetwork = tn; File tnFile = getTransportNetworkPath(); - OutputStream tnOut = null; + OutputStream tnOut; try { tnOut = new FileOutputStream(tnFile); tn.write(tnOut); return transportNetwork; - } catch (FileNotFoundException e) { - e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } @@ -607,17 +586,18 @@ public Long getFileSize() { */ public void delete() { // reset lastModified if feed is latest version + System.out.println("deleting version"); FeedSource fs = getFeedSource(); FeedVersion latest = fs.getLatest(); - if (latest != null && latest.id == this.id) { + if (latest != null && latest.id.equals(this.id)) { fs.lastFetched = null; fs.save(); } feedStore.deleteFeed(id); - /*for (Deployment d : Deployment.getAll()) { + for (Deployment d : Deployment.getAll()) { d.feedVersionIds.remove(this.id); - }*/ + } versionStore.delete(this.id); } diff --git a/src/main/java/com/conveyal/datatools/manager/models/Model.java b/src/main/java/com/conveyal/datatools/manager/models/Model.java index 618958d26..de918f9e5 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/Model.java +++ b/src/main/java/com/conveyal/datatools/manager/models/Model.java @@ -1,5 +1,6 @@ package com.conveyal.datatools.manager.models; +import com.conveyal.datatools.manager.auth.Auth0Users; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonView; @@ -36,6 +37,8 @@ public Model () { @JsonView(JsonViews.DataDump.class) public String userId; + private String userEmail; + /** * Notes on this object */ @@ -65,13 +68,14 @@ public List getNotes() { */ @JsonView(JsonViews.UserInterface.class) public String getUser () { - return this.userId; + return this.userEmail; } /** * Set the owner of this object */ public void setUser (Auth0UserProfile profile) { userId = profile.getUser_id(); + userEmail = profile.getEmail(); } /** @@ -79,6 +83,8 @@ public void setUser (Auth0UserProfile profile) { */ public void setUserById (String id) { userId = id; + Auth0UserProfile profile = Auth0Users.getUserById(userId); + userEmail = profile != null ? profile.getEmail() : null; } public void addNote(Note n) { From 1c398cf7141e0aa0fd22defceec6f53dd71d37af Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 12:51:54 -0500 Subject: [PATCH 141/323] fix(css, timepicker): fixed issue with timepicker being cut off by side pane --- src/main/client/common/style.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/client/common/style.css b/src/main/client/common/style.css index 417e720f4..91d988f52 100644 --- a/src/main/client/common/style.css +++ b/src/main/client/common/style.css @@ -20,7 +20,13 @@ .entity-list-row { /*background-color: '#f5f5f5';*/ - outline: 'none' + outline: 'none'; +} + +/* IMPORTANT (!) for date picker to show up aligned correctly with form input */ +.bootstrap-datetimepicker-widget.dropdown-menu.bottom { + top: 65px !important; + right: 23px !important; } .noselect { From 63896517714219dbee6d166cef7b7a9c71f92914 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 12:58:33 -0500 Subject: [PATCH 142/323] fix(editor, timetable): allow null input in timetable cell --- src/main/client/common/components/EditableCell.js | 10 ++++++---- src/main/client/manager/reducers/projects.js | 9 ++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/client/common/components/EditableCell.js b/src/main/client/common/components/EditableCell.js index 68e32ddd3..c82f14395 100644 --- a/src/main/client/common/components/EditableCell.js +++ b/src/main/client/common/components/EditableCell.js @@ -2,6 +2,8 @@ import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' import moment from 'moment' +import { TIMETABLE_FORMATS } from '../../editor/util' + export default class EditableCell extends Component { static propTypes = { isEditing: PropTypes.bool @@ -138,6 +140,7 @@ export default class EditableCell extends Component { } } save () { + // for non-time rendering if (!this.props.renderTime) { if (this.state.data !== this.state.originalData) { this.setState({isEditing: false}) @@ -148,12 +151,11 @@ export default class EditableCell extends Component { } } else { let date = moment().startOf('day').format('YYYY-MM-DD') - let momentTime = moment(date + 'T' + this.state.data, ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a'], true) - let value = momentTime.diff(date, 'seconds') - console.log(value) + let momentTime = moment(date + 'T' + this.state.data, TIMETABLE_FORMATS, true) + let value = momentTime.isValid() ? momentTime.diff(date, 'seconds') : null // check for valid time and new value - if (momentTime.isValid() && value !== this.state.data) { + if ((this.state.data === '' || momentTime.isValid()) && value !== this.state.data) { this.setState({data: value, isEditing: false}) this.props.onChange(value) this.props.onStopEditing() diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index 74b7276bf..831902c46 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -48,15 +48,10 @@ const projects = (state = { projectIndex = state.all.findIndex(p => p.id === action.projectId) project = state.all[projectIndex] newState = null - console.log("adding fs to project", state.all[projectIndex]); - - // if project's deployment array is undefined, add it - if(!project.deployments) { - console.log('adding new fs array'); + if (!project.deployments) { newState = update(state, {all: {[projectIndex]: {$merge: {deployments: []}}}}) } - // add new empty feed source to feedSources array deployment = { isCreating: true, @@ -68,7 +63,7 @@ const projects = (state = { case 'REQUESTING_FEEDSOURCE': case 'REQUESTING_FEEDSOURCES': case 'REQUEST_PROJECTS': - return update(state, { isFetching: { $set: true }}) + return update(state, { isFetching: { $set: true } }) case 'RECEIVE_PROJECTS': activeProjectId = state.active ? state.active.id : getConfigProperty('application.active_project') activeIndex = action.projects.findIndex(p => p.id === activeProjectId) From 34f0e29aa9bff918442c4943d63d73438ce3fadd Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 14:54:13 -0500 Subject: [PATCH 143/323] refactor(editor, timetable): refactor some util functions and add time formats --- .../client/editor/components/Timetable.js | 18 +++--- src/main/client/editor/util/gtfs.js | 55 +++++++++---------- src/main/client/editor/util/index.js | 2 + 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/main/client/editor/components/Timetable.js b/src/main/client/editor/components/Timetable.js index 07fc63cd5..73862fdba 100644 --- a/src/main/client/editor/components/Timetable.js +++ b/src/main/client/editor/components/Timetable.js @@ -8,9 +8,7 @@ import scrollbarSize from 'dom-helpers/util/scrollbarSize' import EditableCell from '../../common/components/EditableCell' import Loading from '../../common/components/Loading' -import { isTimeFormat } from '../util' - -const DT_FORMATS = ['YYYY-MM-DDTHH:mm:ss', 'YYYY-MM-DDTh:mm:ss a', 'YYYY-MM-DDTh:mm a'] +import { isTimeFormat, TIMETABLE_FORMATS } from '../util' export default class Timetable extends Component { static propTypes = { @@ -52,7 +50,7 @@ export default class Timetable extends Component { } parseTime (timeString) { const date = moment().startOf('day').format('YYYY-MM-DD') - return moment(date + 'T' + timeString, DT_FORMATS).diff(date, 'seconds') + return moment(date + 'T' + timeString, TIMETABLE_FORMATS).diff(date, 'seconds') } handlePastedRows (pastedRows, rowIndex, colIndex) { let newRows = [...this.props.data] @@ -105,6 +103,10 @@ export default class Timetable extends Component { handleCellClick (rowIndex, columnIndex) { this.setState({scrollToColumn: columnIndex, scrollToRow: rowIndex}) } + cellValueInvalid (col, value, previousValue) { + // TRUE if value is invalid + return isTimeFormat(col.type) && value >= 0 && value < previousValue + } _cellRenderer ({ columnIndex, key, rowIndex, scrollToColumn, scrollToRow, style }) { const isFocused = columnIndex === scrollToColumn && rowIndex === scrollToRow const isEditing = this.state.activeCell === `${rowIndex}-${columnIndex}` @@ -121,7 +123,7 @@ export default class Timetable extends Component { val = objectPath.get(this.props.data[rowIndex], 'id') !== 'new' ? objectPath.get(this.props.data[rowIndex], 'id') : null } let previousValue = previousCol && row && objectPath.get(row, previousCol.key) - const isInvalid = isTimeFormat(col.type) && val >= 0 && val < previousValue + const isInvalid = isTimeFormat(col.type) && val >= 0 && val < previousValue && val !== null return ( {/* Top Header Row */} { +export function isNew (entity) { return entity.id === 'new' || typeof entity.id === 'undefined' } -export const getEntityBounds = (entity, offset = 0.005) => { +export function getEntityBounds (entity, offset = 0.005) { if (!entity) return null // [lng, lat] if (entity.constructor === Array) { return latLngBounds([[entity[1] + offset, entity[0] - offset], [entity[1] - offset, entity[0] + offset]]) - } - // stop - else if (typeof entity.stop_lat !== 'undefined') { + } else if (typeof entity.stop_lat !== 'undefined') { + // stop return latLngBounds([[entity.stop_lat + offset, entity.stop_lon - offset], [entity.stop_lat - offset, entity.stop_lon + offset]]) - } - // route - else if (typeof entity.tripPatterns !== 'undefined') { + } else if (typeof entity.tripPatterns !== 'undefined') { + // route let coordinates = [] entity.tripPatterns.map(pattern => { if (pattern.shape && pattern.shape.coordinates) { @@ -33,14 +31,13 @@ export const getEntityBounds = (entity, offset = 0.005) => { } }) return latLngBounds(coordinates) - } - // trip pattern - else if (typeof entity.shape !== 'undefined') { + } else if (typeof entity.shape !== 'undefined') { + // trip pattern return latLngBounds(entity.shape.coordinates.map(c => ([c[1], c[0]]))) } } -export const getEntityName = (component, entity) => { +export function getEntityName (component, entity) { if (!entity) { return '[Unnamed]' } @@ -76,16 +73,16 @@ export const getEntityName = (component, entity) => { : entity.route_long_name && entity.route_long_name !== '""' ? entity.route_long_name : entity.route_id || '[no name]' - case 'description': - return `${entity.service_id} (${entity.description})` + case 'description': + return `${entity.service_id} (${entity.description})` default: return entity[nameKey] || '[no name]' } } -export const getAbbreviatedStopName = (stop, maxCharactersPerWord = 10) => { +export function getAbbreviatedStopName (stop, maxCharactersPerWord = 10) { let stopName = getEntityName('stop', stop) - let stopNameParts = stopName ? stopName.split(/(\band\b|&|@|:)+/i) : null + let stopNameParts = stopName ? stopName.split(/(\band\b|&|@|:|\+)+/i) : null return stopNameParts && stopNameParts.length === 3 && stop.stop_name.length > maxCharactersPerWord * 2 ? `${stopNameParts[0].substr(0, maxCharactersPerWord).trim()}... ${stopNameParts[2].substr(0, maxCharactersPerWord).trim()}` : stop.stop_name @@ -143,7 +140,7 @@ export const gtfsIcons = [ } ] -export const getControlPoints = (pattern, snapToStops) => { +export function getControlPoints (pattern, snapToStops) { if (!pattern) { return [] } @@ -186,23 +183,23 @@ export function getRouteNameAlerts (route) { export function getRouteName (route) { let name = '' - if(route.route_short_name) { + if (route && route.route_short_name) { name += route.route_short_name - if(route.route_long_name) { + if (route.route_long_name) { name += ' - ' } } - if(route.route_long_name) { + if (route && route.route_long_name) { name += route.route_long_name } - if (route.route_id && !route.route_long_name && !route.route_short_name) { + if (route && route.route_id && !route.route_long_name && !route.route_short_name) { name += route.route_id } return name } -export const stopToGtfs = (s) => { +export function stopToGtfs (s) { return { // datatools props id: s.id, @@ -228,7 +225,7 @@ export const stopToGtfs = (s) => { } } -export const stopFromGtfs = (stop) => { +export function stopFromGtfs (stop) { return { gtfsStopId: stop.stop_id, stopCode: stop.stop_code, @@ -247,11 +244,11 @@ export const stopFromGtfs = (stop) => { pickupType: stop.pickupType, dropOffType: stop.dropOffType, feedId: stop.feedId, - id: isNew(stop) ? null : stop.id, + id: isNew(stop) ? null : stop.id } } -export const routeToGtfs = route => { +export function routeToGtfs (route) { return { // datatools props id: route.id, @@ -274,7 +271,7 @@ export const routeToGtfs = route => { } } -export const agencyToGtfs = agency => { +export function agencyToGtfs (agency) { return { // datatools props id: agency.id, @@ -293,7 +290,7 @@ export const agencyToGtfs = agency => { } } -export const calendarToGtfs = cal => { +export function calendarToGtfs (cal) { return { // datatools props id: cal.id, @@ -316,7 +313,7 @@ export const calendarToGtfs = cal => { } } -export const fareToGtfs = fare => { +export function fareToGtfs (fare) { return { // datatools props id: fare.id, @@ -334,7 +331,7 @@ export const fareToGtfs = fare => { } } -export const gtfsSort = (a, b) => { +export function gtfsSort (a, b) { const radix = 10 var aName = getEntityName(null, a) var bName = getEntityName(null, b) diff --git a/src/main/client/editor/util/index.js b/src/main/client/editor/util/index.js index 6feb9cf4e..2c7e3be79 100644 --- a/src/main/client/editor/util/index.js +++ b/src/main/client/editor/util/index.js @@ -1,6 +1,8 @@ import { getAbbreviatedStopName } from '../util/gtfs' export const CLICK_OPTIONS = ['DRAG_HANDLES', 'ADD_STOP_AT_CLICK', 'ADD_STOPS_AT_INTERVAL', 'ADD_STOPS_AT_INTERSECTIONS'] +export const YEAR_FORMAT = 'YYYY-MM-DD' +export const TIMETABLE_FORMATS = ['HH:mm:ss', 'h:mm:ss a', 'h:mm:ssa', 'h:mm a', 'h:mma', 'h:mm', 'HHmm', 'hmm', 'HH:mm'].map(format => `YYYY-MM-DDT${format}`) export function isTimeFormat (type) { return /TIME/.test(type) From e4a50327b57a62169748c6ecc96092151a9432eb Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 14:55:37 -0500 Subject: [PATCH 144/323] refactor(editor): refactor trip pattern list, make files much, much smaller --- .../components/CalculateDefaultTimesForm.js | 69 ++ .../client/editor/components/CalendarList.js | 251 ---- .../editor/components/EditSchedulePanel.js | 72 ++ .../editor/components/EditShapePanel.js | 207 ++++ .../client/editor/components/EditorMap.js | 94 +- .../client/editor/components/EntityDetails.js | 1104 ++++++++--------- .../editor/components/PatternStopsPanel.js | 146 +++ .../editor/components/TripPatternList.js | 644 +--------- .../components/TripPatternListControls.js | 84 ++ .../editor/components/TripPatternViewer.js | 32 + .../editor/containers/ActiveGtfsEditor.js | 4 +- .../containers/ActiveTripPatternList.js | 8 +- 12 files changed, 1277 insertions(+), 1438 deletions(-) create mode 100644 src/main/client/editor/components/CalculateDefaultTimesForm.js delete mode 100644 src/main/client/editor/components/CalendarList.js create mode 100644 src/main/client/editor/components/EditSchedulePanel.js create mode 100644 src/main/client/editor/components/EditShapePanel.js create mode 100644 src/main/client/editor/components/PatternStopsPanel.js create mode 100644 src/main/client/editor/components/TripPatternListControls.js create mode 100644 src/main/client/editor/components/TripPatternViewer.js diff --git a/src/main/client/editor/components/CalculateDefaultTimesForm.js b/src/main/client/editor/components/CalculateDefaultTimesForm.js new file mode 100644 index 000000000..18fd5eb6a --- /dev/null +++ b/src/main/client/editor/components/CalculateDefaultTimesForm.js @@ -0,0 +1,69 @@ +import React, { Component } from 'react' +import { Icon } from '@conveyal/woonerf' +import { Button, FormGroup, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' + +import MinuteSecondInput from './MinuteSecondInput' + +const DEFAULT_SPEED = 20 // km/hr + +export default class CalculateDefaultTimesForm extends Component { + constructor (props) { + super(props) + this.state = {} + } + calculateDefaultTimes (pattern, speed = DEFAULT_SPEED, dwellTime = 0) { + if (!speed) { + speed = DEFAULT_SPEED + } + let patternStops = [...pattern.patternStops] + let convertedSpeed = speed * 1000 / 60 / 60 // km/hr -> m/s + for (var i = 0; i < patternStops.length; i++) { + patternStops[i].defaultDwellTime = dwellTime + patternStops[i].defaultTravelTime = patternStops[i].shapeDistTraveled / convertedSpeed + } + this.props.updateActiveEntity(pattern, 'trippattern', {patternStops}) + this.props.saveActiveEntity('trippattern') + } + render () { + const { activePattern } = this.props + return ( +
    + + Dwell time + { + this.setState({dwellTime: value}) + }} + /> + + {' '} + + { + this.setState({speed: evt.target.value}) + }} + /> + + + + + + ) + } +} diff --git a/src/main/client/editor/components/CalendarList.js b/src/main/client/editor/components/CalendarList.js deleted file mode 100644 index ebe344209..000000000 --- a/src/main/client/editor/components/CalendarList.js +++ /dev/null @@ -1,251 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Table, ListGroup, ListGroupItem, Button, ButtonToolbar, Nav, NavItem } from 'react-bootstrap' -import {Icon} from '@conveyal/woonerf' -import { browserHistory, Link } from 'react-router' -import { LinkContainer } from 'react-router-bootstrap' - -import EditableTextField from '../../common/components/EditableTextField' -import { getConfigProperty } from '../../common/util/config' -import EntityDetails from './EntityDetails' -import GtfsTable from './GtfsTable' - -export default class CalendarList extends Component { - - constructor (props) { - super(props) - } - - render () { - // const routes = ['test', 'Route 123', 'Route 456', 'Route 1', 'Route 10'] - console.log('table view', this.props.tableView) - console.log('entity: ', this.props.entity) - console.log('component: ', this.props.activeComponent) - const feedSource = this.props.feedSource - const sidePadding = '5px' - const rowHeight = '37px' - let panelWidth = !this.props.tableView ? '300px' : '100%' - let panelStyle = { - width: panelWidth, - height: '100%', - position: 'absolute', - left: '0px', - // overflowY: 'scroll', - zIndex: 99, - backgroundColor: 'white', - paddingRight: '0px', - paddingLeft: sidePadding - } - const rowStyle = { - cursor: 'pointer' - } - const activeColor = '#F2F2F2' - let entId = this.props.activeComponent === 'calendar' - ? 'service_id' - : this.props.activeComponent === 'route' - ? 'route_id' - : this.props.activeComponent === 'stop' - ? 'stop_id' - : null - let entName = this.props.activeComponent === 'calendar' - ? 'description' - : this.props.activeComponent === 'route' - ? 'route_short_name' - : this.props.activeComponent === 'stop' - ? 'stop_name' - : null - const activeEntity = this.props.entities ? this.props.entities.find(entity => entity.id === this.props.entity) : null - const agencyList = ( -
    -
  • - - - {this.props.entities - ? this.props.entities.map(entity => { - return ( - console.log(e)} - style={rowStyle} - onClick={() => { - if (this.props.entity && this.props.entity === entity.id) browserHistory.push(`/feed/${feedSource.id}/edit/${this.props.activeComponent}`) - else browserHistory.push(`/feed/${feedSource.id}/edit/${this.props.activeComponent}/${entity.id}`) - }} - > - - - ) - }) - : - } - -
    - {this.props.activeComponent !== 'route' - ? entity[entName] - : entity.route_short_name && entity.route_long_name - ? `${entity.route_short_name} - ${entity.route_long_name}` - : entity.route_short_name - ? entity.route_short_name - : entity.route_long_name - ? entity.route_long_name - : entity.route_id - } -
    -
    - ) - - const activeTable = getConfigProperty('modules.editor.spec') - .find(t => t.id === this.props.activeComponent) - const agencyTable = ( - { - this.props.gtfsEntitySelected(type, entity) - }} - getGtfsEntity={(type, id) => { - return this.props.entities.find(ent => ent[entId] === id) - // return this.props.gtfsEntityLookup[`${type}_${id}`] - }} - showHelpClicked={(tableId, fieldName) => { - const helpContent = fieldName - ? getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).fields - .find(f => f.name === fieldName).helpContent - : getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).helpContent - this.refs.page.showInfoModal({ - title: `Help for ${tableId}.txt` + (fieldName ? `: ${fieldName}` : ''), - body: helpContent || '(No help content found for this field)' - }) - }} - newRowsDisplayed={(rows) => { - this.props.newRowsDisplayed(activeTable.id, rows, this.props.feedSource) - }} - /> - ) - let entityDetails = ( - { - this.props.gtfsEntitySelected(type, entity) - }} - getGtfsEntity={(type, id) => { - return this.props.entities.find(ent => ent[entId] === id) - // return this.props.gtfsEntityLookup[`${type}_${id}`] - }} - showHelpClicked={(tableId, fieldName) => { - const helpContent = fieldName - ? getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).fields - .find(f => f.name === fieldName).helpContent - : getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).helpContent - this.refs.page.showInfoModal({ - title: `Help for ${tableId}.txt` + (fieldName ? `: ${fieldName}` : ''), - body: helpContent || '(No help content found for this field)' - }) - }} - /> - ) - let tableViewButton = ( - - ) - return ( -
    -
    -

    - - - - - -

    - {/*tableViewButton*/} -
    - - {!this.props.tableView - ? agencyList - : agencyTable - } - - {this.props.entity - ? entityDetails - : null - } -
    - ) - } -} diff --git a/src/main/client/editor/components/EditSchedulePanel.js b/src/main/client/editor/components/EditSchedulePanel.js new file mode 100644 index 000000000..04878c065 --- /dev/null +++ b/src/main/client/editor/components/EditSchedulePanel.js @@ -0,0 +1,72 @@ +import React, { Component } from 'react' +import {Icon} from '@conveyal/woonerf' +import { Button, FormGroup, ControlLabel, ButtonGroup, DropdownButton, MenuItem } from 'react-bootstrap' + +export default class EditSchedulePanel extends Component { + render () { + const { activePattern, activePatternId, feedSource, activeEntity } = this.props + const timetableOptions = [ + Use timetables, + Use frequencies + ] + return ( +
    +

    Schedules

    + + { + let useFrequency = key !== 'timetables' + let other = key === 'timetables' ? 'frequencies' : 'timetables' + this.props.showConfirmModal({ + title: `Use ${key} for ${activePattern.name}?`, + body: `Are you sure you want to use ${key} for this trip pattern? Any trips created using ${other} will be lost.`, + onConfirm: () => { + console.log('use ' + key) + this.props.updateActiveEntity(activePattern, 'trippattern', {useFrequency}) + this.props.saveActiveEntity('trippattern') + } + }) + }} + title={activePattern.useFrequency ? timetableOptions[1] : timetableOptions[0]} id='frequency-dropdown'> + {activePattern.useFrequency ? timetableOptions[0] : timetableOptions[1]} + + + + Direction + + + + + + +
    + ) + } +} diff --git a/src/main/client/editor/components/EditShapePanel.js b/src/main/client/editor/components/EditShapePanel.js new file mode 100644 index 000000000..56d716c87 --- /dev/null +++ b/src/main/client/editor/components/EditShapePanel.js @@ -0,0 +1,207 @@ +import React, { Component } from 'react' +import {Icon} from '@conveyal/woonerf' +import { Button, Alert, Checkbox, ButtonToolbar, FormGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' +import { sentence as toSentenceCase } from 'change-case' +import Rcslider from 'rc-slider' +import { polyline as getPolyline } from '../../scenario-editor/utils/valhalla' + +import { CLICK_OPTIONS } from '../util' + +export default class EditShapePanel extends Component { + async drawPatternFromStops (pattern, stops) { + let newShape = await getPolyline(stops) + console.log(newShape) + this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: newShape}}) + this.props.saveActiveEntity('trippattern') + return true + } + renderEditSettings (isEditing) { + return isEditing + ? + Edit settings + this.props.updateEditSetting('followStreets', !this.props.editSettings.followStreets)}> + Snap to streets + + this.props.updateEditSetting('snapToStops', !this.props.editSettings.snapToStops)}> + Snap to stops + + this.props.updateEditSetting('hideStops', !this.props.editSettings.hideStops)}> + Show stops + + Editing mode + this.props.updateEditSetting('onMapClick', evt.target.value)} + > + {CLICK_OPTIONS.map(v => { + return + })} + + {this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERVAL' + ?
    + this.props.updateEditSetting('stopInterval', value)} + step={25} + marks={{ + 100: '100m', + 400: 400m, + 800: '800m', + 1600: '1600m' + }} + tipFormatter={(value) => { + return `${value}m (${Math.round(value * 0.000621371 * 100) / 100}mi)` + }} + /> +
    + : this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERSECTIONS' + ?
    + {/* distance from intersection */} + this.props.updateEditSetting('distanceFromIntersection', evt.target.value)} + style={{width: '60px', marginTop: '10px'}} + /> + meters + {/* before/after intersection */} + this.props.updateEditSetting('afterIntersection', +evt.target.value)} + style={{width: '80px', marginTop: '10px'}} + > + + + + every + {/* every n intersections */} + this.props.updateEditSetting('intersectionStep', evt.target.value)} + style={{width: '55px', marginTop: '10px'}} + /> + intersections + + : null + } + {this.props.editSettings.onMapClick.includes('ADD_') + ? + Warning! This editing mode creates new stops. Unless no existing stops are nearby, this mode is not recommended. + + : null + } +
    + : null + } + renderEditButtons (isEditing, activePattern) { + return isEditing + ? [ + , + , + + ] + : [ + , + , + + ] + } + render () { + const { activePattern } = this.props + return ( +
    +

    + Pattern Shape +

    + + {this.renderEditButtons(this.props.editSettings.editGeometry, activePattern)} + {this.renderEditSettings(this.props.editSettings.editGeometry)} + +
    + ) + } +} diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 49219db8c..869094672 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -117,7 +117,7 @@ export default class EditorMap extends Component { !shallowEqual(nextProps.zoomToTarget, this.props.zoomToTarget) || !shallowEqual(nextProps.editSettings, this.props.editSettings) || !shallowEqual(nextProps.subEntityId, this.props.subEntityId) || - !shallowEqual(nextProps.currentPattern, this.props.currentPattern) || + !shallowEqual(nextProps.currentPattern, this.props.activePattern) || // nextProps.activeComponent === 'stop' && this.props.mapState.zoom !== nextProps.mapState.zoom || nextProps.activeComponent === 'stop' && this.props.activeEntity && nextProps.activeEntity && (this.props.activeEntity.stop_lon !== nextProps.activeEntity.stop_lon || this.props.activeEntity.stop_lat !== nextProps.activeEntity.stop_lat) || nextProps.activeEntityId !== this.props.activeEntityId || @@ -212,14 +212,14 @@ export default class EditorMap extends Component { this.props.saveActiveEntity('trippattern') } handleControlPointDragEnd (e, timer, controlPointRef, controlPointIndex) { - let patternGeojson = this.refs[this.props.currentPattern.id].leafletElement.toGeoJSON() + let patternGeojson = this.refs[this.props.activePattern.id].leafletElement.toGeoJSON() // snap control point to line let controlPointLocation = e.target.toGeoJSON() let snapPoint = pointOnLine(patternGeojson, controlPointLocation) this.refs[controlPointRef].leafletElement.setLatLng(ll.toLatlng(snapPoint.geometry.coordinates)) - this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {shape: {type: 'LineString', coordinates: patternGeojson.geometry.coordinates}}) + this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {shape: {type: 'LineString', coordinates: patternGeojson.geometry.coordinates}}) if (typeof controlPointIndex !== 'undefined') { let lineSegment = lineSlice(point(patternGeojson.geometry.coordinates[0]), snapPoint, patternGeojson) @@ -315,7 +315,7 @@ export default class EditorMap extends Component { const gtfsStop = stopToGtfs(s) // add stop to end of pattern if (addToPattern) { - await this.addStopToPattern(this.props.currentPattern, gtfsStop, index) + await this.addStopToPattern(this.props.activePattern, gtfsStop, index) } return gtfsStop } @@ -353,12 +353,12 @@ export default class EditorMap extends Component { break case 'ADD_STOPS_AT_INTERVAL': // create first stop if none exist - if (this.props.currentPattern.patternStops.length === 0) { + if (this.props.activePattern.patternStops.length === 0) { this.addStopAtPoint(e.latlng, true) } else { - let coordinates = this.props.currentPattern.shape && this.props.currentPattern.shape.coordinates - let patternStops = [...this.props.currentPattern.patternStops] - let initialDistance = lineDistance(this.props.currentPattern.shape, 'meters') + let coordinates = this.props.activePattern.shape && this.props.activePattern.shape.coordinates + let patternStops = [...this.props.activePattern.patternStops] + let initialDistance = lineDistance(this.props.activePattern.shape, 'meters') // extend pattern to click point let endPoint @@ -367,8 +367,8 @@ export default class EditorMap extends Component { } else { endPoint = {lng: patternStops[0].stop_lon, lat: patternStops[0].stop_lat} } - const updatedShape = await this.extendPatternToPoint(this.props.currentPattern, endPoint, e.latlng) - let totalDistance = lineDistance(this.props.currentPattern.shape, 'meters') + const updatedShape = await this.extendPatternToPoint(this.props.activePattern, endPoint, e.latlng) + let totalDistance = lineDistance(this.props.activePattern.shape, 'meters') let distanceAdded = totalDistance - initialDistance let numIntervals = distanceAdded / this.props.editSettings.stopInterval const latlngList = [] @@ -391,7 +391,7 @@ export default class EditorMap extends Component { // patternStops = [...patternStops, ...newStops.map(s => this.stopToStopTime(s))] // update and save all new stops to pattern - this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {patternStops: patternStops}) + this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) this.props.saveActiveEntity('trippattern') } break @@ -419,7 +419,7 @@ export default class EditorMap extends Component { } async handlePatternEdit (controlPointRef, begin, end) { let followRoad = this.props.editSettings.followStreets // !e.originalEvent.shiftKey - let leafletPattern = this.refs[this.props.currentPattern.id].leafletElement + let leafletPattern = this.refs[this.props.activePattern.id].leafletElement let originalLatLngs let originalEndPoint let from, to @@ -442,7 +442,7 @@ export default class EditorMap extends Component { originalEndPoint = originalLatLngs[originalLatLngs.length - 1] from = [originalEndPoint.lng, originalEndPoint.lat] } else { // otherwise use the original endpoint - originalLatLngs = this.props.currentPattern.shape.coordinates.map(c => ([c[1], c[0]])) + originalLatLngs = this.props.activePattern.shape.coordinates.map(c => ([c[1], c[0]])) originalEndPoint = originalLatLngs[originalLatLngs.length - 1] from = [originalEndPoint[1], originalEndPoint[0]] // [latLngs[latLngs.length - 1].lng, latLngs[latLngs.length - 1].lat] to = [markerLatLng.lng, markerLatLng.lat] @@ -505,7 +505,7 @@ export default class EditorMap extends Component { let route = entity let bounds = this.refs.map && this.refs.map.leafletElement.getBounds() // get intervals along path for arrow icons - let patternLength = this.props.currentPattern && this.props.currentPattern.shape ? lineDistance(this.props.currentPattern.shape, 'meters') : 0 + let patternLength = this.props.activePattern && this.props.activePattern.shape ? lineDistance(this.props.activePattern.shape, 'meters') : 0 let zoom = this.refs.map ? this.refs.map.leafletElement.getZoom() : 11 let iconInterval = zoom > 15 ? 200 @@ -524,7 +524,7 @@ export default class EditorMap extends Component { let lengthsAlongPattern = [] for (var i = 0; i < Math.floor(patternLength / iconInterval); i++) { let distance = i ? iconInterval * i : iconInterval / 2 - let position = along(this.props.currentPattern.shape, distance, 'meters') + let position = along(this.props.activePattern.shape, distance, 'meters') if (!bounds) continue if (position.geometry.coordinates[1] > bounds.getNorth() || position.geometry.coordinates[1] < bounds.getSouth() || position.geometry.coordinates[0] > bounds.getEast() || position.geometry.coordinates[0] < bounds.getWest()) { continue @@ -549,7 +549,7 @@ export default class EditorMap extends Component { ? route.tripPatterns .map(tp => { const isActive = this.props.subEntityId === tp.id - let pattern = isActive ? this.props.currentPattern : tp + let pattern = isActive ? this.props.activePattern : tp const latLngs = pattern.shape ? pattern.shape.coordinates.map(c => ([c[1], c[0]])) : [] // skip pattern if latlngs don't exist or some other pattern is active if (!latLngs || !isActive && this.props.subEntityId) { @@ -584,12 +584,12 @@ export default class EditorMap extends Component { ref='directionIcons' key='directionIcons' > - {lengthsAlongPattern.length && this.refs[this.props.currentPattern.id] // && false + {lengthsAlongPattern.length && this.refs[this.props.activePattern.id] // && false ? lengthsAlongPattern.map((length, index) => { let distance = length[0] let position = length[1] - let nextPosition = along(this.props.currentPattern.shape, distance + 5, 'meters') + let nextPosition = along(this.props.activePattern.shape, distance + 5, 'meters') const dir = position && nextPosition ? bearing(position, nextPosition) : 0 const cos = Math.cos(bearing * (Math.PI / 180)) const sin = Math.sin(bearing * (Math.PI / 180)) @@ -622,7 +622,7 @@ export default class EditorMap extends Component { } , - {this.props.stops.length && this.props.currentPattern && this.props.currentPattern.shape && this.props.editSettings.editGeometry + {this.props.stops.length && this.props.activePattern && this.props.activePattern.shape && this.props.editSettings.editGeometry ? this.state.controlPoints.map((s, index) => { // don't include controlPoint on end of segment (for now) or hidden controlPoints if (s.stopId && this.props.editSettings.snapToStops) { @@ -640,7 +640,7 @@ export default class EditorMap extends Component { let begin = prevControlPoint ? prevControlPoint.point - : along(this.props.currentPattern.shape, 0, 'meters') + : along(this.props.activePattern.shape, 0, 'meters') let end if (nextControlPoint) { end = nextControlPoint.point @@ -679,7 +679,7 @@ export default class EditorMap extends Component { console.log('control point clicked', e) // only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop) if (!s.permanent) { - this.removeControlPoint(this.props.currentPattern, index, begin, end) + this.removeControlPoint(this.props.activePattern, index, begin, end) } }} color='black' @@ -689,14 +689,14 @@ export default class EditorMap extends Component { }) : null } - {beginPoint && this.props.editSettings.editGeometry && this.props.currentPattern + {beginPoint && this.props.editSettings.editGeometry && this.props.activePattern ? { - let beginStop = this.props.stops.find(s => s.id === this.props.currentPattern.patternStops[0].stopId) + let beginStop = this.props.stops.find(s => s.id === this.props.activePattern.patternStops[0].stopId) let begin = point([beginStop.stop_lon, beginStop.stop_lat]) const timerFunction = () => { this.handlePatternEdit('controlPointBegin', null, begin) @@ -716,11 +716,11 @@ export default class EditorMap extends Component { ref='patternStops' key='patternStops' > - {this.props.stops.length && this.props.currentPattern && !this.props.editSettings.hideStops - ? this.props.currentPattern.patternStops && this.props.currentPattern.patternStops.map((s, index) => { + {this.props.stops.length && this.props.activePattern && !this.props.editSettings.hideStops + ? this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((s, index) => { const stop = this.props.stops.find(ps => ps.id === s.stopId) if (!stop - // || this.props.mapState.zoom <= 11 && index > 0 && index < this.props.currentPattern.patternStops.length - 1 + // || this.props.mapState.zoom <= 11 && index > 0 && index < this.props.activePattern.patternStops.length - 1 ) return null const patternStopIcon = divIcon({ html: ` @@ -748,8 +748,8 @@ export default class EditorMap extends Component { // onClick={(e) => { // // }} - ref={`${this.props.currentPattern.id}-${s.stopId}-${index}`} - key={`${this.props.currentPattern.id}-${s.stopId}-${index}`} + ref={`${this.props.activePattern.id}-${s.stopId}-${index}`} + key={`${this.props.activePattern.id}-${s.stopId}-${index}`} >
    { - this.removeStopFromPattern(this.props.currentPattern, stop, index) + this.removeStopFromPattern(this.props.activePattern, stop, index) }} > @@ -790,26 +790,26 @@ export default class EditorMap extends Component { { - this.addStopToPattern(this.props.currentPattern, stop, key) + this.addStopToPattern(this.props.activePattern, stop, key) }} > - = this.props.currentPattern.patternStops.length - 2} value={this.props.currentPattern.patternStops.length} eventKey={this.props.currentPattern.patternStops.length}> + = this.props.activePattern.patternStops.length - 2} value={this.props.activePattern.patternStops.length} eventKey={this.props.activePattern.patternStops.length}> Add to end (default) - {this.props.currentPattern.patternStops && this.props.currentPattern.patternStops.map((stop, i) => { - let addIndex = this.props.currentPattern.patternStops.length - i - if (index === this.props.currentPattern.patternStops.length - 1 && index === addIndex - 1) { + {this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((stop, i) => { + let addIndex = this.props.activePattern.patternStops.length - i + if (index === this.props.activePattern.patternStops.length - 1 && index === addIndex - 1) { return null } // disable adding stop to current position or directly before/after current position @@ -840,9 +840,9 @@ export default class EditorMap extends Component { { - let patternStops = [...this.props.currentPattern.patternStops] + let patternStops = [...this.props.activePattern.patternStops] patternStops[index].defaultTravelTime = value - this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {patternStops: patternStops}) + this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) }} /> @@ -858,9 +858,9 @@ export default class EditorMap extends Component { { - let patternStops = [...this.props.currentPattern.patternStops] + let patternStops = [...this.props.activePattern.patternStops] patternStops[index].defaultDwellTime = +evt.target.value - this.props.updateActiveEntity(this.props.currentPattern, 'trippattern', {patternStops: patternStops}) + this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) }} /> @@ -880,7 +880,7 @@ export default class EditorMap extends Component { key='addableStops' > { - this.props.stops.length && this.props.currentPattern && this.props.editSettings.addStops && this.props.mapState.zoom > 14 + this.props.stops.length && this.props.activePattern && this.props.editSettings.addStops && this.props.mapState.zoom > 14 ? this.props.stops .filter(stop => { if (!bounds) return false @@ -893,7 +893,7 @@ export default class EditorMap extends Component { }) .map((stop, index) => { if (!stop) return null - let patternStop = this.props.currentPattern.patternStops.find(ps => ps.stopId === stop.id) + let patternStop = this.props.activePattern.patternStops.find(ps => ps.stopId === stop.id) if (patternStop) return null const color = 'blue' const busIcon = divIcon({ @@ -924,17 +924,17 @@ export default class EditorMap extends Component { id={`split-button-basic-${i}`} bsStyle='success' onSelect={(key) => { - this.addStopToPattern(this.props.currentPattern, stop, key) + this.addStopToPattern(this.props.activePattern, stop, key) }} onClick={(e) => { - this.addStopToPattern(this.props.currentPattern, stop) + this.addStopToPattern(this.props.activePattern, stop) }} > - + Add to end (default) - {this.props.currentPattern.patternStops && this.props.currentPattern.patternStops.map((stop, i) => { - let index = this.props.currentPattern.patternStops.length - i + {this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((stop, i) => { + let index = this.props.activePattern.patternStops.length - i return ( {index === 1 ? 'Add to beginning' : `Insert as stop #${index}`} diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 21c07f3ed..381ce6c81 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -119,9 +119,10 @@ export default class EntityDetails extends Component { height: '100%', position: 'absolute', // overflowY: 'scroll', + overflowX: 'visible', top: '0px', left: this.props.offset || '0px', - // zIndex: 99, + zIndex: 2, backgroundColor: '#F2F2F2', paddingRight: '5px', paddingLeft: '5px' @@ -163,8 +164,8 @@ export default class EntityDetails extends Component { // const standardLabel = {toSentenceCase(editorField.split(/_(.+)?/)[1])} ({editorField}) const basicLabel = field.helpContent ? {field.helpContent}}> - {editorField}{field.required ? ' *' : ''} - + {editorField}{field.required ? ' *' : ''} + : {editorField}{field.required ? ' *' : ''} switch (field.inputType) { case 'ID': @@ -191,16 +192,16 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'TEXT': @@ -224,16 +225,16 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'URL': @@ -253,16 +254,16 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ] if (field.name === 'agencyBrandingUrl' || field.name === 'routeBrandingUrl') { @@ -306,16 +307,16 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'GTFS_ZONE': @@ -392,17 +393,17 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = option.value - this.setState({[editorField]: option.value}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = option.value + this.setState({[editorField]: option.value}) + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'LANGUAGE': @@ -421,17 +422,17 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = option.value - this.setState({[editorField]: option.value}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = option.value + this.setState({[editorField]: option.value}) + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'TIME': @@ -441,16 +442,16 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'LATITUDE': @@ -470,18 +471,18 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - // this.setState({[editorField]: evt.target.value}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + // this.setState({[editorField]: evt.target.value}) + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'NUMBER': @@ -500,16 +501,16 @@ export default class EntityDetails extends Component { : '' } > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'DATE': @@ -532,12 +533,10 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} -
    - -
    + {basicLabel} + ) case 'COLOR': @@ -558,15 +557,15 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} - { -
    this.handleClick(editorField)}> -
    -
    - } - {this.state[editorField] - ?
    -
    this.handleClose(editorField) }/> + {basicLabel} + { +
    this.handleClick(editorField)}> +
    +
    + } + {this.state[editorField] + ?
    +
    this.handleClose(editorField)} />
    - : null - } + : null + } ) case 'POSITIVE_INT': @@ -594,18 +593,18 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'POSITIVE_NUM': @@ -615,45 +614,43 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) case 'DAY_OF_WEEK_BOOLEAN': - return ( - [ - - {editorField === 'monday' - ?
    Days of service
    : null - } -
    , - - { - console.log(evt.target.checked) - let props = {} - currentValue = evt.target.checked ? 1 : 0 - props[field.name] = currentValue - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - > - {toSentenceCase(editorField.substr(0, 3))} - - {' '} - - ] - ) + return [ + + {editorField === 'monday' + ?
    Days of service
    : null + } +
    , + + { + console.log(evt.target.checked) + let props = {} + currentValue = evt.target.checked ? 1 : 0 + props[field.name] = currentValue + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + > + {toSentenceCase(editorField.substr(0, 3))} + + {' '} + + ] case 'DROPDOWN': // isNotValid = field.required && (currentValue === null || typeof currentValue === 'undefined') // if (isNotValid) { @@ -762,20 +759,20 @@ export default class EntityDetails extends Component { className={`col-xs-${field.columnWidth}`} // validationState={this.getValidationState()} > - {basicLabel} - { - console.log(input) - let props = {} - let val = input ? input.value : null - props[field.name] = val - this.setState({[editorField]: val}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> + {basicLabel} + { + console.log(input) + let props = {} + let val = input ? input.value : null + props[field.name] = val + this.setState({[editorField]: val}) + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) + }} + /> ) } @@ -803,154 +800,154 @@ export default class EntityDetails extends Component { const fareRulesForm = this.state.editFareRules && this.props.activeEntity ?
    -

    Specify which routes or zones {this.props.activeEntity.fare_id} fare applies to.

    - {this.props.activeEntity && this.props.activeEntity.fareRules.length} rules apply to this fare - - {this.props.activeEntity.fareRules.map((rule, index) => { - let ruleEntity - if (rule.route_id) { - ruleEntity = this.props.tableData.route && this.props.tableData.route.find(r => r.route_id === rule.route_id) - } - return ( - Specify which routes or zones {this.props.activeEntity.fare_id} fare applies to.

    + {this.props.activeEntity && this.props.activeEntity.fareRules.length} rules apply to this fare + + {this.props.activeEntity.fareRules.map((rule, index) => { + let ruleEntity + if (rule.route_id) { + ruleEntity = this.props.tableData.route && this.props.tableData.route.find(r => r.route_id === rule.route_id) + } + return ( + + + + { + let rules = [...this.props.activeEntity.fareRules] + rules[index] = {fare_id: this.props.activeEntity.fare_id} + rules[index].route_id = true + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) + }} > - - - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].route_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - Route - - {' '} - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].origin_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - To/From - - {' '} - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].contains_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - Contains - - - {rule.route_id - ? { - console.log(input) - let rules = [...this.props.activeEntity.fareRules] - rules[index].route_id = input ? input.value : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - /> - : rule.contains_id - ? { - console.log(input) - let rules = [...this.props.activeEntity.fareRules] - rules[index].origin_id = input ? input.value : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - options={zoneOptions} - />, - { + console.log(input) + let rules = [...this.props.activeEntity.fareRules] + rules[index].destination_id = input ? input.value : null + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) + }} + options={zoneOptions} + /> + ] + : null + } + + ) + })} +
    : null let dateMap = {} let allExceptions = [] @@ -1015,90 +1012,90 @@ export default class EntityDetails extends Component { {this.props.activeEntity && this.props.activeEntity.exemplar === 'CUSTOM' ? - Select calendar to run: - { + console.log(input) + let val = input ? input.map(i => i.value) : null + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {customSchedule: val}) + }} + options={this.props.tableData.calendar + ? this.props.tableData.calendar.map(calendar => { + return { + value: calendar.id, + label: calendar.description, + calendar + } + }) + : [] } /> - + : null } {this.props.activeEntity && this.props.activeEntity.exemplar === 'SWAP' ? - Select calendars to add: - { - console.log(input) - let val = input ? input.map(i => i.value) : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {removedService: val}) - }} - options={this.props.tableData.calendar - ? this.props.tableData.calendar - .filter(cal => !this.props.activeEntity.addedService || this.props.activeEntity.addedService.indexOf(cal.id) === -1) - .map(calendar => { - return { - value: calendar.id, - label: calendar.description, - calendar - } - }) - : [] - } - /> - + controlId={`custom`} + className={`col-xs-12`} + // validationState={this.getValidationState()} + > + Select calendars to add: + { + console.log(input) + let val = input ? input.map(i => i.value) : null + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {removedService: val}) + }} + options={this.props.tableData.calendar + ? this.props.tableData.calendar + .filter(cal => !this.props.activeEntity.addedService || this.props.activeEntity.addedService.indexOf(cal.id) === -1) + .map(calendar => { + return { + value: calendar.id, + label: calendar.description, + calendar + } + }) + : [] + } + /> + : null } - On these dates: - {this.props.activeEntity && this.props.activeEntity.dates.length - ? this.props.activeEntity.dates.map((date, index) => { - let isNotValid = false - const dateString = moment(+date).format('YYYYMMDD') - // check if date already exists in this or other exceptions - if (dateMap[dateString] && dateMap[dateString].length > 1) { - validationErrors.push({field: `dates-${index}`, invalid: true}) - isNotValid = true - } - let dateTimeProps = { - mode: 'date', - dateTime: date ? +moment(date) : +moment(), - onChange: (millis) => { - let dates = [...this.props.activeEntity.dates] - dates[index] = +millis - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {dates}) + On these dates: + {this.props.activeEntity && this.props.activeEntity.dates.length + ? this.props.activeEntity.dates.map((date, index) => { + let isNotValid = false + const dateString = moment(+date).format('YYYYMMDD') + // check if date already exists in this or other exceptions + if (dateMap[dateString] && dateMap[dateString].length > 1) { + validationErrors.push({field: `dates-${index}`, invalid: true}) + isNotValid = true } - } - if (!date) { - dateTimeProps.defaultText = 'Please select a date' - } - return ( -
    - - - {isNotValid - ? {moment(+date).format('MM/DD/YYYY')} appears in another schedule exception. Please choose another date. - : null + let dateTimeProps = { + mode: 'date', + dateTime: date ? +moment(date) : +moment(), + onChange: (millis) => { + let dates = [...this.props.activeEntity.dates] + dates[index] = +millis + this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {dates}) } -
    - ) - } - ) + } + if (!date) { + dateTimeProps.defaultText = 'Please select a date' + } + return ( +
    + + + {isNotValid + ? {moment(+date).format('MM/DD/YYYY')} appears in another schedule exception. Please choose another date. + : null + } +
    + ) + } + ) :
    No dates specified
    }
    - +
    @@ -1184,13 +1181,13 @@ export default class EntityDetails extends Component { // : null return entity - ?
    {getInput(rowIndex, field, entity[editorField], (rowIndex * table.fields.length) + colIndex + 1)}
    + ? getInput(rowIndex, field, entity[editorField], (rowIndex * table.fields.length) + colIndex + 1) : null }) }

    * = field is required

    -
    +
    ) let entityName = this.props.activeComponent === 'feedinfo' ? 'Feed Info' : getEntityName(this.props.activeComponent, entity) let icon = gtfsIcons.find(i => i.id === this.props.activeComponent) @@ -1199,17 +1196,17 @@ export default class EntityDetails extends Component { // console.log(validationErrors) const subNav = this.props.activeComponent === 'route' ? + + : this.props.activeComponent === 'fare' ? + { + this.setState({editFareRules: false}) + }} + > + Attributes + + { + this.setState({editFareRules: true}) + }} + > + Rules + + : null const header = ( -
    - - {this.props.activeComponent === 'stop' || this.props.activeComponent === 'route' - ? Zoom to {this.props.activeComponent}}> +
    + + {this.props.activeComponent === 'stop' || this.props.activeComponent === 'route' + ? Zoom to {this.props.activeComponent}}> - : null - } - Undo changes}> - - - Save changes}> - - - - {this.props.activeComponent === 'route' && entity - ?
    + : null + } + Undo changes}> + + + Save changes}> + + + + {this.props.activeComponent === 'route' && entity + ?
    - : iconName - ?
    + : iconName + ?
    - // schedule exception icon if no icon founds - :
    + // schedule exception icon if no icon founds + :
    - } - {' '} - - { - `${entityName && entityName.length > 18 ? entityName.substr(0, 18) + '...' : entityName}` } - -
    + {' '} + + { + `${entityName && entityName.length > 18 ? entityName.substr(0, 18) + '...' : entityName}` + } + +
    ) return ( @@ -1343,15 +1340,15 @@ export default class EntityDetails extends Component { > {!entity ?
    -

    - -

    -
    +

    + +

    +
    : [
    , -
    +
    ,
    {this.props.subComponent === 'trippattern' ? + showConfirmModal={this.props.showConfirmModal} + /> : this.state.editFareRules ? fareRulesForm : this.props.activeComponent === 'scheduleexception' diff --git a/src/main/client/editor/components/PatternStopsPanel.js b/src/main/client/editor/components/PatternStopsPanel.js new file mode 100644 index 000000000..4f9f4a92c --- /dev/null +++ b/src/main/client/editor/components/PatternStopsPanel.js @@ -0,0 +1,146 @@ +import React, { Component } from 'react' +import {Icon} from '@conveyal/woonerf' +import { Button, ButtonToolbar } from 'react-bootstrap' +import { polyline as getPolyline, getSegment } from '../../scenario-editor/utils/valhalla' +import ll from 'lonlng' + +import PatternStopContainer from './PatternStopContainer' +import VirtualizedEntitySelect from './VirtualizedEntitySelect' + +export default class PatternStopsPanel extends Component { + async extendPatternToStop (pattern, endPoint, stop) { + let newShape = await getPolyline([endPoint, stop]) + if (newShape) { + this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]}}) + this.props.saveActiveEntity('trippattern') + return true + } else { + this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: [...pattern.shape.coordinates, ll.toCoordinates(stop)]}}) + this.props.saveActiveEntity('trippattern') + return false + } + } + async drawPatternFromStops (pattern, stops) { + let newShape = await getPolyline(stops) + console.log(newShape) + this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: newShape}}) + this.props.saveActiveEntity('trippattern') + return true + } + addStopFromSelect (input, activePattern) { + let patternStops = [...activePattern.patternStops] + let stop = input.entity + let coordinates = activePattern.shape && activePattern.shape.coordinates + let newStop = {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} + // if adding stop to end + if (typeof index === 'undefined') { + // if shape coordinates already exist, just extend them + if (coordinates) { + let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) + this.extendPatternToStop(activePattern, endPoint, {lng: stop.stop_lon, lat: stop.stop_lat}) + .then(() => { + patternStops.push(newStop) + this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + this.props.saveActiveEntity('trippattern') + }) + } else { + // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) + patternStops.push(newStop) + if (patternStops.length > 1) { + let previousStop = this.props.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) + getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], this.props.editSettings.followStreets) + .then(geojson => { + this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}}) + this.props.saveActiveEntity('trippattern') + }) + } else { + this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + this.props.saveActiveEntity('trippattern') + } + } + // if not following roads + // updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) + } + } + render () { + const { activePattern } = this.props + const cardStyle = { + border: '1px dashed gray', + padding: '0.5rem 0.5rem', + marginBottom: '.5rem', + backgroundColor: '#f2f2f2', + cursor: 'pointer' + } + return ( +
    +

    + + + + {this.props.editSettings.addStops && this.props.mapState.zoom <= 14 + ? Zoom to view stops + : null + } + Stops +

    + {/* List of pattern stops */} +
    +

    Stop sequence

    +
    +
    +

    Travel time

    +
    +
    + + {/* Add stop selector */} + {this.props.editSettings.addStops + ?
    + this.addStopFromSelect(input, activePattern)} + /> +
    + +
    +
    + :
    +

    this.props.updateEditSetting('addStops', !this.props.editSettings.addStops)} + className='small' + > + Add stop +

    +
    + } +
    + ) + } +} diff --git a/src/main/client/editor/components/TripPatternList.js b/src/main/client/editor/components/TripPatternList.js index 0da29fa29..fbd530c7c 100644 --- a/src/main/client/editor/components/TripPatternList.js +++ b/src/main/client/editor/components/TripPatternList.js @@ -1,20 +1,10 @@ import React, {Component, PropTypes} from 'react' -import { Table, Button, ButtonGroup, Alert, Checkbox, DropdownButton, MenuItem, ButtonToolbar, Collapse, FormGroup, OverlayTrigger, Tooltip, InputGroup, Form, FormControl, ControlLabel } from 'react-bootstrap' +import { Table, Collapse } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' -import { sentence as toSentenceCase } from 'change-case' -import Rcslider from 'rc-slider' -import EditableTextField from '../../common/components/EditableTextField' import Loading from '../../common/components/Loading' -import PatternStopContainer from './PatternStopContainer' -import MinuteSecondInput from './MinuteSecondInput' - -import { CLICK_OPTIONS } from '../util' -import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import { polyline as getPolyline, getSegment } from '../../scenario-editor/utils/valhalla' -import ll from 'lonlng' - -const DEFAULT_SPEED = 20 // km/hr +import TripPatternViewer from './TripPatternViewer' +import TripPatternListControls from './TripPatternListControls' export default class TripPatternList extends Component { static propTypes = { @@ -43,60 +33,62 @@ export default class TripPatternList extends Component { subEntityId: PropTypes.string, currentPattern: PropTypes.object } - constructor (props) { - super(props) - this.state = { - // avgSpeed: 20, // km/hr - // dwellTime: 0 // seconds - } - } - async extendPatternToStop (pattern, endPoint, stop) { - let newShape = await getPolyline([endPoint, stop]) - if (newShape) { - this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]}}) - this.props.saveActiveEntity('trippattern') - return true - } else { - this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: [...pattern.shape.coordinates, ll.toCoordinates(stop)]}}) - this.props.saveActiveEntity('trippattern') - return false - } - } componentWillReceiveProps (nextProps) { } shouldComponentUpdate (nextProps) { return true } - async drawPatternFromStops (pattern, stops) { - let newShape = await getPolyline(stops) - console.log(newShape) - this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: newShape}}) - this.props.saveActiveEntity('trippattern') - return true - } - calculateDefaultTimes (pattern, speed = DEFAULT_SPEED, dwellTime = 0) { - console.log(speed, dwellTime) - if (!speed) { - speed = DEFAULT_SPEED + renderPatternRow (pattern, patternId, route) { + const rowStyle = { + paddingTop: 5, + paddingBottom: 5 } - let patternStops = [...pattern.patternStops] - let convertedSpeed = speed * 1000 / 60 / 60 // km/hr -> m/s - // let cumulativeTravelTime = 0 - for (var i = 0; i < patternStops.length; i++) { - patternStops[i].defaultDwellTime = dwellTime - patternStops[i].defaultTravelTime = patternStops[i].shapeDistTraveled / convertedSpeed - // cumulativeTravelTime += dwellTime + + const activeColor = '#fff' + const activeRowStyle = { + backgroundColor: activeColor, + paddingTop: 5, + paddingBottom: 5 } - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops}) - this.props.saveActiveEntity('trippattern') + const isActive = patternId && pattern.id === patternId + + const patternName = `${`${pattern.name.length > 35 ? pattern.name.substr(0, 35) + '...' : pattern.name}`} ${pattern.patternStops ? `(${pattern.patternStops.length} stops)` : ''}` + return ( + + +

    this._selectRow(isActive, route, pattern)} + > + + {' '} + {pattern.name ? patternName : '[Unnamed]'} +

    + + {isActive + ? + :
    + } + + + + ) + } + _selectRow (isActive, route, pattern) { + if (isActive) this.props.setActiveEntity(this.props.feedSource.id, 'route', route, 'trippattern') + else this.props.setActiveEntity(this.props.feedSource.id, 'route', route, 'trippattern', pattern) } render () { - const { feedSource, activeEntity } = this.props - const activePatternId = this.props.subEntityId + const { activeEntity, activePatternId } = this.props if (!activeEntity.tripPatterns) { - return + return } - const activePattern = this.props.currentPattern // activeEntity.tripPatterns.find(p => p.id === activePatternId) const sidePadding = '5px' let panelWidth = '300px' let panelStyle = { @@ -106,537 +98,29 @@ export default class TripPatternList extends Component { left: '0px', overflowY: 'scroll', zIndex: 99, - // backgroundColor: 'white', paddingRight: '0px', paddingLeft: sidePadding } - const activeColor = '#fff' - const patternList = ( -
    - - - - {activeEntity.tripPatterns ? activeEntity.tripPatterns.map(pattern => { - const rowStyle = { - paddingTop: 5, - paddingBottom: 5 - } - const activeRowStyle = { - backgroundColor: activeColor, - paddingTop: 5, - paddingBottom: 5 - } - const cardStyle = { - border: '1px dashed gray', - padding: '0.5rem 0.5rem', - marginBottom: '.5rem', - backgroundColor: '#f2f2f2', - cursor: 'pointer' - } - const isActive = activePatternId && pattern.id === activePatternId - const timetableOptions = [ - Use timetables, - Use frequencies - ] - const patternName = `${`${pattern.name.length > 35 ? pattern.name.substr(0, 35) + '...' : pattern.name}`} ${pattern.patternStops ? `(${pattern.patternStops.length} stops)` : ''}` - return ( - - - - ) - }) - : - } - -
    -

    { - if (isActive) this.props.setActiveEntity(feedSource.id, 'route', activeEntity, 'trippattern') - else this.props.setActiveEntity(feedSource.id, 'route', activeEntity, 'trippattern', pattern) - }} - > - - {' '} - {pattern.name ? patternName : '[Unnamed]'} -

    - - {isActive - ?
    - { - let props = {} - props.name = value - this.props.updateActiveEntity(pattern, 'trippattern', props) - this.props.saveActiveEntity('trippattern') - }} - /> -
    -

    - Pattern Shape -

    - - {this.props.editSettings.editGeometry - ? [ - , - , - - ] - : [ - , - , - - ] - } - {this.props.editSettings.editGeometry - ? - Edit settings - this.props.updateEditSetting('followStreets', !this.props.editSettings.followStreets)}> - Snap to streets - - this.props.updateEditSetting('snapToStops', !this.props.editSettings.snapToStops)}> - Snap to stops - - this.props.updateEditSetting('hideStops', !this.props.editSettings.hideStops)}> - Show stops - - Editing mode - this.props.updateEditSetting('onMapClick', evt.target.value)} - > - {CLICK_OPTIONS.map(v => { - return - })} - - {this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERVAL' - ?
    - this.props.updateEditSetting('stopInterval', value)} - step={25} - marks={{ - 100: '100m', - 400: 400m, - 800: '800m', - 1600: '1600m' - }} - tipFormatter={(value) => { - return `${value}m (${Math.round(value * 0.000621371 * 100) / 100}mi)` - }} - /> -
    - : this.props.editSettings.onMapClick === 'ADD_STOPS_AT_INTERSECTIONS' - ?
    - {/* distance from intersection */} - this.props.updateEditSetting('distanceFromIntersection', evt.target.value)} - style={{width: '60px', marginTop: '10px'}} - /> - meters - {/* before/after intersection */} - this.props.updateEditSetting('afterIntersection', +evt.target.value)} - style={{width: '80px', marginTop: '10px'}} - > - - - - every - {/* every n intersections */} - this.props.updateEditSetting('intersectionStep', evt.target.value)} - style={{width: '55px', marginTop: '10px'}} - /> - intersections - - : null - } - {this.props.editSettings.onMapClick.includes('ADD_') - ? - Warning! This editing mode creates new stops. Unless no existing stops are nearby, this mode is not recommended. - - : null - } -
    - : null - } -
    -
    -

    Schedules

    - - { - let useFrequency = key !== 'timetables' - let other = key === 'timetables' ? 'frequencies' : 'timetables' - this.props.showConfirmModal({ - title: `Use ${key} for ${activePattern.name}?`, - body: `Are you sure you want to use ${key} for this trip pattern? Any trips created using ${other} will be lost.`, - onConfirm: () => { - console.log('use ' + key) - this.props.updateActiveEntity(activePattern, 'trippattern', {useFrequency}) - this.props.saveActiveEntity('trippattern') - } - }) - }} - title={activePattern.useFrequency ? timetableOptions[1] : timetableOptions[0]} id='frequency-dropdown'> - {activePattern.useFrequency ? timetableOptions[0] : timetableOptions[1]} - - - -
    -

    - - - - {this.props.editSettings.addStops && this.props.mapState.zoom <= 14 - ? Zoom to view stops - : null - } - Stops -

    - {/* List of pattern stops */} -
    -

    Stop sequence

    -
    -
    -

    Travel time

    -
    -
    - - {/* Add stop selector */} - {this.props.editSettings.addStops - ?
    - { - let patternStops = [...activePattern.patternStops] - let stop = input.entity - let coordinates = activePattern.shape && activePattern.shape.coordinates - let newStop = {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} - // if adding stop to end - if (typeof index === 'undefined') { - // if shape coordinates already exist, just extend them - if (coordinates) { - let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) - this.extendPatternToStop(activePattern, endPoint, {lng: stop.stop_lon, lat: stop.stop_lat}) - .then(() => { - patternStops.push(newStop) - this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - }) - } - // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) - else { - patternStops.push(newStop) - if (patternStops.length > 1) { - let previousStop = this.props.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) - getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], this.props.editSettings.followStreets) - .then(geojson => { - this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}}) - this.props.saveActiveEntity('trippattern') - }) - } - else { - this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - } - } - - // if not following roads - // updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) - } - // if adding stop in middle - else { - // patternStops.splice(index, 0, newStop) - // updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) - // saveActiveEntity('trippattern') - } - // TODO: add strategy for stop at beginning - }} - /> -
    - -
    -
    - :
    -

    { - this.props.updateEditSetting('addStops', !this.props.editSettings.addStops) - }} - className='small' - > - Add stop -

    -
    - } -
    - - Dwell time - { - this.setState({dwellTime: value}) - }} - /> - - {' '} - - { - this.setState({speed: evt.target.value}) - }} - /> - - - - -
    -
    - :
    - } -
    -
    -
    - ) - return ( -
    -
    +
    +
    -

    - - Reverse trip pattern}> - - - Duplicate trip pattern}> - - - Delete trip pattern}> - - - - -

    + + + {activeEntity.tripPatterns + ? activeEntity.tripPatterns.map(pattern => this.renderPatternRow(pattern, activePatternId, activeEntity)) + : + } + +
    - {patternList} -
    ) } diff --git a/src/main/client/editor/components/TripPatternListControls.js b/src/main/client/editor/components/TripPatternListControls.js new file mode 100644 index 000000000..ef4d33a06 --- /dev/null +++ b/src/main/client/editor/components/TripPatternListControls.js @@ -0,0 +1,84 @@ +import React, { Component } from 'react' +import { Icon } from '@conveyal/woonerf' + +import { OverlayTrigger, Tooltip, ButtonToolbar, Button } from 'react-bootstrap' + +export default class TripPatternListControls extends Component { + render () { + const { activePattern, activePatternId, feedSource, activeEntity } = this.props + return ( +
    +

    + + Reverse trip pattern}> + + + Duplicate trip pattern}> + + + Delete trip pattern}> + + + + +

    +
    + ) + } +} diff --git a/src/main/client/editor/components/TripPatternViewer.js b/src/main/client/editor/components/TripPatternViewer.js new file mode 100644 index 000000000..21e22bbd3 --- /dev/null +++ b/src/main/client/editor/components/TripPatternViewer.js @@ -0,0 +1,32 @@ +import React, { Component } from 'react' + +import EditableTextField from '../../common/components/EditableTextField' +import EditShapePanel from './EditShapePanel' +import EditSchedulePanel from './EditSchedulePanel' +import CalculateDefaultTimesForm from './CalculateDefaultTimesForm' +import PatternStopsPanel from './PatternStopsPanel' + +export default class TripPatternViewer extends Component { + savePatternName (name) { + this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {name}) + this.props.saveActiveEntity('trippattern') + } + render () { + const { activePattern } = this.props + return ( +
    + this.savePatternName(value)} + /> +
    + +
    + +
    + + +
    + ) + } +} diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index 3c55c212e..549922b29 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -61,7 +61,7 @@ const mapStateToProps = (state, ownProps) => { : state.editor.active && state.editor.active.entity && activeComponent === 'feedinfo' ? state.editor.active.entity : null - const currentPattern = state.editor.active && state.editor.active.subEntity + const activePattern = state.editor.active && state.editor.active.subEntity // const subEntityId = state.editor.active && state.editor.active.subEntity && state.editor.active.subEntity.id === activeEntityId // ? state.editor.active.subEntity // : state.editor.active && state.editor.active.subEntity && activeComponent === 'feedinfo' @@ -112,7 +112,7 @@ const mapStateToProps = (state, ownProps) => { activeEntity, activeEntityId, subEntityId, - currentPattern, + activePattern, // subEntityIdId, activeSubSubEntity, editSettings, diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js index 3d2f43d2b..6c06676e3 100644 --- a/src/main/client/editor/containers/ActiveTripPatternList.js +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -19,7 +19,7 @@ const mapStateToProps = (state, ownProps) => { const mapState = state.editor.mapState const editSettings = state.editor.editSettings const stops = state.editor.tableData.stop - const currentPattern = state.editor.active.subEntity + const activePattern = state.editor.active.subEntity const feedSourceId = state.editor.active.feedSourceId // find the containing project @@ -28,17 +28,17 @@ const mapStateToProps = (state, ownProps) => { const activeEntity = state.editor.active.entity // const subSubComponent = state.editor.active.subSubComponent - const subEntityId = state.editor.active.subEntityId + const activePatternId = state.editor.active.subEntityId return { mapState, editSettings, stops, - currentPattern, + activePattern, feedSource, activeEntity, // subSubComponent, - subEntityId + activePatternId } } From 48cb0cf1757fbf4c461c1a131d3dc3f07ef121e6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 14:58:04 -0500 Subject: [PATCH 145/323] fix: fix icon for editabletextfield --- .../common/components/EditableTextField.js | 89 ++++++++++--------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/src/main/client/common/components/EditableTextField.js b/src/main/client/common/components/EditableTextField.js index 7f3c89427..f9717f79d 100644 --- a/src/main/client/common/components/EditableTextField.js +++ b/src/main/client/common/components/EditableTextField.js @@ -85,7 +85,7 @@ export default class EditableTextField extends Component { this.save() }}> //feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name @@ -93,51 +93,56 @@ export default class EditableTextField extends Component { const displayValue = this.props.maxLength !== null && this.state.value && this.state.value.length > this.props.maxLength ? this.state.value.substr(0, this.props.maxLength) + '...' : this.state.value + const style = { + ...this.props.style + } + if (this.props.inline) { + style.display = 'inline-block' + } return ( -
    +
    {this.state.isEditing ?
    - - - this.handleKeyDown(e)} - onFocus={(e) => e.target.select()} - defaultValue={ this.state.value } - /> - {saveButton} - - -
    - + + + this.handleKeyDown(e)} + onFocus={(e) => e.target.select()} + defaultValue={this.state.value} + /> + {saveButton} + + + : - {this.props.link - ? {displayValue} - : displayValue || '(none)' - } - {this.props.hideEditButton - ? null - : - {' '} - - - } - + title={this.state.value} + > + {this.props.link + ? {displayValue} + : displayValue || '(none)' + } + {this.props.hideEditButton + ? null + : + {' '} + + + } + }
    ) From 4d7eedba310d509ffb08661981cef0ba25d20212 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 14:58:43 -0500 Subject: [PATCH 146/323] fix(manager): typo in search placeholder on home page --- i18n/english.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/english.yml b/i18n/english.yml index 33d410619..e21a842a2 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -265,7 +265,7 @@ UserHomePage: title: Projects noProjects: You currently do not have any projects. createFirst: Create my first project - search: Search projects + search: Search feeds table: name: Project Name new: New Project From 1eea75c143c44f78ee3c7d6dd17bb5451a9c4bfd Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 15:01:28 -0500 Subject: [PATCH 147/323] fix(manager): move deployment viewer to nest under projectviewer --- .../client/manager/actions/deployments.js | 2 +- .../manager/components/DeploymentViewer.js | 265 +++++------ .../manager/components/FeedSourceTable.js | 318 +++++++------ .../manager/components/ProjectViewer.js | 416 ++++++++++-------- .../client/manager/components/UserHomePage.js | 10 +- .../containers/ActiveDeploymentViewer.js | 32 +- .../manager/containers/ActiveUserHomePage.js | 9 +- 7 files changed, 575 insertions(+), 477 deletions(-) diff --git a/src/main/client/manager/actions/deployments.js b/src/main/client/manager/actions/deployments.js index 1348f3786..4f2a25dc0 100644 --- a/src/main/client/manager/actions/deployments.js +++ b/src/main/client/manager/actions/deployments.js @@ -168,7 +168,7 @@ export function fetchDeploymentTargets () { export function createDeployment (projectId) { return { type: 'CREATE_DEPLOYMENT', - projectId, + projectId } } diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index f5d547dda..f3212ad9d 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -1,162 +1,165 @@ import React, {Component, PropTypes} from 'react' -import Helmet from 'react-helmet' import moment from 'moment' -import moment_tz from 'moment-timezone' -import { Grid, Row, Col, Button, Table, FormControl, Panel, Glyphicon, Badge, ButtonToolbar, DropdownButton, MenuItem, Label } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' +import { LinkContainer } from 'react-router-bootstrap' +import { Row, Col, Button, Table, FormControl, Panel, Glyphicon, Badge, ButtonToolbar, DropdownButton, MenuItem, Label } from 'react-bootstrap' import { Link } from 'react-router' -import ManagerPage from '../../common/components/ManagerPage' -import Breadcrumbs from '../../common/components/Breadcrumbs' +import Loading from '../../common/components/Loading' import EditableTextField from '../../common/components/EditableTextField' -import { versionsSorter, retrievalMethodString } from '../../common/util/util' -import languages from '../../common/util/languages' -import { isModuleEnabled, isExtensionEnabled, getComponentMessages, getMessage } from '../../common/util/config' +import { versionsSorter } from '../../common/util/util' +import { getComponentMessages, getMessage } from '../../common/util/config' export default class DeploymentViewer extends Component { - + static propTypes = { + project: PropTypes.object + } constructor (props) { super(props) - this.state = {} } componentWillMount () { - this.props.onComponentMount(this.props) + // this.props.onComponentMount(this.props) } render () { - - if(!this.props.deployment) { - return + const { project, deployment, feedSources } = this.props + if (!this.props.deployment || !this.props.project || !this.props.feedSources) { + return } - const deployableFeeds = this.props.project.feedSources ? this.props.project.feedSources.filter(fs => - this.props.deployment.feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 && - fs.deployable && - fs.latestValidation - ) : [] + const deployableFeeds = this.props.feedSources + ? this.props.feedSources.filter(fs => + this.props.deployment.feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 && + fs.deployable && + fs.latestValidation + ) + : [] const messages = getComponentMessages('DeploymentViewer') const versions = this.props.deployment.feedVersions.sort(versionsSorter) - - console.log(this.props.deployment) return ( -
    - - -
    - - - {getMessage(messages, 'deploy')} - : {getMessage(messages, 'noServers')} - } - onSelect={(evt) => { - console.log(evt) - this.props.deployToTargetClicked(this.props.deployment, evt) - //setTimeout(() => this.props.getDeploymentStatus(this.props.deployment, evt), 5000) - }} - > - {this.props.project.otpServers - ? this.props.project.otpServers.map(server => ( - {server.name} - )) - : null - } - - -

    - {/* - this.props.deploymentPropertyChanged(this.props.deployment, 'name', value)} - /> - */} - {this.props.deployment.name} - {this.props.deployment.deployedTo - ? - : null - } -

    -
    - -
    - {getMessage(messages, 'versions')})} - collapsible - defaultExpanded={true} - > - - - this.props.searchTextChanged(evt.target.value)} - /> - - +
    + + +
    + + {getMessage(messages, 'addFeedSource')} : {getMessage(messages, 'allFeedsAdded')}} + disabled={!this.props.project.otpServers || !this.props.project.otpServers.length} + title={this.props.project.otpServers && this.props.project.otpServers.length + ? {getMessage(messages, 'deploy')} + : {getMessage(messages, 'noServers')} + } onSelect={(evt) => { console.log(evt) - let feed = deployableFeeds.find(fs => fs.id === evt) - this.props.addFeedVersion(this.props.deployment, {id: feed.latestVersionId}) + this.props.deployToTargetClicked(this.props.deployment, evt) + //setTimeout(() => this.props.getDeploymentStatus(this.props.deployment, evt), 5000) }} > - { - deployableFeeds.map(fs => ( - {fs.name} - )) + {this.props.project.otpServers + ? this.props.project.otpServers.map(server => ( + {server.name} + )) + : null } - - - - - - - - - - - - - - - - - - - - - - {versions.map((version) => { - return - })} - -
    {getMessage(messages, 'table.name')}Version{getMessage(messages, 'table.dateRetrieved')}{getMessage(messages, 'table.loadStatus')}{getMessage(messages, 'table.errorCount')}{getMessage(messages, 'table.routeCount')}{getMessage(messages, 'table.tripCount')}{getMessage(messages, 'table.stopTimesCount')}{getMessage(messages, 'table.validFrom')}{getMessage(messages, 'table.expires')}
    - -
    - -
    + +

    + + + + this.props.deploymentPropertyChanged(this.props.deployment, 'name', value)} + /> + {this.props.deployment.deployedTo + ? + : null + } +

    +
    + +
    + {getMessage(messages, 'versions')})} + collapsible + defaultExpanded={true} + > + + + this.props.searchTextChanged(evt.target.value)} + /> + + + {getMessage(messages, 'addFeedSource')} : {getMessage(messages, 'allFeedsAdded')}} + onSelect={(evt) => { + console.log(evt) + let feed = deployableFeeds.find(fs => fs.id === evt) + this.props.addFeedVersion(this.props.deployment, {id: feed.latestVersionId}) + }} + > + { + deployableFeeds.map(fs => ( + {fs.name} + )) + } + + + + + + + + + + + + + + + + + + + + + + + {versions.map((version) => { + return + })} + +
    {getMessage(messages, 'table.name')}Version{getMessage(messages, 'table.dateRetrieved')}{getMessage(messages, 'table.loadStatus')}{getMessage(messages, 'table.errorCount')}{getMessage(messages, 'table.routeCount')}{getMessage(messages, 'table.tripCount')}{getMessage(messages, 'table.stopTimesCount')}{getMessage(messages, 'table.validFrom')}{getMessage(messages, 'table.expires')}
    + +
    +
    +
    ) } } diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index 682bf985c..286e6c5c6 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -1,9 +1,8 @@ import React, { Component, PropTypes } from 'react' import moment from 'moment' -import { Button, Table, Checkbox, Glyphicon, Dropdown, MenuItem, Panel, ListGroupItem, ListGroup } from 'react-bootstrap' -import { browserHistory, Link } from 'react-router' -import { LinkContainer } from 'react-router-bootstrap' +import { Button, Checkbox, Glyphicon, Dropdown, MenuItem, ListGroupItem, ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap' +import { browserHistory } from 'react-router' import {Icon} from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' @@ -60,21 +59,21 @@ export default class FeedSourceTable extends Component { ? : this.props.feedSources.length ? this.props.feedSources.map((feedSource) => { - return this.setState({activeFeedSource: fs})} - active={this.state.activeFeedSource && this.state.activeFeedSource.id === feedSource.id} - hold={this.state.holdFeedSource && this.state.holdFeedSource.id === feedSource.id} - /> - }) + return this.setState({activeFeedSource: fs})} + active={this.state.activeFeedSource && this.state.activeFeedSource.id === feedSource.id} + hold={this.state.holdFeedSource && this.state.holdFeedSource.id === feedSource.id} + /> + }) : - - + + } ) @@ -103,11 +102,9 @@ class FeedSourceTableRow extends Component { hovered: false } } - shouldComponentUpdate (nextProps, nextState) { return !shallowEqual(nextProps.feedSource, this.props.feedSource) || this.props.active !== nextProps.active } - render () { const fs = this.props.feedSource const na = (N/A) @@ -116,75 +113,47 @@ class FeedSourceTableRow extends Component { const messages = getComponentMessages('ProjectViewer') const feedItem = ( - { - if (fs.isCreating) this.props.saveFeedSource(value) - else this.props.updateFeedSourceProperty(fs, 'name', value) - }} - link={`/feed/${fs.id}`} - /> - {' '} - {!fs.isPublic ? : null} - {' '} - {fs.editedSinceSnapshot - ? - : - } - - } key={fs.id} - // bsStyle={fs.isPublic ? 'default' : 'warning'} - onMouseEnter={() => { - this.props.onHover(fs) - }} + onMouseEnter={() => this.props.onHover(fs)} onMouseLeave={() => { - if (!this.props.hold) + if (!this.props.hold) { this.props.onHover(null) + } }} > - {this.props.active - ? this.props.hoverComponent - : null - } - -
      - {fs.lastUpdated - ?
    • {getMessage(messages, 'feeds.table.lastUpdated')} {moment(fs.lastUpdated).format(dateFormat)}
    • - :
    • No versions exist yet.
    • - } - {fs.latestValidation && fs.latestValidation.errorCount > 0 - ?
    • {fs.latestValidation.errorCount}
    • - : fs.latestValidation - ?
    • - :
    • - } - {fs.latestValidation && fs.latestValidation.endDate < +moment() - ?
    • - : fs.latestValidation - ?
    • - :
    • - } - {isModuleEnabled('deployment') && fs.deployable - ?
    • - : isModuleEnabled('deployment') - ?
    • - : null - } - {fs.url - ?
    • - : null - } -
    +
    + + {this.props.active + ? this.props.hoverComponent + : null + } + +

    + { + if (fs.isCreating) this.props.saveFeedSource(value) + else this.props.updateFeedSourceProperty(fs, 'name', value) + }} + link={`/feed/${fs.id}`} + /> + {' '} + {!fs.isPublic ? : null} + {' '} + {fs.editedSinceSnapshot + ? + : + } +

    + +
    ) const feedRow = ( @@ -250,6 +219,83 @@ class FeedSourceTableRow extends Component { } } +class FeedSourceAttributes extends Component { + render () { + const { feedSource, messages } = this.props + const dateFormat = getConfigProperty('application.date_format') + const hasErrors = feedSource.latestValidation && feedSource.latestValidation.errorCount > 0 + const hasVersion = feedSource.latestValidation + const isExpired = feedSource.latestValidation && feedSource.latestValidation.endDate < +moment() + const end = feedSource.latestValidation && moment(feedSource.latestValidation.endDate) + const endDate = end && end.format(dateFormat) + const timeTo = end && moment().to(end) + return ( +
      + + + + {isModuleEnabled('deployment') + ? + : null + } + {feedSource.url + ? + : null + } +
    + ) + } +} + +class Attribute extends Component { + render () { + const li = ( +
  • + {this.props.icon && } + {this.props.text && ` ${this.props.text}`} +
  • + ) + return this.props.title + ? {this.props.title}}> + {li} + + : li + } +} + class FeedSourceDropdown extends Component { static propTypes = { @@ -262,6 +308,20 @@ class FeedSourceDropdown extends Component { fetchFeed: PropTypes.func, uploadFeed: PropTypes.func } + _selectItem (key) { + switch (key) { + case 'delete': + return this.deleteFeed() + case 'fetch': + return this.props.fetchFeed(this.props.feedSource) + case 'upload': + return this.uploadFeed() + case 'deploy': + return this.props.createDeploymentFromFeedSource(this.props.feedSource) + case 'public': + return browserHistory.push(`/public/feed/${this.props.feedSource.id}`) + } + } deleteFeed () { this.props.setHold(this.props.feedSource) this.refs['deleteModal'].open() @@ -271,6 +331,16 @@ class FeedSourceDropdown extends Component { this.props.setHold(this.props.feedSource) this.refs['uploadModal'].open() } + confirmUpload (files) { + const file = files[0] + if (isValidZipFile(file)) { + this.props.uploadFeed(this.props.feedSource, file) + this.props.setHold(false) + return true + } else { + return false + } + } render () { const fs = this.props.feedSource const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') @@ -281,30 +351,15 @@ class FeedSourceDropdown extends Component { { - console.log('OK, deleting') - this.props.deleteFeedSource(fs) - }} - onClose={() => { - this.props.setHold(false) - }} + onConfirm={() => this.props.deleteFeedSource(fs)} + onClose={() => this.props.setHold(false)} /> { - if (isValidZipFile(files[0])) { - this.props.uploadFeed(fs, files[0]) - this.props.setHold(false) - return true - } else { - return false - } - }} - onClose={() => { - this.props.setHold(false) - }} + onConfirm={(files) => this.confirmUpload(files)} + onClose={() => this.props.setHold(false)} errorMessage='Uploaded file must be a valid zip file (.zip).' /> @@ -312,31 +367,18 @@ class FeedSourceDropdown extends Component { className='pull-right' bsStyle='default' bsSize='small' - onSelect={key => { - switch (key) { - case 'delete': - return this.deleteFeed() - case 'fetch': - return this.props.fetchFeed(fs) - case 'upload': - return this.uploadFeed() - case 'deploy': - return this.props.createDeploymentFromFeedSource(fs) - case 'public': - return browserHistory.push(`/public/feed/${fs.id}`) - } - }} + onSelect={key => this._selectItem(key)} id={`feed-source-action-button`} pullRight > - + Fetch Upload @@ -348,32 +390,34 @@ class FeedSourceDropdown extends Component { } {isModuleEnabled('deployment') ? Deploy + disabled={disabled || !fs.deployable || !fs.feedVersionCount} + title={disabled + ? 'You do not have permission to deploy feed' + : !fs.deployable + ? 'Feed source is not deployable. Change in feed source settings.' + : !fs.feedVersionCount + ? 'No versions exist. Create new version to deploy feed' + : 'Deploy feed source.' + } + eventKey='deploy' + > + Deploy + : null } {getConfigProperty('application.notifications_enabled') ? + isWatching={isWatchingFeed} + user={this.props.user} + target={fs.id} + subscriptionType='feed-updated' + componentClass='menuItem' + /> : null } - View public page + View public page - Delete + Delete
    diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index a57236289..1306edb1d 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -1,7 +1,8 @@ import React, {Component, PropTypes} from 'react' import Helmet from 'react-helmet' import moment from 'moment' -import { Tabs, Tab, Grid, Row, Label, Col, Button, InputGroup, Table, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' +import { LinkContainer } from 'react-router-bootstrap' +import { Tabs, Tab, Grid, Row, Label, Col, Button, InputGroup, ListGroup, ListGroupItem, Table, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' import { sentence as toSentenceCase } from 'change-case' import {Icon} from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' @@ -11,6 +12,7 @@ import ManagerPage from '../../common/components/ManagerPage' import Breadcrumbs from '../../common/components/Breadcrumbs' import WatchButton from '../../common/containers/WatchButton' import ProjectSettings from './ProjectSettings' +import ActiveDeploymentViewer from '../containers/ActiveDeploymentViewer' import FeedSourceTable from './FeedSourceTable' import EditableTextField from '../../common/components/EditableTextField' import { defaultSorter } from '../../common/util/util' @@ -23,7 +25,7 @@ export default class ProjectViewer extends Component { visibilityFilter: PropTypes.object, visibilityFilterChanged: PropTypes.func, - searchTextChanged: PropTypes.func, + searchTextChanged: PropTypes.func } constructor (props) { super(props) @@ -41,7 +43,16 @@ export default class ProjectViewer extends Component { } }) } - + _selectTab (key) { + if (key === 'sources') { + browserHistory.push(`/project/${this.props.project.id}/`) + } else { + browserHistory.push(`/project/${this.props.project.id}/${key}`) + } + if (key === 'deployments' && !this.props.project.deployments) { + this.props.deploymentsRequested() + } + } showUploadFeedModal (feedSource) { this.refs.page.showSelectFileModal({ title: 'Upload Feed', @@ -50,8 +61,7 @@ export default class ProjectViewer extends Component { let nameArray = files[0].name.split('.') if (files[0].type !== 'application/zip' || nameArray[nameArray.length - 1] !== 'zip') { return false - } - else { + } else { this.props.uploadFeedClicked(feedSource, files[0]) return true } @@ -70,7 +80,7 @@ export default class ProjectViewer extends Component { this.props.onComponentMount(this.props) } render () { - if(!this.props.project) { + if (!this.props.project) { return } const messages = getComponentMessages('ProjectViewer') @@ -78,21 +88,21 @@ export default class ProjectViewer extends Component { const projectEditDisabled = !this.props.user.permissions.isProjectAdmin(this.props.project.id) const filteredFeedSources = this.props.project.feedSources ? this.props.project.feedSources.filter(feedSource => { - if(feedSource.isCreating) return true // feeds actively being created are always visible - let visible = feedSource.name !== null ? feedSource.name.toLowerCase().indexOf((this.props.visibilityFilter.searchText || '').toLowerCase()) !== -1 : '[unnamed project]' - switch (this.props.visibilityFilter.filter) { - case 'ALL': - return visible - case 'STARRED': - return [].indexOf(feedSource.id) !== -1 // check userMetaData - case 'PUBLIC': - return feedSource.isPublic - case 'PRIVATE': - return !feedSource.isPublic - default: - return visible - } - }).sort(defaultSorter) + if (feedSource.isCreating) return true // feeds actively being created are always visible + let visible = feedSource.name !== null ? feedSource.name.toLowerCase().indexOf((this.props.visibilityFilter.searchText || '').toLowerCase()) !== -1 : '[unnamed project]' + switch (this.props.visibilityFilter.filter) { + case 'ALL': + return visible + case 'STARRED': + return [].indexOf(feedSource.id) !== -1 // check userMetaData + case 'PUBLIC': + return feedSource.isPublic + case 'PRIVATE': + return !feedSource.isPublic + default: + return visible + } + }).sort(defaultSorter) : [] const projectsHeader = ( @@ -127,50 +137,50 @@ export default class ProjectViewer extends Component { {getMessage(messages, 'feeds.new')} - {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') - ? Sync}> + {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') + ? Sync}> {isExtensionEnabled('transitland') ? { - this.props.thirdPartySync('TRANSITLAND') - }} - > - transit.land - + bsStyle='primary' + disabled={projectEditDisabled} + id='TRANSITLAND' + onClick={(evt) => { + this.props.thirdPartySync('TRANSITLAND') + }} + > + transit.land + : null } {isExtensionEnabled('transitfeeds') ? { - this.props.thirdPartySync('TRANSITFEEDS') - }} - > - transitfeeds.com - + bsStyle='primary' + disabled={projectEditDisabled} + id='TRANSITFEEDS' + onClick={(evt) => { + this.props.thirdPartySync('TRANSITFEEDS') + }} + > + transitfeeds.com + : null } {isExtensionEnabled('mtc') ? { - this.props.thirdPartySync('MTC') - }} - > - MTC - + bsStyle='primary' + disabled={projectEditDisabled} + id='MTC' + onClick={(evt) => { + this.props.thirdPartySync('MTC') + }} + > + MTC + : null } - : null - } + : null + } @@ -198,9 +208,9 @@ export default class ProjectViewer extends Component { /> } > - +

    - {this.props.project.name} + {this.props.project.name} @@ -232,10 +242,10 @@ export default class ProjectViewer extends Component {

      -
    • {this.props.project.defaultLocationLon ? `${this.props.project.defaultLocationLat}, ${this.props.project.defaultLocationLon}` : 'n/a'}
    • -
    • {this.props.project.autoFetchFeeds ? `${this.props.project.autoFetchHour}:${this.props.project.autoFetchMinute < 10 ? '0' + this.props.project.autoFetchMinute : this.props.project.autoFetchMinute}` : 'Auto fetch disabled'}
    • +
    • {this.props.project.defaultLocationLon ? `${this.props.project.defaultLocationLat}, ${this.props.project.defaultLocationLon}` : 'n/a'}
    • +
    • {this.props.project.autoFetchFeeds ? `${this.props.project.autoFetchHour}:${this.props.project.autoFetchMinute < 10 ? '0' + this.props.project.autoFetchMinute : this.props.project.autoFetchMinute}` : 'Auto fetch disabled'}
    • {/* -
    • {fs.feedVersions ? `${this.getAverageFileSize(fs.feedVersions)} MB` : 'n/a'}
    • +
    • {fs.feedVersions ? `${this.getAverageFileSize(fs.feedVersions)} MB` : 'n/a'}
    • */}
    @@ -243,21 +253,16 @@ export default class ProjectViewer extends Component { { - if (key === 'sources') { - browserHistory.push(`/project/${this.props.project.id}/`) - } - else { - browserHistory.push(`/project/${this.props.project.id}/${key}`) - } - if (key === 'deployments' && !this.props.project.deployments) { - this.props.deploymentsRequested() - } - }} + onSelect={(key) => this._selectTab(key)} > {getMessage(messages, 'feeds.title')}} + title={ + + + {getMessage(messages, 'feeds.title')} + + } > @@ -266,6 +271,10 @@ export default class ProjectViewer extends Component { + What is a feed source?}> A feed source defines the location or upstream source of a GTFS feed. GTFS can be populated via automatic fetch, directly editing or uploading a zip file. @@ -274,26 +283,22 @@ export default class ProjectViewer extends Component { {isModuleEnabled('deployment') ? {getMessage(messages, 'deployments')}} - > - - + eventKey='deployments' + disabled={projectEditDisabled} + title={{getMessage(messages, 'deployments')}} + > + + : null } {getMessage(messages, 'settings')}} + title={{getMessage(messages, 'settings')}} > fs.latestValidation ? fs.latestValidation.errorCount : 0).reduce((a, b) => a + b, 0) + const serviceSeconds = feedSources.map(fs => fs.latestValidation ? fs.latestValidation.avgDailyRevenueTime : 0).reduce((a, b) => a + b, 0) + return ( + {project.name} summary}> + + Number of feeds: {feedSources.length} + Total errors: {errorCount} + Total service: {Math.floor(serviceSeconds / 60 / 60 * 100) / 100} hours per weekday + + + ) + } +} + class DeploymentsPanel extends Component { static propTypes = { - deployments: PropTypes.object, + deployments: PropTypes.array, deleteDeploymentConfirmed: PropTypes.func, deploymentsRequested: PropTypes.func, onNewDeploymentClick: PropTypes.func, @@ -326,9 +348,6 @@ class DeploymentsPanel extends Component { this.props.deploymentsRequested() } } - shouldComponentUpdate (nextProps, nextState) { - return !shallowEqual(nextProps.deployments, this.props.deployments) - } // deleteDeployment (deployment) { // console.log(this.refs) // this.refs['page'].showConfirmModal({ @@ -340,106 +359,131 @@ class DeploymentsPanel extends Component { // } // }) // } + render () { + const deployment = this.props.deployments && this.props.deployments.find(d => d.id === this.props.activeSubComponent) + if (deployment) { + return ( + + ) + } + return ( + + + + + + Deploying feeds to OTP}> +

    A collection of feeds can be deployed to OpenTripPlanner (OTP) instances that have been defined in the organization settings.

    + + + +
    + +
    + ) + } +} + +class DeploymentsList extends Component { render () { const messages = getComponentMessages('DeploymentsPanel') const na = (N/A) return ( - - - - - this.props.searchTextChanged(evt.target.value)} - /> - - - - - - } - > - - - - - - - - + + + this.props.searchTextChanged(evt.target.value)} + /> + + + + + + } + > +
    {getMessage(messages, 'table.name')}{getMessage(messages, 'table.creationDate')}{getMessage(messages, 'table.deployedTo')}{getMessage(messages, 'table.feedCount')}
    + + + + + + + + + + + {this.props.deployments + ? this.props.deployments.map(dep => { + return ( + + + + + + - - - {this.props.deployments - ? this.props.deployments.map(dep => { - return ( - - - - - - - - ) - }) - : null - } - -
    {getMessage(messages, 'table.name')}{getMessage(messages, 'table.creationDate')}{getMessage(messages, 'table.deployedTo')}{getMessage(messages, 'table.feedCount')}
    + { + if (dep.isCreating) this.props.newDeploymentNamed(value) + else this.props.updateDeployment(dep, {name: value}) + }} + link={`/project/${dep.project.id}/deployments/${dep.id}`} + /> + + {dep.dateCreated + ? ({moment(dep.dateCreated).format('MMM Do YYYY')} ({moment(dep.dateCreated).fromNow()})) + : na + } + + {dep.deployedTo + ? () + : na + } + + {dep.feedVersions + ? ({dep.feedVersions.length}) + : na + } + + +
    - { - if (dep.isCreating) this.props.newDeploymentNamed(value) - else this.props.updateDeployment(dep, {name: value}) - }} - link={`/deployment/${dep.id}`} - /> - - {dep.dateCreated - ? ({moment(dep.dateCreated).format('MMM Do YYYY')} ({moment(dep.dateCreated).fromNow()})) - : na - } - - {dep.deployedTo - ? () - : na - } - - {dep.feedVersions - ? ({dep.feedVersions.length}) - : na - } - - -
    - - - - Deploying feeds to OTP}> -

    A collection of feeds can be deployed to OpenTripPlanner (OTP) instances that have been defined in the organization settings.

    -
    - -
    + ) + }) + : null + } + + + ) } } diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index 495311e72..85200b052 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -115,7 +115,7 @@ export default class UserHomePage extends Component {

    - + Hello, {this.props.user.profile.nickname}.

    @@ -160,7 +160,7 @@ export default class UserHomePage extends Component { {activeProject.name} + ? {activeProject.name} : {this.props.user.profile.nickname} } // onSelect={(eventKey) => { @@ -186,7 +186,7 @@ export default class UserHomePage extends Component { return ( - {project.name} + {project.name} ) @@ -194,9 +194,9 @@ export default class UserHomePage extends Component { : null } {activeProject && visibleProjects.length > 1 || !activeProject ? : null} - Manage organizations + Manage organizations - Create organization + Create organization
    {/* Starred Feeds Panel */} diff --git a/src/main/client/manager/containers/ActiveDeploymentViewer.js b/src/main/client/manager/containers/ActiveDeploymentViewer.js index 0f0f1cfd5..30385ff26 100644 --- a/src/main/client/manager/containers/ActiveDeploymentViewer.js +++ b/src/main/client/manager/containers/ActiveDeploymentViewer.js @@ -14,29 +14,29 @@ import { fetchDeployment, import { fetchProjectFeeds } from '../actions/feeds' const mapStateToProps = (state, ownProps) => { - let deploymentId = ownProps.routeParams.deploymentId + // let deploymentId = ownProps.routeParams.deploymentId let user = state.user - let project = state.projects.all - ? state.projects.all.find(p => { - if (!p.deployments) return false - return (p.deployments.findIndex(dep => dep.id === deploymentId) !== -1) - }) - : null + // let project = state.projects.all + // ? state.projects.all.find(p => { + // if (!p.deployments) return false + // return (p.deployments.findIndex(dep => dep.id === deploymentId) !== -1) + // }) + // : null - let deployment - if (project) { - deployment = project.deployments.find(dep => dep.id === deploymentId) - } + // let deployment + // if (project) { + // deployment = project.deployments.find(dep => dep.id === deploymentId) + // } return { - deployment, - project, + // deployment, + // project, user } } const mapDispatchToProps = (dispatch, ownProps) => { - const deploymentId = ownProps.routeParams.deploymentId + const deploymentId = ownProps.deployment && ownProps.deployment.id return { onComponentMount: (initialProps) => { if (initialProps.user.profile === null) { @@ -61,7 +61,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { // }, // deploymentsRequested: () => { dispatch(fetchProjectDeployments(projectId)) }, // searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text))}, - getDeploymentStatus: (deployment, target) => {dispatch(fetchDeploymentStatus(deployment, target))}, + getDeploymentStatus: (deployment, target) => { dispatch(fetchDeploymentStatus(deployment, target)) }, updateVersionForFeedSource: (deployment, feedSource, feedVersion) => { let feedVersions = [...deployment.feedVersions] let index = feedVersions.findIndex(v => v.feedSource.id === feedSource.id) @@ -78,7 +78,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { let index = feedVersions.findIndex(v => v.id === feedVersion.id) feedVersions.splice(index, 1) dispatch(updateDeployment(deployment, {feedVersions})) - }, + } } } diff --git a/src/main/client/manager/containers/ActiveUserHomePage.js b/src/main/client/manager/containers/ActiveUserHomePage.js index e7a329738..b8847380a 100644 --- a/src/main/client/manager/containers/ActiveUserHomePage.js +++ b/src/main/client/manager/containers/ActiveUserHomePage.js @@ -1,4 +1,5 @@ import { connect } from 'react-redux' +import { browserHistory } from 'react-router' import UserHomePage from '../components/UserHomePage' import { getRecentActivity, logout } from '../actions/user' @@ -16,14 +17,20 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const activeProjectId = ownProps.routeParams.projectId + let activeProjectId = ownProps.routeParams.projectId return { onComponentMount: (props) => { dispatch(getRecentActivity(props.user)) dispatch(fetchProjects()) .then(projects => { + if (!activeProjectId) { + const userProjectIds = props.user && Object.keys(props.user.permissions.projectLookup) + activeProjectId = userProjectIds && userProjectIds[0] + } + console.log(activeProjectId) if (activeProjectId) { dispatch(fetchProjectFeeds(activeProjectId)) + .then(() => browserHistory.push(`/home/${activeProjectId}`)) } // for (var i = 0; i < projects.length; i++) { // dispatch(fetchProjectFeeds(projects[i].id)) From 04da9a236712cc8abfe1c743e75265c257cb408f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 21 Nov 2016 15:03:55 -0500 Subject: [PATCH 148/323] style(style): find/replace for common standard.js infraction --- src/main/client/admin/components/UserAdmin.js | 4 +- .../client/alerts/components/AlertsViewer.js | 2 +- .../client/common/components/ManagerPage.js | 106 ++++++++---------- src/main/client/common/components/Sidebar.js | 4 +- .../containers/FeedSourceActionButton.js | 8 +- .../client/common/containers/StarButton.js | 4 +- .../client/common/containers/WatchButton.js | 8 +- .../components/EditorFeedSourcePanel.js | 6 +- .../editor/components/EditorHelpModal.js | 6 +- .../client/editor/components/EditorMap.js | 12 +- .../client/editor/components/EntityList.js | 2 +- .../client/editor/components/FeedInfoPanel.js | 10 +- .../client/editor/components/GtfsEditor.js | 4 +- .../client/editor/components/RouteEditor.js | 6 +- src/main/client/gtfs/components/GtfsMap.js | 10 +- .../manager/components/FeedSourceViewer.js | 40 +++---- .../components/FeedVersionNavigator.js | 10 +- .../manager/components/FeedVersionReport.js | 4 +- .../manager/components/FeedVersionViewer.js | 8 +- .../manager/components/ProjectSettings.js | 12 +- .../client/manager/components/ProjectsList.js | 2 +- .../validation/GtfsValidationSummary.js | 2 +- .../client/public/components/PublicHeader.js | 8 +- .../client/public/components/PublicPage.js | 4 +- .../client/public/components/UserAccount.js | 2 +- .../client/signs/components/SignEditor.js | 6 +- 26 files changed, 140 insertions(+), 150 deletions(-) diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index c49b78a59..d538bb00a 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -52,7 +52,7 @@ export default class UserAdmin extends Component { Back to dashboard - {getMessage(messages, 'title')} + {getMessage(messages, 'title')} @@ -97,7 +97,7 @@ export default class UserAdmin extends Component { bsStyle='danger' bsSize='large' href='https://manage.auth0.com/#/logs'> - View application logs on Auth0.com + View application logs on Auth0.com

    : null diff --git a/src/main/client/alerts/components/AlertsViewer.js b/src/main/client/alerts/components/AlertsViewer.js index 4eaa81366..9eb96ce95 100644 --- a/src/main/client/alerts/components/AlertsViewer.js +++ b/src/main/client/alerts/components/AlertsViewer.js @@ -33,7 +33,7 @@ export default class AlertsViewer extends React.Component {

    - Service Alerts + Service Alerts - - - - + + + + {isModuleEnabled('alerts') ? + icon='exclamation-circle' + label='Alerts' + link={`/alerts`} + active={this.isActive('alerts')} + /> : null } {isModuleEnabled('sign_config') ? + icon='television' + label='eTID Config' + link={`/signs`} + active={this.isActive('signs')} + /> : null } - {/**/} -
    - {this.props.children} -
    + {/* */} +
    + {this.props.children} +
    - +

    diff --git a/src/main/client/common/components/Sidebar.js b/src/main/client/common/components/Sidebar.js index 94602b98a..e0e0807d8 100644 --- a/src/main/client/common/components/Sidebar.js +++ b/src/main/client/common/components/Sidebar.js @@ -74,7 +74,9 @@ export default class Sidebar extends Pure { inverse style={navbarStyle}> -
    {children}
    +
    + {children} +
    Edit - + Update Upload @@ -47,7 +47,7 @@ class FeedSourceActionButton extends Component { : null } {isModuleEnabled('deployment') - ? Deploy + ? Deploy : null } {getConfigProperty('application.notifications_enabled') @@ -60,9 +60,9 @@ class FeedSourceActionButton extends Component { /> : null } - View public page + View public page - Delete + Delete ) diff --git a/src/main/client/common/containers/StarButton.js b/src/main/client/common/containers/StarButton.js index e0f2de9d6..d2bcdc7fb 100644 --- a/src/main/client/common/containers/StarButton.js +++ b/src/main/client/common/containers/StarButton.js @@ -16,8 +16,8 @@ class StarButton extends React.Component { dispatch(updateStar(user.profile, target, !isStarred)) }}> {isStarred - ? {getMessage(messages, 'unstar')} - : {getMessage(messages, 'star')} + ? {getMessage(messages, 'unstar')} + : {getMessage(messages, 'star')} } ) diff --git a/src/main/client/common/containers/WatchButton.js b/src/main/client/common/containers/WatchButton.js index dd2d69dce..476c5df3f 100644 --- a/src/main/client/common/containers/WatchButton.js +++ b/src/main/client/common/containers/WatchButton.js @@ -27,8 +27,8 @@ class WatchButton extends Component { onClick={() => dispatch(updateTargetForSubscription(user.profile, target, subscriptionType)) } > { - isWatching ? {getMessage(messages, 'unwatch')} - : {getMessage(messages, 'watch')} + isWatching ? {getMessage(messages, 'unwatch')} + : {getMessage(messages, 'watch')} } ) @@ -38,8 +38,8 @@ class WatchButton extends Component { onClick={() => dispatch(updateTargetForSubscription(user.profile, target, subscriptionType)) } > { - isWatching ? {getMessage(messages, 'unwatch')} - : {getMessage(messages, 'watch')} + isWatching ? {getMessage(messages, 'unwatch')} + : {getMessage(messages, 'watch')} } ) diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 4a25da875..68f9fcd4e 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -104,7 +104,7 @@ export default class EditorFeedSourcePanel extends Component { } - What are snapshots?}> + What are snapshots?}>

    Snapshots are save points you can always revert back to when editing a GTFS feed.

    A snapshot might represent a work-in-progress, future planning scenario or even different service patterns (e.g., summer schedule markup).

    @@ -134,7 +134,7 @@ class SnapshotItem extends Component { onClick={() => this.props.restoreSnapshot(feedSource, snapshot)} > {snapshot.current - ? {getMessage(messages, 'active')} + ? {getMessage(messages, 'active')} : {getMessage(messages, 'restore')} } @@ -154,7 +154,7 @@ class SnapshotItem extends Component { {getMessage(messages, 'delete')} - created {moment(snapshot.snapshotTime).fromNow()} + created {moment(snapshot.snapshotTime).fromNow()}

    ) diff --git a/src/main/client/editor/components/EditorHelpModal.js b/src/main/client/editor/components/EditorHelpModal.js index 467010abf..b1cec30c0 100644 --- a/src/main/client/editor/components/EditorHelpModal.js +++ b/src/main/client/editor/components/EditorHelpModal.js @@ -36,21 +36,21 @@ export default class EditorHelpModal extends Component { - 900x500 + 900x500

    First slide label

    Nulla vitae elit libero, a pharetra augue mollis interdum.

    - 900x500 + 900x500

    Second slide label

    Lorem ipsum dolor sit amet, consectetur adipiscing elit.

    - 900x500 + 900x500

    Third slide label

    Praesent commodo cursus magna, vel scelerisque nisl consectetur.

    diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 869094672..1465e4144 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -766,7 +766,7 @@ export default class EditorMap extends Component { this.props.saveActiveEntity('trippattern') }} > - + Edit stop}> Remove from pattern}> @@ -784,7 +784,7 @@ export default class EditorMap extends Component { this.removeStopFromPattern(this.props.activePattern, stop, index) }} > - + - + - + = this.props.activePattern.patternStops.length - 2} value={this.props.activePattern.patternStops.length} eventKey={this.props.activePattern.patternStops.length}> Add to end (default) @@ -920,7 +920,7 @@ export default class EditorMap extends Component {
    {stop.stop_name}
    Add stop} + title={ Add stop} id={`split-button-basic-${i}`} bsStyle='success' onSelect={(key) => { diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index b7eb88554..819c49021 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -208,7 +208,7 @@ export default class EntityList extends Component { this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) }} > - Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} + Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent}
    diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index 4692ae975..e27b00e57 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -66,7 +66,7 @@ export default class FeedInfoPanel extends Component { return (
    - +
    { @@ -109,12 +109,12 @@ export default class FeedInfoPanel extends Component { {/* Add entity dropdown */} } + title={} id='add-entity-dropdown' onSelect={key => { console.log(key) @@ -153,7 +153,7 @@ export default class FeedInfoPanel extends Component { this.refs.snapshotModal.open() }} > - + { return ( - Revert to {snapshot.name} + Revert to {snapshot.name} ) }) : No snapshots diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index ea4303d4d..052925547 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -232,8 +232,8 @@ export default class GtfsEditor extends Component { />
    - - + +
    ) } diff --git a/src/main/client/editor/components/RouteEditor.js b/src/main/client/editor/components/RouteEditor.js index 4a8159abf..bf12a34fc 100644 --- a/src/main/client/editor/components/RouteEditor.js +++ b/src/main/client/editor/components/RouteEditor.js @@ -58,18 +58,18 @@ export default class RouteEditor extends Component { bsSize='small' bsStyle='success' > - + Route editor diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index b4c69d177..e451beaff 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -180,7 +180,7 @@ export default class GtfsMap extends Component { >
    -

    {stop.stop_name} ({stop.stop_id})

    +

    {stop.stop_name} ({stop.stop_id})

    {this.props.renderTransferPerformance && this.renderTransferPerformance(stop)} {this.props.onStopClick ? : null } @@ -209,8 +209,8 @@ export default class GtfsMap extends Component { const popup = (
    -

    {routeName}

    -

    {getRouteName(route)}

    +

    {routeName}

    +

    {getRouteName(route)}

    • ID: {routeId}
    • Agency:{' '} @@ -226,7 +226,7 @@ export default class GtfsMap extends Component { block onClick={() => this.props.onRouteClick(route, feed, this.props.newEntityId)} > - {this.props.popupAction} route + {this.props.popupAction} route :

      [Must add stops first]

      } diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index e3096d40c..130021324 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -213,7 +213,7 @@ export default class FeedSourceViewer extends Component {

      This feed source is currently {fs.isPublic ? 'public' : 'private'}.

      - +

      Delete this feed source.

      Once you delete a feed source, it cannot be recovered.

      @@ -257,15 +257,15 @@ export default class FeedSourceViewer extends Component { > {/* Title + Shortcut Buttons Row */}

      - + {this.props.project.name} {' '}/{' '} {fs.name}{' '} - {fs.isPublic ? null : } + {fs.isPublic ? null : } {' '} {fs.editedSinceSnapshot - ? - : + ? + : } - +

        -
      • {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
      • -
      • {fs.url ? fs.url : '(none)'} +
      • {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
      • +
      • {fs.url ? fs.url : '(none)'}
      • - {
      • {this.getAverageFileSize(fs.feedVersions)}
      • } + {
      • {this.getAverageFileSize(fs.feedVersions)}
      • }
      - {/*
    • {fs.feedVersionCount}
    • {fs.url}*/} + {/*
    • {fs.feedVersionCount}
    • {fs.url}*/} @@ -301,7 +301,7 @@ export default class FeedSourceViewer extends Component { activeKey={activeTab} onSelect={(eventKey => browserHistory.push(`/feed/${fs.id}/${eventKey}`))} > - {getMessage(messages, 'gtfs')}}> + {getMessage(messages, 'gtfs')}}> {/* @@ -376,7 +376,7 @@ export default class FeedSourceViewer extends Component { {/* - + @@ -384,22 +384,22 @@ export default class FeedSourceViewer extends Component {
      Create new version
        -
      • -
      • +
      • +

      • -
      • +
      */} {/* New version} + title={ New version} > - Upload - Fetch + Upload + Fetch - From snapshot + From snapshot */} @@ -420,7 +420,7 @@ export default class FeedSourceViewer extends Component { /> {/* - Snapshots}> + Snapshots}> Snapshot 1 diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index e483d5749..634de6ece 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -96,8 +96,8 @@ export default class FeedVersionNavigator extends Component { - - + + {this.state.listView ? null @@ -140,7 +140,7 @@ export default class FeedVersionNavigator extends Component { // disabled={editGtfsDisabled} // || !fs.latestValidation} // bsStyle='info' onClick={() => { this.props.createDeploymentFromFeedSource(fs) }}> - Deploy feed + Deploy feed : null } @@ -155,7 +155,7 @@ export default class FeedVersionNavigator extends Component { } Create new version} id='bg-nested-dropdown' + title={ Create new version} id='bg-nested-dropdown' onSelect={key => { console.log(key) switch (key) { @@ -175,7 +175,7 @@ export default class FeedVersionNavigator extends Component { Fetch Upload - From snapshot + From snapshot diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index d86278590..74cd0ca8f 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -125,7 +125,7 @@ export default class FeedVersionReport extends Component { return {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} + footer={ {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} >

      {version.validationSummary && version.validationSummary.avgDailyRevenueTime - ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) + ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) : null }

      diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index ca8f5e0c0..3dd851202 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -201,19 +201,19 @@ class VersionSectionSelector extends Component { - Version summary + Version summary - Validation issues {this.renderIssuesLabel(version)} + Validation issues {this.renderIssuesLabel(version)} {isModuleEnabled('gtfsplus') ? - GTFS+ for this version + GTFS+ for this version : null } - Version comments + Version comments diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index 7be314150..765a9524f 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -169,7 +169,7 @@ export default class ProjectSettings extends Component { }) }} > - + @@ -221,7 +221,7 @@ export default class ProjectSettings extends Component { }) }} > - + } @@ -253,7 +253,7 @@ export default class ProjectSettings extends Component { Danger zone}> - +

      Delete this organization.

      Once you delete an organization, the organization and all feed sources it contains cannot be recovered.

      @@ -431,7 +431,7 @@ export default class ProjectSettings extends Component { this.setState(update(this.state, stateUpdate)) }} > - {getMessage(messages, 'deployment.servers.new')} + {getMessage(messages, 'deployment.servers.new')} {getMessage(messages, 'deployment.servers.title')} @@ -460,7 +460,7 @@ export default class ProjectSettings extends Component { this.setState(update(this.state, stateUpdate)) }} > - Remove + Remove {getMessage(messages, 'deployment.servers.name')} @@ -591,7 +591,7 @@ export default class ProjectSettings extends Component { {activeSettingsPanel} - + ) diff --git a/src/main/client/manager/components/ProjectsList.js b/src/main/client/manager/components/ProjectsList.js index fa0447e83..d87b7bd43 100644 --- a/src/main/client/manager/components/ProjectsList.js +++ b/src/main/client/manager/components/ProjectsList.js @@ -55,7 +55,7 @@ export default class ProjectsList extends React.Component { {getMessage(messages, 'new')} {getMessage(messages, 'help.content')}}> - + diff --git a/src/main/client/manager/components/validation/GtfsValidationSummary.js b/src/main/client/manager/components/validation/GtfsValidationSummary.js index 00dbdb2f1..af56eb4a3 100644 --- a/src/main/client/manager/components/validation/GtfsValidationSummary.js +++ b/src/main/client/manager/components/validation/GtfsValidationSummary.js @@ -89,7 +89,7 @@ export default class GtfsValidationSummary extends Component { bsSize='large' style={{marginTop: '20px'}} > - View full validation report + View full validation report
    diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index ac84a0ba8..58a30336d 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -19,7 +19,7 @@ export default class PublicHeader extends Component { render () { const userDropdown = {this.props.username}} + title={ {this.props.username}} > browserHistory.push('/settings/profile')}>My Account this.props.resetPassword()}>Change Password @@ -46,7 +46,7 @@ export default class PublicHeader extends Component { : } @@ -54,13 +54,13 @@ export default class PublicHeader extends Component { {this.props.username || getConfigProperty('modules.enterprise.enabled') ? null : } {/* "Log out" Button */} {this.props.username - ? + ? : null } diff --git a/src/main/client/public/components/PublicPage.js b/src/main/client/public/components/PublicPage.js index dec2510d1..1675d0324 100644 --- a/src/main/client/public/components/PublicPage.js +++ b/src/main/client/public/components/PublicPage.js @@ -32,8 +32,8 @@ export default class PublicPage extends React.Component { {this.props.children} - - + +
    ) } diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index d27bbfbcb..9f4938067 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -160,7 +160,7 @@ export default class UserAccount extends Component { - My settings + My settings diff --git a/src/main/client/signs/components/SignEditor.js b/src/main/client/signs/components/SignEditor.js index 309304437..f80f07789 100644 --- a/src/main/client/signs/components/SignEditor.js +++ b/src/main/client/signs/components/SignEditor.js @@ -56,7 +56,7 @@ export default class SignEditor extends React.Component { (evt) => { browserHistory.push('/signs') }} - > Back + > Back @@ -145,7 +145,7 @@ export default class SignEditor extends React.Component { }} /> - Associated Displays for Sign Configuration}> + Associated Displays for Sign Configuration}> - Stops/Routes for Sign Configuration}> + Stops/Routes for Sign Configuration}> From 6ee7a1cfe9fd8d2257219ab683dd2603d94fc566 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Tue, 22 Nov 2016 14:01:24 +0800 Subject: [PATCH 149/323] chore(gitignore): Ignore non-default configuration files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e776b729a..ad3a24cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ src/main/client/config.js datatools-manager.iml config.yml config_server.yml +configurations/* +!configurations/default From 228ef8622ce2eb8b38088fd3ea67bf484cf91547 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 22 Nov 2016 12:19:01 -0500 Subject: [PATCH 150/323] style(linting, style): major fix towards clean lint --- src/main/client/admin/actions/admin.js | 4 +- .../client/alerts/components/AlertEditor.js | 20 +- .../client/alerts/components/AlertsList.js | 4 +- .../alerts/containers/VisibleAlertsList.js | 8 +- .../client/alerts/reducers/activeAlert.js | 4 +- src/main/client/alerts/reducers/alerts.js | 8 +- .../common/components/LanguageSelect.js | 2 +- .../client/common/components/ManagerPage.js | 2 +- src/main/client/common/components/MapModal.js | 6 +- src/main/client/common/components/Sidebar.js | 2 +- .../common/components/SidebarNavItem.js | 2 +- .../common/components/TimezoneSelect.js | 2 +- src/main/client/common/util/jwtHelper.js | 2 +- src/main/client/common/util/languages.js | 736 +++++++++--------- src/main/client/common/util/util.js | 22 +- src/main/client/editor/actions/editor.js | 12 +- .../components/EditorFeedSourcePanel.js | 104 ++- .../client/editor/components/EditorMap.js | 251 ++---- .../client/editor/components/EntityDetails.js | 26 +- .../client/editor/components/EntityList.js | 88 +-- .../client/editor/components/FeedInfoPanel.js | 2 +- .../client/editor/components/GtfsEditor.js | 6 +- .../client/editor/components/GtfsInput.js | 10 +- .../client/editor/components/GtfsTable.js | 8 +- .../editor/components/GtfsTableEditor.js | 6 +- .../editor/components/GtfsVersionSummary.js | 50 +- .../editor/components/HourMinuteInput.js | 21 +- .../editor/components/PatternStopPopup.js | 121 +++ .../editor/containers/ActiveGtfsEditor.js | 2 +- .../containers/ActiveGtfsTableEditor.js | 6 +- .../containers/ActiveTripPatternList.js | 2 +- src/main/client/editor/reducers/editor.js | 8 +- src/main/client/gtfs/components/GtfsFilter.js | 4 +- src/main/client/gtfs/components/GtfsMap.js | 2 +- src/main/client/gtfs/components/gtfssearch.js | 2 +- .../client/gtfs/containers/ActiveGtfsMap.js | 4 +- src/main/client/gtfsplus/actions/gtfsplus.js | 12 +- .../gtfsplus/components/GtfsPlusEditor.js | 6 +- .../gtfsplus/components/GtfsPlusTable.js | 8 +- .../components/GtfsPlusVersionSummary.js | 16 +- .../containers/ActiveGtfsPlusEditor.js | 6 +- src/main/client/gtfsplus/reducers/gtfsplus.js | 10 +- src/main/client/manager/actions/user.js | 2 +- .../manager/components/DeploymentViewer.js | 2 +- .../manager/components/FeedSourceViewer.js | 4 +- .../manager/components/FeedVersionReport.js | 194 +++-- .../manager/components/FeedVersionViewer.js | 2 +- .../client/manager/components/NotesViewer.js | 4 +- .../manager/components/ProjectSettings.js | 2 +- .../client/manager/components/ProjectsList.js | 4 +- .../client/manager/components/UserHomePage.js | 49 +- .../reporter/components/FeedLayout.js | 6 +- .../reporter/containers/Patterns.js | 2 +- .../components/reporter/containers/Routes.js | 2 +- .../components/reporter/containers/Stops.js | 2 +- .../validation/GtfsValidationExplorer.js | 8 +- .../validation/GtfsValidationMap.js | 2 +- .../validation/GtfsValidationViewer.js | 4 +- .../components/validation/IsochroneMap.js | 4 +- .../components/validation/ValidationMap.js | 4 +- .../manager/containers/ActiveUserHomePage.js | 3 - src/main/client/manager/reducers/projects.js | 102 ++- src/main/client/public/components/FeedsMap.js | 19 +- .../public/components/PublicFeedsViewer.js | 119 +-- .../client/public/components/PublicHeader.js | 2 +- .../client/public/components/RegionSearch.js | 75 +- .../client/public/components/SignupPage.js | 74 +- .../client/public/components/UserAccount.js | 2 +- .../ActivePublicFeedSourceViewer.js | 16 +- .../client/scenario-editor/utils/valhalla.js | 43 +- src/main/client/signs/actions/signs.js | 33 +- .../client/signs/components/AffectedEntity.js | 39 +- .../signs/components/DisplaySelector.js | 99 +-- .../client/signs/components/SignEditor.js | 88 +-- .../client/signs/components/SignPreview.js | 50 +- .../signs/containers/ActiveSignEditor.js | 19 +- .../signs/containers/MainSignsViewer.js | 4 +- src/main/client/signs/reducers/signs.js | 37 +- 78 files changed, 1211 insertions(+), 1527 deletions(-) create mode 100644 src/main/client/editor/components/PatternStopPopup.js diff --git a/src/main/client/admin/actions/admin.js b/src/main/client/admin/actions/admin.js index 3d90cbd0c..8705363e0 100644 --- a/src/main/client/admin/actions/admin.js +++ b/src/main/client/admin/actions/admin.js @@ -20,12 +20,12 @@ export function fetchUsers () { const queryString = getState().admin.userQueryString let countUrl = '/api/manager/secure/usercount' - if(queryString) countUrl += `?queryString=${queryString}` + if (queryString) countUrl += `?queryString=${queryString}` const getCount = secureFetch(countUrl, getState()) .then(response => response.json()) let usersUrl = `/api/manager/secure/user?page=${getState().admin.page}` - if(queryString) usersUrl += `&queryString=${queryString}` + if (queryString) usersUrl += `&queryString=${queryString}` const getUsers = secureFetch(usersUrl, getState()) .then(response => response.json()) diff --git a/src/main/client/alerts/components/AlertEditor.js b/src/main/client/alerts/components/AlertEditor.js index fccca9bf2..2450105a1 100644 --- a/src/main/client/alerts/components/AlertEditor.js +++ b/src/main/client/alerts/components/AlertEditor.js @@ -98,11 +98,11 @@ export default class AlertEditor extends React.Component { var bName = b.shortName || b.name // return 511 Staff as first in list to avoid 511 Emergency being first in list - if(/511 Staff/.test(aName)) return -1 - if(/511 Staff/.test(bName)) return 1 + if (/511 Staff/.test(aName)) return -1 + if (/511 Staff/.test(bName)) return 1 - if(aName < bName) return -1 - if(aName > bName) return 1 + if (aName < bName) return -1 + if (aName > bName) return 1 return 0 } const canPublish = checkEntitiesForFeeds(this.props.alert.affectedEntities, this.props.publishableFeeds) @@ -145,23 +145,23 @@ export default class AlertEditor extends React.Component { disabled={editingIsDisabled} onClick={(evt) => { console.log('times', this.props.alert.end, this.props.alert.start); - if(!this.props.alert.title) { + if (!this.props.alert.title) { alert('You must specify an alert title') return } - if(!this.props.alert.end || !this.props.alert.start) { + if (!this.props.alert.end || !this.props.alert.start) { alert('Alert must have a start and end date') return } - if(this.props.alert.end < this.props.alert.start) { + if (this.props.alert.end < this.props.alert.start) { alert('Alert end date cannot be before start date') return } - if(moment(this.props.alert.end).isBefore(moment())) { + if (moment(this.props.alert.end).isBefore(moment())) { alert('Alert end date cannot be before the current date') return } - if(this.props.alert.affectedEntities.length === 0) { + if (this.props.alert.affectedEntities.length === 0) { alert('You must specify at least one affected entity') return } @@ -318,7 +318,7 @@ export default class AlertEditor extends React.Component { placeholder='Add stop/route' limit={100} entities={['stops', 'routes']} - clearable={true} + clearable onChange={(evt) => { console.log('we need to add this entity to the store', evt) if (typeof evt !== 'undefined' && evt !== null){ diff --git a/src/main/client/alerts/components/AlertsList.js b/src/main/client/alerts/components/AlertsList.js index 72ed3eb22..2961e6cb2 100644 --- a/src/main/client/alerts/components/AlertsList.js +++ b/src/main/client/alerts/components/AlertsList.js @@ -31,8 +31,8 @@ export default class AlertsList extends Component { var compare = function (a, b) { var aName = a.shortName || a.name var bName = b.shortName || b.name - if(aName < bName) return -1 - if(aName > bName) return 1 + if (aName < bName) return -1 + if (aName > bName) return 1 return 0 } let sortedFeeds = this.props.editableFeeds.sort(compare) diff --git a/src/main/client/alerts/containers/VisibleAlertsList.js b/src/main/client/alerts/containers/VisibleAlertsList.js index 102c16b51..4ea2135bd 100644 --- a/src/main/client/alerts/containers/VisibleAlertsList.js +++ b/src/main/client/alerts/containers/VisibleAlertsList.js @@ -27,15 +27,15 @@ const getVisibleAlerts = (alerts, visibilityFilter) => { visibleAlerts = visibleAlerts.sort((a, b) => { var aValue = visibilityFilter.sort.type === 'title' ? a[visibilityFilter.sort.type].toUpperCase() : a[visibilityFilter.sort.type] var bValue = visibilityFilter.sort.type === 'title' ? b[visibilityFilter.sort.type].toUpperCase() : b[visibilityFilter.sort.type] - if(aValue < bValue) return visibilityFilter.sort.direction === 'asc' ? -1 : 1 - if(aValue > bValue) return visibilityFilter.sort.direction === 'asc' ? 1 : -1 + if (aValue < bValue) return visibilityFilter.sort.direction === 'asc' ? -1 : 1 + if (aValue > bValue) return visibilityFilter.sort.direction === 'asc' ? 1 : -1 return 0 }) } else { visibleAlerts.sort((a,b) => { - if(a.id < b.id) return -1 - if(a.id > b.id) return 1 + if (a.id < b.id) return -1 + if (a.id > b.id) return 1 return 0 }) } diff --git a/src/main/client/alerts/reducers/activeAlert.js b/src/main/client/alerts/reducers/activeAlert.js index a5e7f0e10..720530d27 100644 --- a/src/main/client/alerts/reducers/activeAlert.js +++ b/src/main/client/alerts/reducers/activeAlert.js @@ -54,7 +54,7 @@ const activeAlert = (state = null, action) => { case 'UPDATE_ACTIVE_ALERT_ENTITY': console.log('update entity', action.entity, action.field, action.value) foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) - if(foundIndex !== -1) { + if (foundIndex !== -1) { switch (action.field) { case 'TYPE': updatedEntity = update(action.entity, { @@ -148,7 +148,7 @@ const activeAlert = (state = null, action) => { return state case 'DELETE_ACTIVE_ALERT_AFFECTED_ENTITY': foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) - if(foundIndex !== -1) { + if (foundIndex !== -1) { entities = [ ...state.affectedEntities.slice(0, foundIndex), ...state.affectedEntities.slice(foundIndex + 1) diff --git a/src/main/client/alerts/reducers/alerts.js b/src/main/client/alerts/reducers/alerts.js index 4a87a956d..36336344e 100644 --- a/src/main/client/alerts/reducers/alerts.js +++ b/src/main/client/alerts/reducers/alerts.js @@ -115,23 +115,23 @@ const alerts = (state = { id: ent.Id, } - if(ent.AgencyId !== null) { + if (ent.AgencyId !== null) { let feed = project.feedSources.find(f => getFeedId(f) === ent.AgencyId) entity.agency = feed entity.type = 'AGENCY' } // stop goes ahead of route type and route because it's an optional field in the below - if(ent.StopId !== null) { + if (ent.StopId !== null) { entity.stop_id = ent.StopId entity.type = 'STOP' } - if(ent.RouteId !== null) { + if (ent.RouteId !== null) { entity.route_id = ent.RouteId entity.type = 'ROUTE' } - if(ent.RouteType !== null) { + if (ent.RouteType !== null) { let mode = modes.find(m => m.gtfsType === ent.RouteType) // catch any integers outside of 0 -7 range diff --git a/src/main/client/common/components/LanguageSelect.js b/src/main/client/common/components/LanguageSelect.js index 7a40495f3..c34102775 100644 --- a/src/main/client/common/components/LanguageSelect.js +++ b/src/main/client/common/components/LanguageSelect.js @@ -57,7 +57,7 @@ export default class LanguageSelect extends React.Component { cache={false} style={{marginBottom: '20px'}} onFocus={onFocus} - filterOptions={true} + filterOptions minimumInput={this.props.minimumInput !== null ? this.props.minimumInput : 1} clearable={this.props.clearable} placeholder={this.props.placeholder || placeholder} diff --git a/src/main/client/common/components/ManagerPage.js b/src/main/client/common/components/ManagerPage.js index 45b9d080d..8435c4574 100644 --- a/src/main/client/common/components/ManagerPage.js +++ b/src/main/client/common/components/ManagerPage.js @@ -64,7 +64,7 @@ export default class ManagerPage extends Component { } - {/* */} + {/* */}
    { diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index ed489e308..7365226ce 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -67,7 +67,7 @@ export default class SidebarNavItem extends Component { } const icon = this.props.image ?
    - +
    :
    b.name.toLowerCase()) return 1 + if (a.isCreating && !b.isCreating) return -1 + if (!a.isCreating && b.isCreating) return 1 + if (a.name.toLowerCase() < b.name.toLowerCase()) return -1 + if (a.name.toLowerCase() > b.name.toLowerCase()) return 1 return 0 } export function versionsSorter (a, b) { - // if(a.isCreating && !b.isCreating) return -1 - // if(!a.isCreating && b.isCreating) return 1 - if(a.feedSource.name < b.feedSource.name) return -1 - if(a.feedSource.name > b.feedSource.name) return 1 + // if (a.isCreating && !b.isCreating) return -1 + // if (!a.isCreating && b.isCreating) return 1 + if (a.feedSource.name < b.feedSource.name) return -1 + if (a.feedSource.name > b.feedSource.name) return 1 return 0 } @@ -78,7 +78,7 @@ export function generateRandomColor () { return color } // export function invertHex (hexnum) { -// if(hexnum.length != 6) { +// if (hexnum.length != 6) { // alert('Hex color must be six hex numbers in length.') // return false // } @@ -96,9 +96,9 @@ export function generateRandomColor () { // complexnum.F = '0' // // for(var i=0; i<6; i++){ -// if(!isNaN(splitnum[i])) { +// if (!isNaN(splitnum[i])) { // resultnum += simplenum[splitnum[i]] -// } else if(complexnum[splitnum[i]]){ +// } else if (complexnum[splitnum[i]]){ // resultnum += complexnum[splitnum[i]] // } else { // alert('Hex colors must only include hex numbers 0-9, and A-F') diff --git a/src/main/client/editor/actions/editor.js b/src/main/client/editor/actions/editor.js index 2bef089ce..008c9db4f 100644 --- a/src/main/client/editor/actions/editor.js +++ b/src/main/client/editor/actions/editor.js @@ -480,7 +480,7 @@ export function downloadGtfsFeed (feedVersionId) { cache: 'default', headers: { 'Authorization': 'Bearer ' + getState().user.token } }).then((response) => { - if(response.status !== 200) { + if (response.status !== 200) { console.log('error downloading gtfs+ feed', response.statusCode) dispatch(clearGtfsContent()) } @@ -583,10 +583,10 @@ export function loadGtfsEntities (tableId, rows, feedSource) { const typeLookup = {} const getDataType = function(tableId, fieldName) { const lookupKey = tableId + ':' + fieldName - if(lookupKey in typeLookup) return typeLookup[lookupKey] + if (lookupKey in typeLookup) return typeLookup[lookupKey] const fieldInfo = getConfigProperty('modules.editor.spec') .find(t => t.id === tableId).fields.find(f => f.name === fieldName) - if(!fieldInfo) return null + if (!fieldInfo) return null typeLookup[lookupKey] = fieldInfo.inputType return fieldInfo.inputType } @@ -602,17 +602,17 @@ export function loadGtfsEntities (tableId, rows, feedSource) { switch(getDataType(tableId, fieldName)) { case 'GTFS_ROUTE': const routeId = rowData[fieldName] - if(routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) + if (routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) break; case 'GTFS_STOP': const stopId = rowData[fieldName] - if(stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) + if (stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) break; } } } - if(routesToLoad.length === 0 && stopsToLoad.length === 0) return + if (routesToLoad.length === 0 && stopsToLoad.length === 0) return var loadRoutes = Promise.all(routesToLoad.map(routeId => { const url = `/api/manager/routes/${routeId}?feed=${feedSource.externalProperties.MTC.AgencyId}` diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 68f9fcd4e..300c019c3 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import { Panel, Row, Col, Table, ButtonToolbar, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Panel, Row, Col, ButtonToolbar, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' import { browserHistory } from 'react-router' import moment from 'moment' import {Icon} from '@conveyal/woonerf' @@ -18,25 +18,19 @@ export default class EditorFeedSourcePanel extends Component { deleteSnapshot: PropTypes.func.isRequired, loadFeedVersionForEditing: PropTypes.func.isRequired } - componentWillMount () { this.props.getSnapshots(this.props.feedSource) } - constructor (props) { super(props) this.state = { expanded: false } } - render () { const messages = getComponentMessages('EditorFeedSourcePanel') const hasVersions = this.props.feedSource && this.props.feedSource.feedVersions && this.props.feedSource.feedVersions.length > 0 const currentSnapshot = this.props.feedSource.editorSnapshots && this.props.feedSource.editorSnapshots.length ? this.props.feedSource.editorSnapshots.find(s => s.current) : null - const activeSnapshots = this.props.feedSource.editorSnapshots - ? this.props.feedSource.editorSnapshots.filter(s => s.current) - : [] const inactiveSnapshots = this.props.feedSource.editorSnapshots ? this.props.feedSource.editorSnapshots.filter(s => !s.current) : [] @@ -47,60 +41,52 @@ export default class EditorFeedSourcePanel extends Component { {this.props.feedSource.editorSnapshots && this.props.feedSource.editorSnapshots.length ?
    - Active snapshot}> - {currentSnapshot - ? - - - : - No active snapshot - - } - - Inactive snapshots}> - - {inactiveSnapshots.length === 0 - ? No other snapshots - : inactiveSnapshots.map(s => { - return ( - - ) - }) - } - {/* activeSnapshots.length === 0 - ? No other snapshots - : activeSnapshots.map(s => { - return ( - - ) - }) - */} + Active snapshot}> + {currentSnapshot + ? + + + : + No active snapshot - -
    + } + + Inactive snapshots}> + + {inactiveSnapshots.length === 0 + ? No other snapshots + : inactiveSnapshots.map(s => { + return ( + + ) + }) + } + + +
    :
    -

    No snapshots loaded.

    - - {' '}or{' '} - -
    +

    No snapshots loaded.

    + + {' '}or{' '} + +
    } diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js index 1465e4144..cecb4b505 100644 --- a/src/main/client/editor/components/EditorMap.js +++ b/src/main/client/editor/components/EditorMap.js @@ -1,7 +1,7 @@ import React, { Component, PropTypes } from 'react' import { Map, Marker, Popup, Polyline, TileLayer, FeatureGroup, ZoomControl, LayersControl, GeoJson } from 'react-leaflet' import { divIcon, Browser } from 'leaflet' -import { Button, Dropdown, OverlayTrigger, Tooltip, Row, Col, ButtonGroup, MenuItem, SplitButton, FormGroup, ControlLabel } from 'react-bootstrap' +import { MenuItem, SplitButton } from 'react-bootstrap' import { shallowEqual } from 'react-pure-render' import {Icon} from '@conveyal/woonerf' import ll from 'lonlng' @@ -17,7 +17,7 @@ import { generateUID } from '../../common/util/util' import { stopToGtfs } from '../util/gtfs' import { getUserMetadataProperty } from '../../common/util/user' import { getConfigProperty } from '../../common/util/config' -import MinuteSecondInput from './MinuteSecondInput' +import PatternStopPopup from './PatternStopPopup' // import StopMarkersLayer from './StopMarkersLayer' import StopLayer from '../../scenario-editor/components/StopLayer' import {polyline as getPolyline, getSegment} from '../../scenario-editor/utils/valhalla' @@ -236,15 +236,14 @@ export default class EditorMap extends Component { async constructStop (latlng) { let stopLatLng = this.getStopLatLng(latlng) let result = await reverse(latlng) - console.log(result) - let stop_id = generateUID() - let stop_name = `New Stop (${stop_id})` + let stopId = generateUID() + let stopName = `New Stop (${stopId})` if (result && result.address) { - stop_name = result.address.Address + stopName = result.address.Address } return { - stop_id, - stop_name, + stop_id: stopId, + stop_name: stopName, feedId: this.props.feedSource.id, ...stopLatLng } @@ -536,7 +535,6 @@ export default class EditorMap extends Component { // iconSize: [24, 24], html: `` }) - let endPoint let beginPoint if (!route) { return null @@ -556,8 +554,6 @@ export default class EditorMap extends Component { return null } beginPoint = latLngs[0] - endPoint = latLngs[latLngs.length - 1] - let lineColor = this.props.activeEntity.route_color && this.props.editSettings.editGeometry ? '#F3F315' // yellow if editing : this.props.activeEntity.route_color // otherwise, use route color if it exists @@ -591,14 +587,6 @@ export default class EditorMap extends Component { let nextPosition = along(this.props.activePattern.shape, distance + 5, 'meters') const dir = position && nextPosition ? bearing(position, nextPosition) : 0 - const cos = Math.cos(bearing * (Math.PI / 180)) - const sin = Math.sin(bearing * (Math.PI / 180)) - const icon = divIcon({ - className: '', - iconAnchor: [50 * cos, 50 * sin], - iconSize: [24, 24], - html: `` - }) const color = '#000' const arrowIcon = divIcon({ // html: ``, @@ -628,16 +616,9 @@ export default class EditorMap extends Component { if (s.stopId && this.props.editSettings.snapToStops) { return null } - let nextStop let prevControlPoint = this.state.controlPoints[index - 1] let nextControlPoint = this.state.controlPoints[index + 1] - for (var i = index + 1; i < this.state.controlPoints.length; i++) { - if (this.state.controlPoints[i].stopId) { - nextStop = this.state.controlPoints[i] - } - } - let begin = prevControlPoint ? prevControlPoint.point : along(this.props.activePattern.shape, 0, 'meters') @@ -683,32 +664,31 @@ export default class EditorMap extends Component { } }} color='black' - > - + /> ) }) : null } {beginPoint && this.props.editSettings.editGeometry && this.props.activePattern ? { - let beginStop = this.props.stops.find(s => s.id === this.props.activePattern.patternStops[0].stopId) - let begin = point([beginStop.stop_lon, beginStop.stop_lat]) - const timerFunction = () => { - this.handlePatternEdit('controlPointBegin', null, begin) - } - timerFunction() - timer = setInterval(timerFunction, 1000) - }} - onDragEnd={(e) => { - this.handleControlPointDragEnd(e, timer, 'controlPointBegin') - }} - color='black' - /> + position={beginPoint} + icon={circleIcon} + ref='controlPointBegin' + draggable + onDragStart={(e) => { + let beginStop = this.props.stops.find(s => s.id === this.props.activePattern.patternStops[0].stopId) + let begin = point([beginStop.stop_lon, beginStop.stop_lat]) + const timerFunction = () => { + this.handlePatternEdit('controlPointBegin', null, begin) + } + timerFunction() + timer = setInterval(timerFunction, 1000) + }} + onDragEnd={(e) => { + this.handleControlPointDragEnd(e, timer, 'controlPointBegin') + }} + color='black' + /> : null } , @@ -727,11 +707,6 @@ export default class EditorMap extends Component { ${index + 1} `, - // html: ` - // - // 27 - // `, - // html: ``, className: '', iconSize: [24, 24] }) @@ -740,133 +715,19 @@ export default class EditorMap extends Component { position={[stop.stop_lat, stop.stop_lon]} style={{cursor: 'move'}} icon={patternStopIcon} - // label={`${index + 1} - ${stop.stop_name}`} - // labelOptions={{ - // // noHide: true, - // direction: 'right' - // }} - // onClick={(e) => { - // - // }} ref={`${this.props.activePattern.id}-${s.stopId}-${index}`} key={`${this.props.activePattern.id}-${s.stopId}-${index}`} > -
    -
    {index + 1}. {stop.stop_name}
    - - - - - Edit stop}> - - - Remove from pattern}> - - - { - this.addStopToPattern(this.props.activePattern, stop, key) - }} - > - - - - = this.props.activePattern.patternStops.length - 2} value={this.props.activePattern.patternStops.length} eventKey={this.props.activePattern.patternStops.length}> - Add to end (default) - - {this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((stop, i) => { - let addIndex = this.props.activePattern.patternStops.length - i - if (index === this.props.activePattern.patternStops.length - 1 && index === addIndex - 1) { - return null - } - // disable adding stop to current position or directly before/after current position - return ( - = addIndex - 2 && index <= addIndex} - value={addIndex - 1} - key={i} - eventKey={addIndex - 1} - > - {addIndex === 1 ? 'Add to beginning' : `Insert as stop #${addIndex}`} - - ) - })} - - - - - - - - - Travel time - { - let patternStops = [...this.props.activePattern.patternStops] - patternStops[index].defaultTravelTime = value - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) - }} - /> - - - - - Dwell time - { - let patternStops = [...this.props.activePattern.patternStops] - patternStops[index].defaultDwellTime = +evt.target.value - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) - }} - /> - - - -
    +
    ) @@ -886,8 +747,7 @@ export default class EditorMap extends Component { if (!bounds) return false if (stop.stop_lat > bounds.getNorth() || stop.stop_lat < bounds.getSouth() || stop.stop_lon > bounds.getEast() || stop.stop_lon < bounds.getWest()) { return false - } - else { + } else { return true } }) @@ -903,7 +763,7 @@ export default class EditorMap extends Component { `, // html: ``, className: '', - iconSize: [24, 24], + iconSize: [24, 24] }) return (
    `, className: '', - iconSize: [24, 24], + iconSize: [24, 24] }) const activeBusIcon = divIcon({ html: ` @@ -1015,11 +875,11 @@ export default class EditorMap extends Component { }} onClick={(e) => { // set active entity - if (!isActive) + if (!isActive) { this.props.setActiveEntity(this.props.feedSource.id, 'stop', stop) + } }} - > - + /> ) return marker }) @@ -1029,32 +889,12 @@ export default class EditorMap extends Component { return null } } - - getBounds (component, entities) { - switch (component) { - // case 'route': - // return entities.map(route => { - // return ( - // null - // ) - // }) - // case 'stop': - // return entities.map(stop => { - // return ( - // null - // ) - // }) - default: - return null - } - } overlayAdded (e) { if (e.name === 'Route alignments' && !this.props.tripPatterns) { this.props.fetchTripPatterns(this.props.feedSource.id) } } render () { - // console.log(this.props) const mapLayers = [ { name: 'Streets', @@ -1090,7 +930,7 @@ export default class EditorMap extends Component { /> } ] - const { feedSource, activeComponent, activeEntity } = this.props + const { feedSource } = this.props const offset = 0.005 let feedSourceBounds = feedSource && feedSource.latestValidation && feedSource.latestValidation.bounds ? [[feedSource.latestValidation.bounds.north + offset, feedSource.latestValidation.bounds.west - offset], [feedSource.latestValidation.bounds.south - offset, feedSource.latestValidation.bounds.east + offset]] @@ -1111,7 +951,6 @@ export default class EditorMap extends Component { } if (this.props.hidden) { mapStyle.display = 'none' - // return null } let mapProps = { ref: 'map', @@ -1157,9 +996,9 @@ export default class EditorMap extends Component { } {this.props.mapState.routesGeojson ? + key={this.props.mapState.routesGeojson.key} + data={this.props.mapState.routesGeojson} + /> : null } diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 381ce6c81..cdd3715ee 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -475,7 +475,7 @@ export default class EntityDetails extends Component { { let props = {} props[field.name] = evt.target.value @@ -1310,20 +1310,20 @@ export default class EntityDetails extends Component { {this.props.activeComponent === 'route' && entity - ?
    - - -
    + ? + + + : iconName - ?
    - - -
    + ? + + + // schedule exception icon if no icon founds - :
    - - -
    + : + + + } {' '} diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index 819c49021..729e3d3ce 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -305,8 +305,7 @@ export default class EntityList extends Component { } }) // this.props.deleteEntity(this.props.feedSource.id, this.props.activeComponent, activeEntity.id) - } - else { + } else { this.props.showConfirmModal({ title: `Delete ${+this.state.toIndex - +this.state.fromindex} ${this.props.activeComponent}s?`, body: `Are you sure you want to delete these ${this.state.toIndex - this.state.fromindex} ${this.props.activeComponent}s?`, @@ -331,57 +330,56 @@ export default class EntityList extends Component { this.props.activeComponent === 'stop' ? Right-click map for new stop : + bsSize='small' + disabled={this.props.entities && this.props.entities.findIndex(e => e.id === 'new') !== -1} + onClick={() => { + this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent) + }} + > New {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent} + }
    {/* Table view button */}
    {this.props.activeComponent === 'calendar' || this.props.activeComponent === 'scheduleexception' ? - : this.props.activeComponent === 'stop' || this.props.activeComponent === 'route' - ? { - if (!value) { - this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent) - } else { - this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent, value.entity) + { + if (this.props.activeComponent !== 'calendar') { + // browserHistory.push(`/feed/${this.props.feedSource.id}/edit/calendar`) + this.props.setActiveEntity(this.props.feedSource.id, 'calendar') } }} - /> + > + Calendars + + { + if (this.props.activeComponent !== 'scheduleexception') { + // browserHistory.push(`/feed/${this.props.feedSource.id}/edit/scheduleexception`) + this.props.setActiveEntity(this.props.feedSource.id, 'scheduleexception') + } + }} + > + Exceptions + + + : this.props.activeComponent === 'stop' || this.props.activeComponent === 'route' + ? { + if (!value) { + this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent) + } else { + this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent, value.entity) + } + }} + /> : null } {!this.props.tableView diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index e27b00e57..748a41bd2 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -86,7 +86,7 @@ export default class FeedInfoPanel extends Component { } }} > - + {/* Navigation dropdown */} diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 052925547..54defcced 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -1,6 +1,7 @@ import React, {Component, PropTypes} from 'react' import Helmet from 'react-helmet' import { shallowEqual } from 'react-pure-render' +import { browserHistory } from 'react-router' import CurrentStatusMessage from '../../common/containers/CurrentStatusMessage' import ConfirmModal from '../../common/components/ConfirmModal.js' @@ -113,7 +114,10 @@ export default class GtfsEditor extends Component { // console.log(this.props) const feedSource = this.props.feedSource const editingIsDisabled = this.props.feedSource ? !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') : true - + if (this.props.feedSource && editingIsDisabled) { + console.log('editing disabled') + // browserHistory.push(`/feed/${this.props.feedSource.id}`) + } let listWidth = 220 let detailsWidth = 300 let entityDetails = this.props.activeEntityId diff --git a/src/main/client/editor/components/GtfsInput.js b/src/main/client/editor/components/GtfsInput.js index 437db4fd0..8c445598a 100644 --- a/src/main/client/editor/components/GtfsInput.js +++ b/src/main/client/editor/components/GtfsInput.js @@ -184,7 +184,7 @@ export default class GtfsInput extends Component { {basicLabel} { console.log(input) diff --git a/src/main/client/editor/components/GtfsTable.js b/src/main/client/editor/components/GtfsTable.js index 8114e2302..c315f10fe 100644 --- a/src/main/client/editor/components/GtfsTable.js +++ b/src/main/client/editor/components/GtfsTable.js @@ -20,7 +20,7 @@ export default class GtfsTable extends Component { } componentWillReceiveProps (nextProps) { - if(this.props.table !== nextProps.table) { + if (this.props.table !== nextProps.table) { this.setState({ currentPage: 1 }) } } @@ -34,9 +34,9 @@ export default class GtfsTable extends Component { } getActiveRowData (currentPage) { - if(!this.props.tableData) return [] + if (!this.props.tableData) return [] const tableValidation = this.props.validation || [] - if(this.state.visibility === 'validation' && tableValidation.length < 5000) { + if (this.state.visibility === 'validation' && tableValidation.length < 5000) { return this.props.tableData .filter(record => (tableValidation.find(v => v.rowIndex === record.origRowIndex))) .slice((currentPage - 1) * recordsPerPage, @@ -287,7 +287,7 @@ export default class GtfsTable extends Component { onKeyUp={(e) => { if (e.keyCode == 13) { const newPage = parseInt(e.target.value) - if(newPage > 0 && newPage <= pageCount) { + if (newPage > 0 && newPage <= pageCount) { e.target.value = '' this.setState({ currentPage: newPage }) } diff --git a/src/main/client/editor/components/GtfsTableEditor.js b/src/main/client/editor/components/GtfsTableEditor.js index 8bbf4f1b0..7d4963bcf 100644 --- a/src/main/client/editor/components/GtfsTableEditor.js +++ b/src/main/client/editor/components/GtfsTableEditor.js @@ -28,7 +28,7 @@ export default class GtfsTableEditor extends Component { const zip = new JSZip() for(const table of getConfigProperty('modules.editor.spec')) { - if(!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue + if (!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue let fileContent = '' // white the header line @@ -53,7 +53,7 @@ export default class GtfsTableEditor extends Component { } render () { - if(!this.props.feedSource) return null + if (!this.props.feedSource) return null const editingIsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') const buttonStyle = { display: 'block', @@ -109,7 +109,7 @@ export default class GtfsTableEditor extends Component { }} > {this.props.validation && (table.id in this.props.validation) - ? + ? : null } {table.id} diff --git a/src/main/client/editor/components/GtfsVersionSummary.js b/src/main/client/editor/components/GtfsVersionSummary.js index fcfab2f5a..55afdf283 100644 --- a/src/main/client/editor/components/GtfsVersionSummary.js +++ b/src/main/client/editor/components/GtfsVersionSummary.js @@ -1,37 +1,39 @@ import React, {Component, PropTypes} from 'react' -import { Panel, Row, Col, Table, Input, Button, Glyphicon, Well, Alert } from 'react-bootstrap' -import { Link, browserHistory } from 'react-router' +import { Panel, Row, Col, Table, Button, Glyphicon, Alert } from 'react-bootstrap' +import { browserHistory } from 'react-router' import moment from 'moment' import { getConfigProperty } from '../../common/util/config' export default class GtfsVersionSummary extends Component { - + static propTypes = { + editor: PropTypes.object + } constructor (props) { super(props) this.state = { expanded: false } } isTableIncluded (tableId) { - if(!this.props.editor.tableData) return null + if (!this.props.editor.tableData) return null return tableId in this.props.editor.tableData ? 'Yes' : 'No' } tableRecordCount (tableId) { - if(!this.props.editor.tableData) return null - if(!(tableId in this.props.editor.tableData)) return 'N/A' + if (!this.props.editor.tableData) return null + if (!(tableId in this.props.editor.tableData)) return 'N/A' return this.props.editor.tableData[tableId].length.toLocaleString() } validationIssueCount (tableId) { - if(!this.props.editor.validation) return null - if(!(tableId in this.props.editor.validation)) return 'None' + if (!this.props.editor.validation) return null + if (!(tableId in this.props.editor.validation)) return 'None' return this.props.editor.validation[tableId].length.toLocaleString() } feedStatus () { - if(!this.props.editor.timestamp) return null - if(!this.gtfsPlusEdited()) return GTFS+ data for this feed version has not been edited. + if (!this.props.editor.timestamp) return null + if (!this.gtfsPlusEdited()) return GTFS+ data for this feed version has not been edited. return GTFS+ Data updated {moment(this.props.editor.timestamp).format('MMM. DD, YYYY, h:MMa')} } @@ -44,7 +46,7 @@ export default class GtfsVersionSummary extends Component { const publishingIsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.version.feedSource.projectId, this.props.version.feedSource.id, 'approve-gtfs') const header = (

    { - if(!this.state.expanded) this.props.gtfsPlusDataRequested(this.props.version) + if (!this.state.expanded) this.props.gtfsPlusDataRequested(this.props.version) this.setState({ expanded: !this.state.expanded }) }}> GTFS+ for this Version @@ -72,17 +74,17 @@ export default class GtfsVersionSummary extends Component { {this.gtfsPlusEdited() ? + bsSize='large' + disabled={publishingIsDisabled} + bsStyle='primary' + style={{ marginLeft: '6px' }} + onClick={() => { + this.props.publishClicked(this.props.version) + this.setState({ expanded: false }) + }} + > + Publish as New Version + : null } @@ -96,7 +98,7 @@ export default class GtfsVersionSummary extends Component { Included? Records Validation Issues - + @@ -106,7 +108,7 @@ export default class GtfsVersionSummary extends Component { {this.isTableIncluded(table.id)} {this.tableRecordCount(table.id)} {this.validationIssueCount(table.id)} - + ) })} diff --git a/src/main/client/editor/components/HourMinuteInput.js b/src/main/client/editor/components/HourMinuteInput.js index c7d066566..fff73547b 100644 --- a/src/main/client/editor/components/HourMinuteInput.js +++ b/src/main/client/editor/components/HourMinuteInput.js @@ -19,11 +19,11 @@ export default class HourMinuteInput extends Component { let seconds = this.convertStringToSeconds(value) if (seconds === this.state.seconds) { this.setState({string: value}) - } - else { + } else { this.setState({seconds, string: value}) - if (typeof this.props.onChange !== 'undefined') + if (typeof this.props.onChange !== 'undefined') { this.props.onChange(seconds) + } } } convertSecondsToString (seconds) { @@ -37,17 +37,14 @@ export default class HourMinuteInput extends Component { // if both hours and minutes are present if (!isNaN(hourMinute[0]) && !isNaN(hourMinute[1])) { return Math.abs(+hourMinute[0]) * 60 * 60 + Math.abs(+hourMinute[1]) * 60 - } - // if less than one hour - else if (isNaN(hourMinute[0])) { + } else if (isNaN(hourMinute[0])) { + // if less than one hour return Math.abs(+hourMinute[1]) * 60 - } - // if minutes are not present - else if (isNaN(hourMinute[1])) { + } else if (isNaN(hourMinute[1])) { + // if minutes are not present return Math.abs(+hourMinute[0]) * 60 * 60 - } - // if no input - else { + } else { + // if no input return 0 } } diff --git a/src/main/client/editor/components/PatternStopPopup.js b/src/main/client/editor/components/PatternStopPopup.js new file mode 100644 index 000000000..b108b8ca2 --- /dev/null +++ b/src/main/client/editor/components/PatternStopPopup.js @@ -0,0 +1,121 @@ +import React, { Component } from 'react' +import MinuteSecondInput from './MinuteSecondInput' +import { Button, Dropdown, OverlayTrigger, Tooltip, Row, Col, ButtonGroup, MenuItem, FormGroup, ControlLabel } from 'react-bootstrap' +import { Icon } from '@conveyal/woonerf' + +export default class PatternStopPopup extends Component { + render () { + const { stop, index, i, patternStop, activePattern } = this.props + return ( +
    +
    {index + 1}. {stop.stop_name}
    + + + + + Edit stop}> + + + Remove from pattern}> + + + this.props.addStopToPattern(activePattern, stop, key)} + > + + + + = activePattern.patternStops.length - 2} value={activePattern.patternStops.length} eventKey={activePattern.patternStops.length}> + Add to end (default) + + {activePattern.patternStops && activePattern.patternStops.map((stop, i) => { + let addIndex = activePattern.patternStops.length - i + if (index === activePattern.patternStops.length - 1 && index === addIndex - 1) { + return null + } + // disable adding stop to current position or directly before/after current position + return ( + = addIndex - 2 && index <= addIndex} + value={addIndex - 1} + key={i} + eventKey={addIndex - 1} + > + {addIndex === 1 ? 'Add to beginning' : `Insert as stop #${addIndex}`} + + ) + })} + + + + + + + + + Travel time + { + let patternStops = [...activePattern.patternStops] + patternStops[index].defaultTravelTime = value + this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + }} + /> + + + + + Dwell time + { + let patternStops = [...activePattern.patternStops] + patternStops[index].defaultDwellTime = +evt.target.value + this.props.updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + }} + /> + + + +
    + ) + } +} diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index 549922b29..5185cd45d 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -280,7 +280,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { updateControlPoint: (index, point, distance) => { dispatch(updateControlPoint(index, point, distance)) }, // EDITOR UI - setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) }, + setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) } } } diff --git a/src/main/client/editor/containers/ActiveGtfsTableEditor.js b/src/main/client/editor/containers/ActiveGtfsTableEditor.js index 188f75194..bdb8755a8 100644 --- a/src/main/client/editor/containers/ActiveGtfsTableEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsTableEditor.js @@ -49,8 +49,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { - if(!initialProps.feedSource) dispatch(fetchFeedSourceAndProject(feedSourceId)) - if(!initialProps.tableData) dispatch(downloadGtfsFeed(feedVersionId)) + if (!initialProps.feedSource) dispatch(fetchFeedSourceAndProject(feedSourceId)) + if (!initialProps.tableData) dispatch(downloadGtfsFeed(feedVersionId)) if (initialProps.currentTable) dispatch(getGtfsTable(initialProps.currentTable, feedSourceId)) }, newRowClicked: (tableId) => { @@ -76,7 +76,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) }, newRowsDisplayed: (tableId, rows, feedSource) => { - if(feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) + if (feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) }, gtfsEntitySelected: (type, entity) => { dispatch(receiveGtfsEntities([entity])) diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js index 6c06676e3..05af640cc 100644 --- a/src/main/client/editor/containers/ActiveTripPatternList.js +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -77,7 +77,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { return dispatch(newGtfsEntity(feedSourceId, component, props, save)) }, - undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, + undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) } } } diff --git a/src/main/client/editor/reducers/editor.js b/src/main/client/editor/reducers/editor.js index 69b45e403..e89f6fd15 100644 --- a/src/main/client/editor/reducers/editor.js +++ b/src/main/client/editor/reducers/editor.js @@ -624,8 +624,8 @@ const editor = (state = defaultState, action) => { sortedTrips = trips ? action.trips.filter(t => t.useFrequency === activePattern.useFrequency) // filter out based on useFrequency .sort((a, b) => { - // if(a.isCreating && !b.isCreating) return -1 - // if(!a.isCreating && b.isCreating) return 1 + // if (a.isCreating && !b.isCreating) return -1 + // if (!a.isCreating && b.isCreating) return 1 if (a.stopTimes[0].departureTime < b.stopTimes[0].departureTime) return -1 if (a.stopTimes[0].departureTime > b.stopTimes[0].departureTime) return 1 return 0 @@ -747,7 +747,7 @@ const editor = (state = defaultState, action) => { newTableData = {} for(let i = 0; i < action.filenames.length; i++) { const lines = action.fileContent[i].split('\n') - if(lines.length < 2) continue + if (lines.length < 2) continue fields = lines[0].split(',') newTableData[action.filenames[i].split('.')[0]] = lines.slice(1) .filter(line => line.split(',').length === fields.length) @@ -767,7 +767,7 @@ const editor = (state = defaultState, action) => { case 'ADD_GTFSEDITOR_ROW': // create this table if it doesn already exist - if(!(action.tableId in state.tableData)) { + if (!(action.tableId in state.tableData)) { return update(state, {tableData: {$merge: {[action.tableId]: [action.rowData]} } diff --git a/src/main/client/gtfs/components/GtfsFilter.js b/src/main/client/gtfs/components/GtfsFilter.js index ce575c33a..4baebd2f9 100644 --- a/src/main/client/gtfs/components/GtfsFilter.js +++ b/src/main/client/gtfs/components/GtfsFilter.js @@ -22,8 +22,8 @@ export default class GtfsFilter extends React.Component { var compare = function (a, b) { var aName = a.shortName || a.name var bName = b.shortName || b.name - if(aName < bName) return -1 - if(aName > bName) return 1 + if (aName < bName) return -1 + if (aName > bName) return 1 return 0 } diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index e451beaff..7ad5c1547 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -268,7 +268,7 @@ export default class GtfsMap extends Component { ) }) } - if(this.state && this.state.lastClicked) { + if (this.state && this.state.lastClicked) { comps.push( { const feedId = ownProps.version && ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { - // if(!initialProps.routes.fetchStatus.fetched) { + // if (!initialProps.routes.fetchStatus.fetched) { // dispatch(fetchRoutes(feedId)) // } - // if(!initialProps.patterns.fetchStatus.fetched) { + // if (!initialProps.patterns.fetchStatus.fetched) { // dispatch(fetchPatterns(feedId, null)) // } }, diff --git a/src/main/client/gtfsplus/actions/gtfsplus.js b/src/main/client/gtfsplus/actions/gtfsplus.js index 72fc09165..3fe5ac96a 100644 --- a/src/main/client/gtfsplus/actions/gtfsplus.js +++ b/src/main/client/gtfsplus/actions/gtfsplus.js @@ -74,7 +74,7 @@ export function downloadGtfsPlusFeed (feedVersionId) { cache: 'default', headers: { 'Authorization': 'Bearer ' + getState().user.token } }).then((response) => { - if(response.status !== 200) { + if (response.status !== 200) { console.log('error downloading gtfs+ feed', response.statusCode) dispatch(clearGtfsPlusContent()) } @@ -177,10 +177,10 @@ export function loadGtfsEntities (tableId, rows, feedSource) { const typeLookup = {} const getDataType = function(tableId, fieldName) { const lookupKey = tableId + ':' + fieldName - if(lookupKey in typeLookup) return typeLookup[lookupKey] + if (lookupKey in typeLookup) return typeLookup[lookupKey] const fieldInfo = getConfigProperty('modules.gtfsplus.spec') .find(t => t.id === tableId).fields.find(f => f.name === fieldName) - if(!fieldInfo) return null + if (!fieldInfo) return null typeLookup[lookupKey] = fieldInfo.inputType return fieldInfo.inputType } @@ -196,17 +196,17 @@ export function loadGtfsEntities (tableId, rows, feedSource) { switch(getDataType(tableId, fieldName)) { case 'GTFS_ROUTE': const routeId = rowData[fieldName] - if(routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) + if (routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) break; case 'GTFS_STOP': const stopId = rowData[fieldName] - if(stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) + if (stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) break; } } } - if(routesToLoad.length === 0 && stopsToLoad.length === 0) return + if (routesToLoad.length === 0 && stopsToLoad.length === 0) return const feedId = getFeedId(feedSource) var loadRoutes = Promise.all(routesToLoad.map(routeId => { const url = `/api/manager/routes/${routeId}?feed=${feedId}` diff --git a/src/main/client/gtfsplus/components/GtfsPlusEditor.js b/src/main/client/gtfsplus/components/GtfsPlusEditor.js index d7c903d93..715cf2d08 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusEditor.js +++ b/src/main/client/gtfsplus/components/GtfsPlusEditor.js @@ -28,7 +28,7 @@ export default class GtfsPlusEditor extends Component { const zip = new JSZip() for(const table of getConfigProperty('modules.gtfsplus.spec')) { - if(!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue + if (!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue let fileContent = '' // white the header line @@ -53,7 +53,7 @@ export default class GtfsPlusEditor extends Component { } render () { - if(!this.props.feedSource) return null + if (!this.props.feedSource) return null const editingIsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') const buttonStyle = { display: 'block', @@ -107,7 +107,7 @@ export default class GtfsPlusEditor extends Component { }} > {this.props.validation && (table.id in this.props.validation) - ? + ? : null } {table.id} diff --git a/src/main/client/gtfsplus/components/GtfsPlusTable.js b/src/main/client/gtfsplus/components/GtfsPlusTable.js index 498429701..d6a440d18 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusTable.js +++ b/src/main/client/gtfsplus/components/GtfsPlusTable.js @@ -19,7 +19,7 @@ export default class GtfsPlusTable extends Component { } componentWillReceiveProps (nextProps) { - if(this.props.table !== nextProps.table) { + if (this.props.table !== nextProps.table) { this.setState({ currentPage: 1 }) } } @@ -33,9 +33,9 @@ export default class GtfsPlusTable extends Component { } getActiveRowData (currentPage) { - if(!this.props.tableData) return [] + if (!this.props.tableData) return [] const tableValidation = this.props.validation || [] - if(this.state.visibility === 'validation' && tableValidation.length < 5000) { + if (this.state.visibility === 'validation' && tableValidation.length < 5000) { return this.props.tableData .filter(record => (tableValidation.find(v => v.rowIndex === record.origRowIndex))) .slice((currentPage - 1) * recordsPerPage, @@ -194,7 +194,7 @@ export default class GtfsPlusTable extends Component { onKeyUp={(e) => { if (e.keyCode == 13) { const newPage = parseInt(e.target.value) - if(newPage > 0 && newPage <= pageCount) { + if (newPage > 0 && newPage <= pageCount) { e.target.value = '' this.setState({ currentPage: newPage }) } diff --git a/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js b/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js index 76c51d626..4ac1b07ae 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js +++ b/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js @@ -15,25 +15,25 @@ export default class GtfsPlusVersionSummary extends Component { this.props.gtfsPlusDataRequested(this.props.version) } isTableIncluded (tableId) { - if(!this.props.gtfsplus.tableData) return null + if (!this.props.gtfsplus.tableData) return null return tableId in this.props.gtfsplus.tableData ? 'Yes' : 'No' } tableRecordCount (tableId) { - if(!this.props.gtfsplus.tableData) return null - if(!(tableId in this.props.gtfsplus.tableData)) return 'N/A' + if (!this.props.gtfsplus.tableData) return null + if (!(tableId in this.props.gtfsplus.tableData)) return 'N/A' return this.props.gtfsplus.tableData[tableId].length.toLocaleString() } validationIssueCount (tableId) { - if(!this.props.gtfsplus.validation) return null - if(!(tableId in this.props.gtfsplus.validation)) return 'None' + if (!this.props.gtfsplus.validation) return null + if (!(tableId in this.props.gtfsplus.validation)) return 'None' return this.props.gtfsplus.validation[tableId].length.toLocaleString() } feedStatus () { - if(!this.props.gtfsplus.timestamp) return null - if(!this.gtfsPlusEdited()) return GTFS+ data for this feed version has not been edited. + if (!this.props.gtfsplus.timestamp) return null + if (!this.gtfsPlusEdited()) return GTFS+ data for this feed version has not been edited. return GTFS+ Data updated {moment(this.props.gtfsplus.timestamp).format('MMM. DD, YYYY, h:MMa')} } @@ -46,7 +46,7 @@ export default class GtfsPlusVersionSummary extends Component { const publishingIsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.version.feedSource.projectId, this.props.version.feedSource.id, 'approve-gtfs') const header = (

    { - if(!this.state.expanded) this.props.gtfsPlusDataRequested(this.props.version) + if (!this.state.expanded) this.props.gtfsPlusDataRequested(this.props.version) this.setState({ expanded: !this.state.expanded }) }}> GTFS+ for this Version diff --git a/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js b/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js index 27dcbf040..bdedfa9e6 100644 --- a/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js +++ b/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js @@ -46,8 +46,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { - if(!initialProps.feedSource) dispatch(fetchFeedSourceAndProject(feedSourceId)) - if(!initialProps.tableData) dispatch(downloadGtfsPlusFeed(feedVersionId)) + if (!initialProps.feedSource) dispatch(fetchFeedSourceAndProject(feedSourceId)) + if (!initialProps.tableData) dispatch(downloadGtfsPlusFeed(feedVersionId)) }, newRowClicked: (tableId) => { dispatch(addGtfsPlusRow(tableId)) @@ -66,7 +66,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) }, newRowsDisplayed: (tableId, rows, feedSource) => { - if(feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) + if (feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) }, gtfsEntitySelected: (type, entity) => { dispatch(receiveGtfsEntities([entity])) diff --git a/src/main/client/gtfsplus/reducers/gtfsplus.js b/src/main/client/gtfsplus/reducers/gtfsplus.js index 0311a0b26..ee1b0d2d6 100644 --- a/src/main/client/gtfsplus/reducers/gtfsplus.js +++ b/src/main/client/gtfsplus/reducers/gtfsplus.js @@ -36,7 +36,7 @@ const gtfsplus = (state = { let newTableData = {} for(let i = 0; i < action.filenames.length; i++) { const lines = action.fileContent[i].split(/\r\n|\r|\n/g) - if(lines.length < 2) continue + if (lines.length < 2) continue console.log(lines[0]) const fields = lines[0].split(',') console.log(fields) @@ -61,7 +61,7 @@ const gtfsplus = (state = { case 'ADD_GTFSPLUS_ROW': // create this table if it doesn't already exist - if(!(action.tableId in state.tableData)) { + if (!(action.tableId in state.tableData)) { return update(state, {tableData: {$merge: {[action.tableId]: [action.rowData]} } @@ -106,8 +106,8 @@ const gtfsplus = (state = { case 'RECEIVE_GTFS_PLUS_ENTITIES': const getType = function (entity) { - if(entity.hasOwnProperty('route_id')) return 'route' - if(entity.hasOwnProperty('stop_id')) return 'stop' + if (entity.hasOwnProperty('route_id')) return 'route' + if (entity.hasOwnProperty('stop_id')) return 'stop' } const newLookupEntries = {} @@ -126,7 +126,7 @@ const gtfsplus = (state = { case 'RECEIVE_GTFSPLUS_VALIDATION': const validationTable = {} for(const issue of action.validationIssues) { - if(!(issue.tableId in validationTable)) { + if (!(issue.tableId in validationTable)) { validationTable[issue.tableId] = [] } validationTable[issue.tableId].push(issue) diff --git a/src/main/client/manager/actions/user.js b/src/main/client/manager/actions/user.js index a4d0eb906..91135291c 100644 --- a/src/main/client/manager/actions/user.js +++ b/src/main/client/manager/actions/user.js @@ -28,7 +28,7 @@ export function checkExistingLogin() { return function (dispatch, getState) { dispatch(checkingExistingLogin()) var login = getState().user.auth0.checkExistingLogin() - if(login) { + if (login) { return login.then((userTokenAndProfile) => { if (userTokenAndProfile) { dispatch(userLoggedIn(userTokenAndProfile.token, userTokenAndProfile.profile)) diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index f3212ad9d..bc6761c2c 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -93,7 +93,7 @@ export default class DeploymentViewer extends Component { {getMessage(messages, 'versions')}

    )} collapsible - defaultExpanded={true} + defaultExpanded > diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index 130021324..c94c745df 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -309,7 +309,7 @@ export default class FeedSourceViewer extends Component {
    • Last Updated: {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
    • Number of versions: {fs.feedVersionCount}
    • -
      +
    • URL:
    • -
      +
    diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index 74cd0ca8f..6f93e507f 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -1,5 +1,5 @@ import React, {Component, PropTypes} from 'react' -import { Row, Col, Image, Button, Panel, ControlLabel, Label, Tabs, Tab, Glyphicon, FormControl, ButtonGroup, ButtonToolbar, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Row, Col, Button, Panel, ControlLabel, Label, Tabs, Tab, ButtonGroup, ListGroup, ListGroupItem } from 'react-bootstrap' import moment from 'moment' import {Icon} from '@conveyal/woonerf' import numeral from 'numeral' @@ -7,7 +7,7 @@ import Rcslider from 'rc-slider' import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' -import { getComponentMessages, getMessage, getConfigProperty, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' +import { getComponentMessages, getMessage, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' import { getProfileLink } from '../../common/util/util' // import Feed from './reporter/containers/Feed' @@ -31,27 +31,18 @@ export default class FeedVersionReport extends Component { version: PropTypes.object, versions: PropTypes.array, feedVersionIndex: PropTypes.number, - // versionSection: PropTypes.string, - isPublic: PropTypes.bool, hasVersions: PropTypes.bool, - // listView: PropTypes.bool, - - // newNotePosted: PropTypes.func, - // notesRequested: PropTypes.func, - // fetchValidationResult: PropTypes.func, feedVersionRenamed: PropTypes.func, fetchValidationResult: PropTypes.func, - publishFeedVersion: PropTypes.func, - // downloadFeedClicked: PropTypes.func, - // loadFeedVersionForEditing: PropTypes.func + publishFeedVersion: PropTypes.func } constructor (props) { super(props) this.state = { tab: 'feed', mapHeight: MAP_HEIGHTS[0], - isochroneBand: 60 * 60, + isochroneBand: 60 * 60 } } getVersionDateLabel (version) { @@ -68,11 +59,9 @@ export default class FeedVersionReport extends Component { renderIsochroneMessage (version) { if (version.isochrones && version.isochrones.features) { return 'Move marker or change date/time to recalculate travel shed.' - } - else if (version.isochrones) { + } else if (version.isochrones) { return 'Reading transport network, please try again later.' - } - else { + } else { return 'Click on map above to show travel shed for this feed.' } } @@ -84,30 +73,30 @@ export default class FeedVersionReport extends Component { const versionHeader = (
    -

    - {/* Name Display / Editor */} - {version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0 - ? - : version.validationSummary.errorCount > 0 - ? - : - } - {this.props.isPublic - ? {version.name} - : + {/* Name Display / Editor */} + {version.validationSummary.loadStatus === 'SUCCESS' && version.validationSummary.errorCount === 0 + ? + : version.validationSummary.errorCount > 0 + ? + : + } + {this.props.isPublic + ? {version.name} + : this.props.feedVersionRenamed(version, value)} /> - } - -

    - - Version published {moment(version.updated).fromNow()} by {version.user ? {version.user} : '[unknown]'} - + } + +

    + + Version published {moment(version.updated).fromNow()} by {version.user ? {version.user} : '[unknown]'} +
    ) const tableOptions = { @@ -122,65 +111,66 @@ export default class FeedVersionReport extends Component { sizePerPageList: [10, 20, 50, 100] } } - return {numeral(version.fileSize || 0).format('0 b')} zip file last modified at {version.fileTimestamp ? moment(version.fileTimestamp).format(timeFormat + ', ' + dateFormat) : 'N/A' }} > - - - - - - - - -

    - {isExtensionEnabled('mtc') - ? - : null - } - {`Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} - {' '} - {this.getVersionDateLabel(version)} -

    -

    - {version.validationSummary && version.validationSummary.avgDailyRevenueTime - ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) - : null - } -

    -
    - + + + + + + + + +

    + {isExtensionEnabled('mtc') + ? + : null + } + {`Valid from ${moment(version.validationSummary.startDate).format(dateFormat)} to ${moment(version.validationSummary.endDate).format(dateFormat)}`} + {' '} + {this.getVersionDateLabel(version)} +

    +

    + {version.validationSummary && version.validationSummary.avgDailyRevenueTime + ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) + : null + } +

    +
    + { @@ -211,13 +201,6 @@ export default class FeedVersionReport extends Component {

    {getMessage(messages, 'stopTimesCount')}

    - { - /* this.selectTab(key)} - tableOptions={tableOptions} - />*/ - } - this.selectTab(key)} - tableOptions={tableOptions} - /> + this.selectTab(key)} + tableOptions={tableOptions} + /> -
    +
    + ) } } diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index 3dd851202..2864c15c2 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -84,7 +84,7 @@ export default class FeedVersionViewer extends Component { : this.props.versionSection === 'comments' ? - {note.userEmail}/ + {note.userEmail} {getMessage(messages, 'postComment')} - + {userLink}}> diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index 765a9524f..84960772b 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -532,7 +532,7 @@ export default class ProjectSettings extends Component { {getMessage(messages, 'deployment.osm.custom')} diff --git a/src/main/client/manager/components/ProjectsList.js b/src/main/client/manager/components/ProjectsList.js index d87b7bd43..41f33c7c5 100644 --- a/src/main/client/manager/components/ProjectsList.js +++ b/src/main/client/manager/components/ProjectsList.js @@ -26,7 +26,7 @@ export default class ProjectsList extends React.Component { const messages = getComponentMessages('ProjectsList') const projectCreationDisabled = !this.props.user.permissions.isApplicationAdmin() const visibleProjects = this.props.projects.filter((project) => { - if(project.isCreating) return true // projects actively being created are always visible + if (project.isCreating) return true // projects actively being created are always visible return project.name.toLowerCase().indexOf((this.props.visibilitySearchText || '').toLowerCase()) !== -1 }).sort(defaultSorter) @@ -80,7 +80,7 @@ export default class ProjectsList extends React.Component { value={project.name} disabled={disabled} onChange={(value) => { - if(project.isCreating) this.props.newProjectNamed(value) + if (project.isCreating) this.props.newProjectNamed(value) else this.props.projectNameChanged(project, value) }} link={`/project/${project.id}`} diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index 85200b052..0874829af 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -44,7 +44,7 @@ export default class UserHomePage extends Component { } render () { const messages = getComponentMessages('UserHomePage') - const projectCreationDisabled = !this.props.user.permissions.isApplicationAdmin() + // const projectCreationDisabled = !this.props.user.permissions.isApplicationAdmin() const feedVisibilityFilter = (feed) => { let visible = feed.name.toLowerCase().indexOf((this.props.visibilityFilter.searchText || '').toLowerCase()) !== -1 switch (this.props.visibilityFilter.filter) { @@ -94,31 +94,30 @@ export default class UserHomePage extends Component {

    Manage, edit, validate and deploy your data in a streamlined workflow.

    - - + +

    {/* Recent Activity List */} -

    - Recent Activity -

    - {this.props.user.recentActivity && this.props.user.recentActivity.length - ? this.props.user.recentActivity.sort(sortByDate).map(item => renderRecentActivity(item)) - : No Recent Activity for your subscriptions. - } - - +

    + Recent Activity +

    + {this.props.user.recentActivity && this.props.user.recentActivity.length + ? this.props.user.recentActivity.sort(sortByDate).map(item => renderRecentActivity(item)) + : No Recent Activity for your subscriptions. + } + {/* User Account Info Panel */} - -

    - - Hello, {this.props.user.profile.nickname}. -

    - + +

    + + Hello, {this.props.user.profile.nickname}. +

    +
    @@ -141,10 +140,10 @@ export default class UserHomePage extends Component { {this.props.user.permissions.isApplicationAdmin() ? - - + + : null } @@ -161,7 +160,7 @@ export default class UserHomePage extends Component { id='context-dropdown' title={activeProject ? {activeProject.name} - : {this.props.user.profile.nickname} + : {this.props.user.profile.nickname} } // onSelect={(eventKey) => { // this.props.setActiveProject(eventKey) @@ -171,10 +170,10 @@ export default class UserHomePage extends Component { ? [ - {this.props.user.profile.nickname} + {this.props.user.profile.nickname} , - + ] : null } diff --git a/src/main/client/manager/components/reporter/components/FeedLayout.js b/src/main/client/manager/components/reporter/components/FeedLayout.js index 79327d746..f3c2e6250 100644 --- a/src/main/client/manager/components/reporter/components/FeedLayout.js +++ b/src/main/client/manager/components/reporter/components/FeedLayout.js @@ -31,10 +31,10 @@ export default class FeedLayout extends Component { - Statistic + Statistic Value } diff --git a/src/main/client/manager/components/reporter/containers/Patterns.js b/src/main/client/manager/components/reporter/containers/Patterns.js index 63abf1415..04e4a011b 100644 --- a/src/main/client/manager/components/reporter/containers/Patterns.js +++ b/src/main/client/manager/components/reporter/containers/Patterns.js @@ -20,7 +20,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (!initialProps.routes.fetchStatus.fetched) { dispatch(fetchRoutes(feedId)) } - // if(!initialProps.patterns.fetchStatus.fetched) { + // if (!initialProps.patterns.fetchStatus.fetched) { // dispatch(fetchPatterns(feedId, null)) // } }, diff --git a/src/main/client/manager/components/reporter/containers/Routes.js b/src/main/client/manager/components/reporter/containers/Routes.js index 58396f3c2..bfb3323eb 100644 --- a/src/main/client/manager/components/reporter/containers/Routes.js +++ b/src/main/client/manager/components/reporter/containers/Routes.js @@ -17,7 +17,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { const feedId = ownProps.version.id.replace('.zip', '') return { onComponentMount: (initialProps) => { - if(!initialProps.routes.fetchStatus.fetched) { + if (!initialProps.routes.fetchStatus.fetched) { dispatch(fetchRoutes(feedId)) } }, diff --git a/src/main/client/manager/components/reporter/containers/Stops.js b/src/main/client/manager/components/reporter/containers/Stops.js index 754ec8111..6d3148c60 100644 --- a/src/main/client/manager/components/reporter/containers/Stops.js +++ b/src/main/client/manager/components/reporter/containers/Stops.js @@ -23,7 +23,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (!initialProps.routes.fetchStatus.fetched) { dispatch(fetchRoutes(feedId)) } - // if(!initialProps.patterns.fetchStatus.fetched) { + // if (!initialProps.patterns.fetchStatus.fetched) { // dispatch(fetchPatterns(feedId, null)) // } }, diff --git a/src/main/client/manager/components/validation/GtfsValidationExplorer.js b/src/main/client/manager/components/validation/GtfsValidationExplorer.js index 08b83c81e..fa6f9b7a9 100644 --- a/src/main/client/manager/components/validation/GtfsValidationExplorer.js +++ b/src/main/client/manager/components/validation/GtfsValidationExplorer.js @@ -28,7 +28,7 @@ export default class GtfsValidationExplorer extends Component { componentWillReceiveProps (nextProps) { // - if(this.props.version && nextProps && nextProps.version && + if (this.props.version && nextProps && nextProps.version && this.props.version.id !== nextProps.version.id && !nextProps.version.validationResult) { this.props.fetchValidationResult(nextProps.version) @@ -64,7 +64,7 @@ export default class GtfsValidationExplorer extends Component { this.setState({activeTab: key}) setTimeout(() => { const map = this.refs[key + '-map'] - if(map) map.initializeMap() + if (map) map.initializeMap() }, 100); // Adjust timeout to tab transition }} > @@ -74,7 +74,7 @@ export default class GtfsValidationExplorer extends Component { @@ -102,7 +102,7 @@ export default class GtfsValidationExplorer extends Component { - + diff --git a/src/main/client/manager/components/validation/GtfsValidationMap.js b/src/main/client/manager/components/validation/GtfsValidationMap.js index e38cceb4e..ac02ba07c 100644 --- a/src/main/client/manager/components/validation/GtfsValidationMap.js +++ b/src/main/client/manager/components/validation/GtfsValidationMap.js @@ -182,7 +182,7 @@ class ValidationMap extends React.Component { bounds={bounds} onLeafletClick={getIsochrones} onLeafletLayeradd={(e) => console.log(e)} - scrollWheelZoom={true} + scrollWheelZoom > { - if(!result) this.props.fetchValidationResult() + if (!result) this.props.fetchValidationResult() this.setState({ expanded: !this.state.expanded }) }}> {getMessage(messages, 'title')} diff --git a/src/main/client/manager/components/validation/IsochroneMap.js b/src/main/client/manager/components/validation/IsochroneMap.js index 8ea173c85..290603c80 100644 --- a/src/main/client/manager/components/validation/IsochroneMap.js +++ b/src/main/client/manager/components/validation/IsochroneMap.js @@ -22,7 +22,7 @@ export default class IsochroneMap extends ValidationMap { getMapComponents () { let comps = [] - if(this.props.version && this.props.version.isochrones) { + if (this.props.version && this.props.version.isochrones) { comps = this.props.version.isochrones.features.map((iso, index) => { if (iso.properties.time !== 60*60) return null return ( @@ -40,7 +40,7 @@ export default class IsochroneMap extends ValidationMap { }) } - if(this.state && this.state.lastClicked) { + if (this.state && this.state.lastClicked) { comps.push( this.mapClicked(e)} - scrollWheelZoom={true} + scrollWheelZoom > { dispatch(fetchProjectFeeds(activeProjectId)) .then(() => browserHistory.push(`/home/${activeProjectId}`)) } - // for (var i = 0; i < projects.length; i++) { - // dispatch(fetchProjectFeeds(projects[i].id)) - // } }) }, fetchProjectFeeds: (projectId) => { dispatch(fetchProjectFeeds(projectId)) }, diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index 831902c46..0567ceac9 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -10,7 +10,7 @@ const projects = (state = { searchText: null } }, action) => { - let projects, sources, projectIndex, sourceIndex, versionIndex, activeProject, activeIndex, feeds, activeProjectId, project, newState, deployment + let projects, projectIndex, sourceIndex, versionIndex, activeProject, activeIndex, feeds, activeProjectId, project, newState, deployment switch (action.type) { case 'SET_PROJECT_VISIBILITY_SEARCH_TEXT': return update(state, {filter: {searchText: {$set: action.text}}}) @@ -21,18 +21,17 @@ const projects = (state = { isCreating: true, name: '' }, ...state.all] - return update(state, { all: { $set: projects }}) + return update(state, { all: { $set: projects } }) case 'CREATE_FEEDSOURCE': projectIndex = state.all.findIndex(p => p.id === action.projectId) project = state.all[projectIndex] newState = null - console.log("adding fs to project", state.all[projectIndex]); - + console.log('adding fs to project', state.all[projectIndex]) // if project's feedSources array is undefined, add it - if(!project.feedSources) { - console.log('adding new fs array'); + if (!project.feedSources) { + console.log('adding new fs array') newState = update(state, {all: {[projectIndex]: {$merge: {feedSources: []}}}}) } @@ -76,7 +75,7 @@ const projects = (state = { } } case 'SET_ACTIVE_PROJECT': - return update(state, { active: { $set: action.project }}) + return update(state, { active: { $set: action.project } }) case 'RECEIVE_PROJECT': if (!action.project) { return state @@ -116,14 +115,12 @@ const projects = (state = { ...state.all[projectIndex].feedSources, ...action.feedSources ] - } - else { + } else { feeds = action.feedSources } feeds = feeds.sort(defaultSorter) if (state.active && action.projectId === state.active.id) { - return update(state, - { + return update(state, { isFetching: {$set: false}, active: {$merge: {feedSources: feeds}}, all: { @@ -143,10 +140,11 @@ const projects = (state = { case 'RECEIVE_FEEDSOURCE': if (!action.feedSource) { - return update(state, { isFetching: { $set: false }}) + return update(state, { isFetching: { $set: false } }) } projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) - let existingSources = state.all[projectIndex].feedSources || [], updatedSources + let existingSources = state.all[projectIndex].feedSources || [] + let updatedSources sourceIndex = existingSources.findIndex(s => s.id === action.feedSource.id) if (sourceIndex === -1) { // source does not currently; add it updatedSources = [ @@ -197,7 +195,8 @@ const projects = (state = { versionIndex = state.all[projectIndex].feedSources[sourceIndex].feedVersions ? state.all[projectIndex].feedSources[sourceIndex].feedVersions.findIndex(v => v.id === action.feedVersion.id) : -1 - let existingVersions = state.all[projectIndex].feedSources[sourceIndex].feedVersions || [], updatedVersions + let existingVersions = state.all[projectIndex].feedSources[sourceIndex].feedVersions || [] + let updatedVersions if (versionIndex === -1) { // version does not currently; add it updatedVersions = [ ...existingVersions, @@ -265,51 +264,48 @@ const projects = (state = { } } ) - - case 'RECEIVE_DEPLOYMENTS': - projectIndex = state.all.findIndex(p => p.id === action.projectId) - if (state.active && action.projectId === state.active.id) { - return update(state, + case 'RECEIVE_DEPLOYMENTS': + projectIndex = state.all.findIndex(p => p.id === action.projectId) + if (state.active && action.projectId === state.active.id) { + return update(state, { + active: {$merge: {deployments: action.deployments}}, + all: { + [projectIndex]: {$merge: {deployments: action.deployments}} + } + }) + } else { // if projectId does not match active project + return update(state, { - active: {$merge: {deployments: action.deployments}}, all: { [projectIndex]: {$merge: {deployments: action.deployments}} } - }) - } else { // if projectId does not match active project - return update(state, - { - all: { - [projectIndex]: {$merge: {deployments: action.deployments}} - } - } - ) - } - - case 'RECEIVE_DEPLOYMENT': - projectIndex = state.all.findIndex(p => p.id === action.deployment.project.id) - let existingDeployments = state.all[projectIndex].deployments || [], updatedDeployments - sourceIndex = existingDeployments.findIndex(s => s.id === action.deployment.id) - if (sourceIndex === -1) { // source does not currently; add it - updatedDeployments = [ - ...existingDeployments, - action.deployment - ] - } else { // existing feedsource array includes this one, replace it - updatedDeployments = [ - ...existingDeployments.slice(0, sourceIndex), - action.deployment, - ...existingDeployments.slice(sourceIndex + 1) - ] - } - return update(state, - {all: - {[projectIndex]: - {$merge: {deployments: updatedDeployments}} - } } ) - + } + case 'RECEIVE_DEPLOYMENT': + projectIndex = state.all.findIndex(p => p.id === action.deployment.project.id) + let existingDeployments = state.all[projectIndex].deployments || [] + let updatedDeployments + sourceIndex = existingDeployments.findIndex(s => s.id === action.deployment.id) + if (sourceIndex === -1) { // source does not currently; add it + updatedDeployments = [ + ...existingDeployments, + action.deployment + ] + } else { // existing feedsource array includes this one, replace it + updatedDeployments = [ + ...existingDeployments.slice(0, sourceIndex), + action.deployment, + ...existingDeployments.slice(sourceIndex + 1) + ] + } + return update(state, + {all: + {[projectIndex]: + {$merge: {deployments: updatedDeployments}} + } + } + ) case 'RECEIVE_NOTES_FOR_FEEDSOURCE': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) diff --git a/src/main/client/public/components/FeedsMap.js b/src/main/client/public/components/FeedsMap.js index 44529aaa1..68dc64682 100644 --- a/src/main/client/public/components/FeedsMap.js +++ b/src/main/client/public/components/FeedsMap.js @@ -1,5 +1,5 @@ import React from 'react' -import { Map, Marker, Popup, TileLayer, FeatureGroup } from 'react-leaflet' +import { Map, Marker, Popup, TileLayer } from 'react-leaflet' import { Button } from 'react-bootstrap' import { getFeedsBounds } from '../../common/util/geo' @@ -22,14 +22,6 @@ export default class FeedsMap extends React.Component { width: '100%' } let feeds = [] - const feedsArray = this.props.projects.map(p => { - if (p.feedSources) { - return p.feedSources.map(f => { - feeds.push(f) - return f - }) - } - }) const getFeedLocation = (bounds) => { if (!bounds) return null let lngEast = bounds.east ? bounds.east : bounds.west // check for 0 values @@ -40,8 +32,6 @@ export default class FeedsMap extends React.Component { // return averaged location return [(latNorth + latSouth) / 2, (lngWest + lngEast) / 2] } - console.log(feeds.filter(feed => feed.latestValidation)) - let position = [this.state.lat, this.state.lng]; if (feeds.length === 0) { return ( feeds.map(feed => { if (feed.latestValidation && feed.latestValidation.bounds) { markers.push( @@ -77,10 +63,7 @@ export default class FeedsMap extends React.Component { ) } }) - console.log(bounds) bounds = bounds && bounds.north ? [[bounds.north, bounds.east], [bounds.south, bounds.west]] : this.state.bounds - console.log(bounds) - console.log(markers) return ( } - let position = this.state.position - // let explore = - // - // - // Explore Transit Data - // - // - // - // - // - // let exploreHeader = - // - // Explore Transit Data - // - // let feeds = [] - const feedArray = this.props.projects.map(p => { - const regions = p.name.split(', ') - if (p.feedSources) { - return p.feedSources.map(f => { - feeds.push(f) - return f - }) - } - }) return ( @@ -64,7 +36,7 @@ export default class PublicFeedsViewer extends React.Component { entities={['regions', 'feeds']} minimumInput={0} bsSize='large' - clearable={true} + clearable onChange={(evt) => { console.log(evt) if (evt && evt.region) { @@ -75,9 +47,9 @@ export default class PublicFeedsViewer extends React.Component { } if (evt && evt.feed) { browserHistory.push('/public/feed/' + evt.feed.id) - } - else if (evt == null) + } else if (evt == null) { this.setState({position: null, bounds: null}) + } }} /> @@ -85,22 +57,16 @@ export default class PublicFeedsViewer extends React.Component { browserHistory.push('/public/feed/' + feedId) } + onFeedClick={(feedId) => browserHistory.push('/public/feed/' + feedId)} bounds={this.state.bounds} /> - - - - - {isExtensionEnabled('mtc') ? null : } - @@ -124,73 +90,26 @@ class PublicFeedsTable extends React.Component { dateSort (a, b, order) { return b.lastUpdated - a.lastUpdated } - - constructor (props) { - super(props) - } - render () { let feeds = [] const messages = getComponentMessages('PublicFeedsTable') - const feedArray = this.props.projects.map(p => { - const regions = p.name.split(', ') - if (p.feedSources) { - return p.feedSources.map(f => { - const feed = { - name: f.name, - id: f.id, - lastUpdated: moment(f.lastUpdated).format('MMMM Do YYYY, h:mm a'), - region: regions[regions.length - 3], - state: regions[regions.length - 2], - country: regions[regions.length - 1], - url: f.url - } - feeds.push(feed) - return feed - }) - } - }) - return ( + return ( - - {getMessage(messages, 'name')} - {getMessage(messages, 'region')} - {getMessage(messages, 'stateProvince')} - {getMessage(messages, 'country')} - {getMessage(messages, 'lastUpdated')} - + + {getMessage(messages, 'name')} + {getMessage(messages, 'region')} + {getMessage(messages, 'stateProvince')} + {getMessage(messages, 'country')} + {getMessage(messages, 'lastUpdated')} + {getMessage(messages, 'link')} ) } } - -class FeedRow extends React.Component { - - constructor (props) { - super(props) - } - - render () { - var buttons; - if (this.props.feed.url){ - // buttons = - } - return ( - - {this.props.feed.name} - {moment(this.props.feed.lastUpdated).format('MMMM Do YYYY, h:mm:ss a')} - - - - - - ) - } -} diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index 58a30336d..0b1c14899 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -42,7 +42,7 @@ export default class PublicHeader extends Component { {this.props.username ? : @@ -65,33 +60,33 @@ export default class SignEditor extends React.Component { bsStyle='default' disabled={editingIsDisabled} onClick={(evt) => { - console.log('times', this.props.sign.end, this.props.sign.start); - // if(moment(this.props.sign.end).isBefore(moment())) { + console.log('times', this.props.sign.end, this.props.sign.start) + // if (moment(this.props.sign.end).isBefore(moment())) { // alert('Sign end date cannot be before the current date') // return // } - if(!this.props.sign.title) { - alert('You must specify a name for the sign configuration') + if (!this.props.sign.title) { + window.alert('You must specify a name for the sign configuration') return } // check for no entities - if(this.props.sign.affectedEntities.length === 0) { - alert('You must specify at least one stop') + if (this.props.sign.affectedEntities.length === 0) { + window.alert('You must specify at least one stop') return } // check for entities without routes for (var i = 0; i < this.props.sign.affectedEntities.length; i++) { let ent = this.props.sign.affectedEntities[i] if (!ent.route || ent.route.length === 0) { - alert('You must specify at least one route for ' + ent.stop.stop_name) + window.alert('You must specify at least one route for ' + ent.stop.stop_name) return } } // check for published displays for unpublished config - for (var i = 0; i < this.props.sign.displays.length; i++) { + for (let i = 0; i < this.props.sign.displays.length; i++) { let disp = this.props.sign.displays[i] if (disp.PublishedDisplayConfigurationId === this.props.sign.id && !this.props.sign.published) { - alert('Published displays may not be associated with an unpublished sign configuration.') + window.alert('Published displays may not be associated with an unpublished sign configuration.') return } } @@ -111,21 +106,14 @@ export default class SignEditor extends React.Component { title={deleteButtonMessage} bsStyle='danger' disabled={deleteIsDisabled} - onClick={ - (evt) => { - // let r = confirm('Are you sure you want to delete this sign configuration?') - // if (r == true) { - // this.props.onDeleteClick(this.props.sign) - // } else { - // return - // } - this.refs.page.showConfirmModal({ - title: 'Delete Configuration #' + this.props.sign.id + '?', - body:

    Are you sure you want to delete Sign Configuration {this.props.sign.id}?

    , - onConfirm: () => { - this.props.onDeleteClick(this.props.sign) - } - }) + onClick={(evt) => { + this.refs.page.showConfirmModal({ + title: 'Delete Configuration #' + this.props.sign.id + '?', + body:

    Are you sure you want to delete Sign Configuration {this.props.sign.id}?

    , + onConfirm: () => { + this.props.onDeleteClick(this.props.sign) + } + }) }} >Delete @@ -162,14 +150,10 @@ export default class SignEditor extends React.Component { placeholder='Click to add displays, or enter a new display name...' sign={this.props.sign} label='List of Signs' - clearable={true} + clearable toggleConfigForDisplay={this.props.toggleConfigForDisplay} onChange={(evt) => { - console.log('we need to add this sign to the draft config', evt) - // if (typeof evt !== 'undefined' && evt !== null){ - this.props.updateDisplays(evt.map(s => s.display)) - // } - + this.props.updateDisplays(evt.map(s => s.display)) }} value={this.props.sign.displays ? this.props.sign.displays.map(d => ({'display': d, 'value': d.Id, 'label': {d.DisplayTitle} {d.LocationDescription}})) : ''} /> @@ -186,9 +170,8 @@ export default class SignEditor extends React.Component { placeholder='Click to add stops...' limit={100} entities={['stops']} - clearable={true} + clearable onChange={(evt) => { - console.log('we need to add this entity to the store', evt) if (typeof evt !== 'undefined' && evt !== null) { this.props.onAddEntityClick('STOP', evt.stop, evt.agency, newEntityId) } @@ -199,15 +182,16 @@ export default class SignEditor extends React.Component { {this.props.sign.affectedEntities .sort((a, b) => b.id - a.id) // reverse sort by entity id .map((entity) => { - return - })} + return + } + )}
    diff --git a/src/main/client/signs/components/SignPreview.js b/src/main/client/signs/components/SignPreview.js index 6d950cc92..0dd1dd3c5 100644 --- a/src/main/client/signs/components/SignPreview.js +++ b/src/main/client/signs/components/SignPreview.js @@ -1,16 +1,9 @@ import React from 'react' -import moment from 'moment' -import { Panel, Grid, Row, Col, ButtonGroup, Button, Glyphicon, Label } from 'react-bootstrap' -import { Link } from 'react-router' +import { Panel, Row, Col, ButtonGroup, Button, Glyphicon, Label } from 'react-bootstrap' import { checkEntitiesForFeeds } from '../../common/util/permissions' export default class SignPreview extends React.Component { - - constructor (props) { - super(props) - } - render () { const canPublish = checkEntitiesForFeeds(this.props.sign.affectedEntities, this.props.publishableFeeds) const canEdit = checkEntitiesForFeeds(this.props.sign.affectedEntities, this.props.editableFeeds) @@ -24,12 +17,12 @@ export default class SignPreview extends React.Component { const editButtonMessage = this.props.sign.published && deleteIsDisabled ? 'Cannot edit because sign is published' : !canEdit ? 'Cannot alter signs for other agencies' : 'Edit sign' - const publishedLabel = this.props.sign.published - ? - : - const displaysLabel = this.props.sign.displays - ? - : + const publishedLabel = this.props.sign.published + ? + : + const displaysLabel = this.props.sign.displays + ? + : return ( @@ -47,11 +40,11 @@ export default class SignPreview extends React.Component { disabled={deleteIsDisabled} onClick={ (evt) => { - let r = confirm('Are you sure you want to delete this sign configuration?') - if (r == true) { - this.props.onDeleteClick(this.props.sign) + let r = window.confirm('Are you sure you want to delete this sign configuration?') + if (r) { + this.props.onDeleteClick(this.props.sign) } else { - return + return } // this.props.showConfirmModal({ // title: 'Delete Configuration #' + this.props.sign.id + '?', @@ -60,7 +53,8 @@ export default class SignPreview extends React.Component { // this.props.onDeleteClick(this.props.sign) // } // }) - }} + } + } > @@ -72,17 +66,17 @@ export default class SignPreview extends React.Component { {publishedLabel} {this.props.sign.published ? 'Published' : 'Draft'}

    Stops:  - {this.props.sign.affectedEntities - ? this.props.sign.affectedEntities.map((e, index) => { - return e.stop - ? ({e.stop.stop_name} ) - : e.stop_id - }) - : '' - } + {this.props.sign.affectedEntities + ? this.props.sign.affectedEntities.map((e, index) => { + return e.stop + ? ({e.stop.stop_name} ) + : e.stop_id + }) + : '' + }

    - {displaysLabel} associated display(s) + {displaysLabel} associated display(s)

    ) diff --git a/src/main/client/signs/containers/ActiveSignEditor.js b/src/main/client/signs/containers/ActiveSignEditor.js index 53bc42641..6bb11c7ee 100644 --- a/src/main/client/signs/containers/ActiveSignEditor.js +++ b/src/main/client/signs/containers/ActiveSignEditor.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux' import { fetchProjects } from '../actions/projects' import { saveSign, deleteSign, createSign, setActiveSign, createDisplay } from '../actions/signs' -import { setActiveTitle, setActiveDescription, setActiveUrl, setActiveCause, +import { setActiveTitle, setActiveUrl, setActiveCause, setActiveEffect, setActiveStart, setActiveEnd, setActivePublished, addActiveEntity, deleteActiveEntity, updateActiveEntity, updateDisplays, toggleConfigForDisplay } from '../actions/activeSign' @@ -12,13 +12,6 @@ import SignEditor from '../components/SignEditor' import { browserHistory } from 'react-router' import { getFeedsForPermission } from '../../common/util/permissions' -const agencyCompare = function(a, b) { - if (a.name < b.name) - return -1; - if (a.name > b.name) - return 1; - return 0; -} const mapStateToProps = (state, ownProps) => { return { sign: state.activeSign, @@ -34,24 +27,24 @@ const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { const signId = initialProps.location.pathname.split('/sign/')[1] - if (initialProps.sign) + if (initialProps.sign) { return + } if (!signId) { dispatch(fetchProjects()) .then((activeProject) => { - if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')){ + if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')) { console.log('cannot create sign!') browserHistory.push('/signs') return } return dispatch(createSign()) }) - } - else { + } else { dispatch(fetchProjects()) .then((activeProject) => { - if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')){ + if (!initialProps.user.permissions.hasProjectPermission(activeProject.id, 'edit-etid')) { console.log('cannot create sign!') browserHistory.push('/signs') return diff --git a/src/main/client/signs/containers/MainSignsViewer.js b/src/main/client/signs/containers/MainSignsViewer.js index 9dadcc5db..c306c54e8 100644 --- a/src/main/client/signs/containers/MainSignsViewer.js +++ b/src/main/client/signs/containers/MainSignsViewer.js @@ -2,7 +2,6 @@ import React from 'react' import { connect } from 'react-redux' import SignsViewer from '../components/SignsViewer' - import { createSign } from '../actions/signs' import { fetchProjects } from '../actions/projects' @@ -22,11 +21,10 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (!initialProps.signs || initialProps.signs.length === 0 || !initialProps.project.feedSources) { dispatch(fetchProjects()) } - }, onStopClick: (stop, agency) => dispatch(createSign(stop, agency)), onRouteClick: (route, agency) => dispatch(createSign(route, agency)), - createSign: () => dispatch(createSign()), + createSign: () => dispatch(createSign()) } } diff --git a/src/main/client/signs/reducers/signs.js b/src/main/client/signs/reducers/signs.js index b12a7a82d..6e953a956 100644 --- a/src/main/client/signs/reducers/signs.js +++ b/src/main/client/signs/reducers/signs.js @@ -12,22 +12,12 @@ const signs = (state = { filter: 'ALL' } }, action) => { - let foundIndex, signs, entities + let signs, entities switch (action.type) { case 'SET_SIGN_VISIBILITY_SEARCH_TEXT': return update(state, {filter: {searchText: {$set: action.text}}}) case 'SET_SIGN_VISIBILITY_FILTER': return update(state, {filter: {filter: {$set: action.filter}}}) - - case 'DELETE_SIGN': - // foundIndex = state.findIndex(a => a.id === action.sign.id) - // if(foundIndex !== -1) { - // return [ - // ...state.slice(0, foundIndex), - // ...state.slice(foundIndex + 1) - // ] - // } - case 'REQUEST_RTD_SIGNS': return { isFetching: true, @@ -44,7 +34,7 @@ const signs = (state = { entities = state.entities signs = clone(state.all) // for those entities we requested, assign the gtfs data to the saved entities - for (var i = 0; i < entities.length; i++) { + for (let i = 0; i < entities.length; i++) { let feed = action.results.feeds.find(f => f.feed_id === entities[i].entity.AgencyId) if (feed) { let gtfs = entities[i].type === 'stop' @@ -59,16 +49,16 @@ const signs = (state = { } } // iterate over processed gtfs entities - for (var i = 0; i < entities.length; i++) { + for (let i = 0; i < entities.length; i++) { let ent = entities[i] - if (ent.gtfs && signs){ + if (ent.gtfs && signs) { let sign = signs.find(s => s.id === ent.entity.DisplayConfigurationId) let selectedEnt = sign && sign.affectedEntities.find(e => e.agencyAndStop === ent.entity.agencyAndStop) - if (selectedEnt && ent.type === 'stop'){ + if (selectedEnt && ent.type === 'stop') { selectedEnt.stop = ent.gtfs } // route is an array for signs - if (ent.type === 'route'){ + if (ent.type === 'route') { let route = ent.gtfs ? ent.gtfs : ent.entity selectedEnt.route.push(route) } @@ -85,15 +75,13 @@ const signs = (state = { } } case 'RECEIVED_RTD_DISPLAYS': - // let signIndex = state.all.find if (state.all !== null) { let displayMap = {} - let count = 0 - for (var i = 0; i < action.rtdDisplays.length; i++) { + for (let i = 0; i < action.rtdDisplays.length; i++) { let d = action.rtdDisplays[i] - if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) + if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) { continue - count++ + } if (d.DraftDisplayConfigurationId) { if (displayMap[d.DraftDisplayConfigurationId] && displayMap[d.DraftDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { displayMap[d.DraftDisplayConfigurationId].push(d) @@ -146,9 +134,8 @@ const signs = (state = { // if entity already exists, push RouteId to existing array if (currentEntity) { entities[ent.AgencyId + ent.StopId].route_id.push(ent.RouteId) - } - // else, construct new object for entity - else { + } else { + // else, construct new object for entity let feed = project.feedSources.find(f => getFeedId(f) === ent.AgencyId) entities[ent.AgencyId + ent.StopId] = { id: ent.Id, @@ -177,7 +164,6 @@ const signs = (state = { } return sign }) - return { isFetching: false, all: allSigns, @@ -187,7 +173,6 @@ const signs = (state = { filter: 'ALL' } } - default: return state } From eacc4d009e8bbbe75cc1df376b65693c83d437d5 Mon Sep 17 00:00:00 2001 From: Trevor Gerhardt Date: Wed, 30 Nov 2016 14:46:11 +0800 Subject: [PATCH 151/323] build(deploy): Update mastarm and react dependencies. Switch to temporary deploy script. --- package.json | 14 +++++++------- yarn.lock | 34 +++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 8df6805f9..c37547df8 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "license": "MIT", "scripts": { "build": "mastarm build --env production", + "deploy": "mastarm build --minify --env production && aws s3 cp --recursive --acl public-read dist", "prestart": "yarn", "start": "mastarm build --serve --proxy http://localhost:4000/api", "test": "mastarm lint \"src/main/client/**/*.js\"" @@ -23,8 +24,6 @@ "change-case": "^3.0.0", "clone": "^1.0.2", "cors": "^2.3.1", - "express": "^4.13.3", - "express-jwt": "^3.3.0", "font-awesome": "^4.7.0", "gravatar": "^1.5.2", "history": "^2.0.1", @@ -43,16 +42,17 @@ "qs": "^6.2.1", "rbush": "^2.0.1", "rc-slider": "^5.1.1", - "react": "^15.2.1", - "react-addons-shallow-compare": "^15.1.0", - "react-addons-update": "^0.14.7", + "react": "^15.4.1", + "react-addons-perf": "^15.4.1", + "react-addons-shallow-compare": "^15.4.1", + "react-addons-update": "^15.4.1", "react-bootstrap": "^0.30.0-rc.2", "react-bootstrap-datetimepicker": "^0.0.22", "react-bootstrap-table": "^2.5.2", "react-color": "^2.3.4", "react-dnd": "^2.1.4", "react-dnd-html5-backend": "^2.1.2", - "react-dom": "^15.2.1", + "react-dom": "^15.4.1", "react-dropzone": "^3.5.3", "react-helmet": "^3.1.0", "react-leaflet": "^0.12.1", @@ -84,7 +84,7 @@ "validator": "^5.5.0" }, "devDependencies": { - "mastarm": "^2.0.0" + "mastarm": "^3.1.0" }, "standard": { "parser": "babel-eslint" diff --git a/yarn.lock b/yarn.lock index 2cc6d943b..0f281dc31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2744,7 +2744,7 @@ fbjs@^0.3.1: ua-parser-js "^0.7.9" whatwg-fetch "^0.9.0" -fbjs@^0.8.4: +fbjs@^0.8.1, fbjs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.5.tgz#f69ba8a876096cb1b9bffe4d7c1e71c19d39d008" dependencies: @@ -5687,13 +5687,17 @@ react-addons-perf@^15.3.2: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.3.2.tgz#bbdbebe8649f936f9636a5750ac145bf5c620213" -"react-addons-shallow-compare@^0.14.0 || ^15.0.0", react-addons-shallow-compare@^15.1.0: - version "15.3.2" - resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.3.2.tgz#c9edba49b9eab44d0c59024d289beb1ab97318b5" +react-addons-perf@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.4.1.tgz#c6dd5a7011f43cd3222f47b7cb1aebe9d4174cb0" + +"react-addons-shallow-compare@^0.14.0 || ^15.0.0", react-addons-shallow-compare@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.4.1.tgz#b68103dd4d13144cb221065f6021de1822bd435a" -"react-addons-update@^0.14.0 || ^15.0.0", react-addons-update@^0.14.7: - version "0.14.8" - resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-0.14.8.tgz#486f1fb71e09ef8ad09c0e95fbe59d173782fe1a" +"react-addons-update@^0.14.0 || ^15.0.0", react-addons-update@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.4.1.tgz#00c07f45243aa9715e1706bbfd1f23d3d8d80bd1" react-bootstrap-datetimepicker@^0.0.22: version "0.0.22" @@ -5751,9 +5755,13 @@ react-dnd@^2.1.4: invariant "^2.1.0" lodash "^4.2.0" -"react-dom@^0.14.0 || ^15.0.0", "react-dom@^15.0.0 || ^16.0.0", react-dom@^15.2.1, react-dom@^15.3.2: - version "15.3.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f" +"react-dom@^0.14.0 || ^15.0.0", "react-dom@^15.0.0 || ^16.0.0", react-dom@^15.3.2, react-dom@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.4.1.tgz#d54c913261aaedb17adc20410d029dcc18a1344a" + dependencies: + fbjs "^0.8.1" + loose-envify "^1.1.0" + object-assign "^4.1.0" react-dropzone@^3.5.3: version "3.7.2" @@ -5871,9 +5879,9 @@ react-virtualized@^8.0.5, react-virtualized@^8.5.0: classnames "^2.2.3" dom-helpers "^2.4.0" -"react@^15.0.0 || ^16.0.0", react@^15.2.1, react@^15.3.2: - version "15.3.2" - resolved "https://registry.yarnpkg.com/react/-/react-15.3.2.tgz#a7bccd2fee8af126b0317e222c28d1d54528d09e" +"react@^15.0.0 || ^16.0.0", react@^15.3.2, react@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/react/-/react-15.4.1.tgz#498e918602677a3983cd0fd206dfe700389a0dd6" dependencies: fbjs "^0.8.4" loose-envify "^1.1.0" From b2e77ec98760ed843946c11a4cbb18aceab5c635 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 6 Dec 2016 12:33:27 -0500 Subject: [PATCH 152/323] major refactoring to pass linting and reduce file size --- src/main/client/admin/actions/admin.js | 6 +- src/main/client/admin/components/UserAdmin.js | 113 +- .../client/admin/components/UserSettings.js | 70 +- .../client/admin/components/permissions.js | 4 +- .../admin/containers/ActiveUserAdmin.js | 7 +- src/main/client/admin/reducers/admin.js | 12 +- src/main/client/alerts/actions/activeAlert.js | 2 +- src/main/client/alerts/actions/alerts.js | 74 +- .../alerts/components/AffectedEntity.js | 221 +--- .../alerts/components/AffectedServices.js | 108 ++ .../alerts/components/AgencySelector.js | 30 + .../client/alerts/components/AlertEditor.js | 486 +++----- .../client/alerts/components/AlertPreview.js | 29 +- .../client/alerts/components/AlertsList.js | 6 +- .../client/alerts/components/AlertsViewer.js | 11 +- .../client/alerts/components/CreateAlert.js | 4 - .../client/alerts/components/ModeSelector.js | 31 + .../alerts/components/NoAccessScreen.js | 33 - .../client/alerts/components/RouteSelector.js | 53 + .../client/alerts/components/StopSelector.js | 49 + .../alerts/containers/ActiveAlertEditor.js | 19 +- .../alerts/containers/MainAlertsViewer.js | 5 +- .../alerts/containers/VisibleAlertsList.js | 9 +- .../client/alerts/reducers/activeAlert.js | 26 +- src/main/client/alerts/reducers/alerts.js | 27 +- src/main/client/alerts/util/index.js | 27 + .../client/common/components/Breadcrumbs.js | 17 +- .../common/components/DatatoolsNavbar.js | 140 +-- .../common/components/EditableTextField.js | 6 +- .../client/common/components/JobMonitor.js | 2 +- .../common/components/LanguageSelect.js | 67 +- src/main/client/common/components/Loading.js | 1 - src/main/client/common/components/MapModal.js | 84 +- .../common/components/NoAccessScreen.js | 32 - .../client/common/components/PageNotFound.js | 14 +- .../common/components/SidebarNavItem.js | 38 +- .../client/common/components/StatusMessage.js | 13 +- .../client/common/components/StatusModal.js | 2 +- .../common/components/TimezoneSelect.js | 46 +- .../client/common/containers/ActiveSidebar.js | 1 - .../common/containers/ActiveSidebarNavItem.js | 1 - src/main/client/common/containers/App.js | 21 +- .../common/containers/CurrentStatusMessage.js | 3 +- .../common/containers/CurrentStatusModal.js | 7 +- .../containers/FeedSourceActionButton.js | 127 ++- .../client/common/containers/ManagerNavbar.js | 1 - .../client/common/containers/StarButton.js | 2 +- .../client/common/containers/WatchButton.js | 18 +- src/main/client/common/user/Auth0Manager.js | 18 +- src/main/client/common/util/currencies.js | 386 ------- src/main/client/common/util/geo.js | 13 +- src/main/client/common/util/languages.js | 738 ------------ src/main/client/common/util/util.js | 37 +- src/main/client/editor/actions/active.js | 195 ++++ src/main/client/editor/actions/agency.js | 6 +- src/main/client/editor/actions/calendar.js | 6 +- src/main/client/editor/actions/editor.js | 577 +--------- src/main/client/editor/actions/fare.js | 7 +- src/main/client/editor/actions/feedInfo.js | 13 +- src/main/client/editor/actions/map/index.js | 256 +++++ .../editor/actions/map/stopStrategies.js | 131 +++ src/main/client/editor/actions/route.js | 13 +- src/main/client/editor/actions/snapshots.js | 3 +- src/main/client/editor/actions/stop.js | 2 +- src/main/client/editor/actions/trip.js | 66 +- src/main/client/editor/actions/tripPattern.js | 38 +- .../editor/components/CreateSnapshotModal.js | 6 +- .../client/editor/components/EditorMap.js | 1007 ----------------- .../client/editor/components/EditorSidebar.js | 24 +- .../client/editor/components/EntityDetails.js | 3 +- .../client/editor/components/EntityList.js | 161 +-- .../editor/components/EntityListButtons.js | 106 ++ .../client/editor/components/FeedInfoPanel.js | 56 +- .../client/editor/components/GtfsEditor.js | 194 ++-- .../client/editor/components/GtfsInput.js | 648 ----------- .../client/editor/components/GtfsTable.js | 422 ------- .../editor/components/GtfsTableEditor.js | 158 --- .../editor/components/GtfsVersionSummary.js | 10 +- .../client/editor/components/RouteEditor.js | 85 -- .../editor/components/StopCircleMarker.js | 65 -- .../editor/components/StopMarkersLayer.js | 96 -- .../editor/components/TimetableHeader.js | 318 ------ .../components/map/AddableStopsLayer.js | 92 ++ .../components/map/ControlPointsLayer.js | 120 ++ .../components/map/DirectionIconsLayer.js | 70 ++ .../client/editor/components/map/EditorMap.js | 270 +++++ .../components/map/EditorMapLayersControl.js | 56 + .../components/{ => map}/PatternStopPopup.js | 16 +- .../components/map/PatternStopsLayer.js | 60 + .../editor/components/map/PatternsLayer.js | 60 + .../editor/components/{ => map}/RouteLayer.js | 20 +- .../editor/components/map/StopMarkersLayer.js | 93 ++ .../editor/components/map/StopsLayer.js | 95 ++ .../CalculateDefaultTimesForm.js | 0 .../{ => pattern}/EditSchedulePanel.js | 0 .../{ => pattern}/EditShapePanel.js | 181 +-- .../{ => pattern}/PatternStopCard.js | 4 +- .../{ => pattern}/PatternStopContainer.js | 0 .../{ => pattern}/PatternStopsPanel.js | 2 +- .../{ => pattern}/TripPatternList.js | 20 +- .../{ => pattern}/TripPatternListControls.js | 0 .../{ => pattern}/TripPatternViewer.js | 0 .../components/timetable/CalendarSelect.js | 64 ++ .../components/timetable}/EditableCell.js | 25 +- .../editor/components/timetable/HeaderCell.js | 46 + .../components/timetable/PatternSelect.js | 43 + .../components/timetable/RouteSelect.js | 38 + .../components/{ => timetable}/Timetable.js | 98 +- .../{ => timetable}/TimetableEditor.js | 59 +- .../components/timetable/TimetableHeader.js | 184 +++ .../editor/containers/ActiveEntityList.js | 23 +- .../editor/containers/ActiveFeedInfoPanel.js | 2 +- .../editor/containers/ActiveGtfsEditor.js | 119 +- .../containers/ActiveGtfsTableEditor.js | 92 -- .../containers/ActiveGtfsVersionSummary.js | 2 +- .../containers/ActiveTimetableEditor.js | 9 +- .../containers/ActiveTripPatternList.js | 20 +- src/main/client/editor/props/index.js | 35 + src/main/client/editor/reducers/data.js | 461 ++++++++ src/main/client/editor/reducers/editor.js | 851 -------------- src/main/client/editor/reducers/index.js | 16 +- src/main/client/editor/reducers/mapState.js | 49 + src/main/client/editor/reducers/settings.js | 148 +++ src/main/client/editor/reducers/timetable.js | 88 ++ src/main/client/editor/util/gtfs.js | 52 - src/main/client/editor/util/index.js | 28 +- src/main/client/editor/util/map.js | 61 + src/main/client/editor/util/ui.js | 51 + src/main/client/gtfs/actions/feed.js | 8 +- src/main/client/gtfs/actions/filter.js | 5 +- src/main/client/gtfs/actions/general.js | 54 +- src/main/client/gtfs/actions/patterns.js | 2 +- src/main/client/gtfs/actions/routes.js | 2 +- src/main/client/gtfs/actions/stops.js | 11 +- src/main/client/gtfs/components/GtfsFilter.js | 125 +- src/main/client/gtfs/components/GtfsMap.js | 284 ++--- .../client/gtfs/components/PatternGeoJson.js | 71 ++ src/main/client/gtfs/components/StopMarker.js | 53 + .../gtfs/components/TransferPerformance.js | 47 + .../client/gtfs/components/gtfsmapsearch.js | 103 +- src/main/client/gtfs/components/gtfssearch.js | 44 +- .../client/gtfs/containers/ActiveGtfsMap.js | 12 +- .../gtfs/containers/GlobalGtfsFilter.js | 9 +- src/main/client/gtfs/reducers/feed.js | 14 +- src/main/client/gtfs/reducers/filter.js | 2 +- src/main/client/gtfs/reducers/routes.js | 5 - src/main/client/gtfs/reducers/stops.js | 1 - src/main/client/gtfs/util/graphql.js | 4 +- src/main/client/gtfsplus/actions/gtfsplus.js | 39 +- .../gtfsplus/components/GtfsPlusEditor.js | 14 +- .../gtfsplus/components/GtfsPlusTable.js | 117 +- .../components/GtfsPlusVersionSummary.js | 32 +- .../containers/ActiveGtfsPlusEditor.js | 12 +- .../ActiveGtfsPlusVersionSummary.js | 2 +- src/main/client/gtfsplus/reducers/gtfsplus.js | 26 +- src/main/client/main.js | 8 +- .../client/manager/actions/deployments.js | 20 +- src/main/client/manager/actions/feeds.js | 379 +------ src/main/client/manager/actions/notes.js | 69 ++ src/main/client/manager/actions/projects.js | 26 +- src/main/client/manager/actions/status.js | 6 +- src/main/client/manager/actions/user.js | 36 +- src/main/client/manager/actions/versions.js | 291 +++++ .../manager/components/DeploymentSettings.js | 315 ++++++ .../manager/components/DeploymentViewer.js | 15 +- .../manager/components/DeploymentsPanel.js | 164 +++ .../components/ExternalPropertiesTable.js | 8 - .../manager/components/FeedSourceDropdown.js | 138 +++ .../manager/components/FeedSourceTable.js | 204 +--- .../manager/components/FeedSourceViewer.js | 370 +----- .../components/FeedVersionNavigator.js | 166 ++- .../manager/components/FeedVersionReport.js | 159 +-- .../manager/components/FeedVersionViewer.js | 141 +-- .../manager/components/GeneralSettings.js | 257 +++++ .../manager/components/ManagerHeader.js | 86 ++ .../client/manager/components/NotesViewer.js | 41 +- .../manager/components/ProjectSettings.js | 611 +--------- .../manager/components/ProjectViewer.js | 223 +--- .../client/manager/components/ProjectsList.js | 151 ++- .../components/ThirdPartySyncButton.js | 54 + .../client/manager/components/UserHomePage.js | 19 +- .../reporter/components/DateTimeFilter.js | 12 +- .../reporter/components/FeedLayout.js | 9 +- .../reporter/components/PageLayout.js | 1 - .../reporter/components/PatternLayout.js | 3 - .../reporter/components/RouteLayout.js | 6 +- .../reporter/components/StopLayout.js | 4 +- .../containers/ActiveDateTimeFilter.js | 2 - .../components/reporter/containers/Feed.js | 1 - .../reporter/containers/Patterns.js | 3 +- .../components/reporter/containers/Routes.js | 3 - .../components/reporter/containers/Stops.js | 7 +- .../validation/GtfsValidationExplorer.js | 130 +-- .../validation/GtfsValidationMap.js | 274 ----- .../validation/GtfsValidationSummary.js | 7 +- .../validation/GtfsValidationViewer.js | 16 +- .../components/validation/IsochroneMap.js | 62 - .../components/validation/IssuesMap.js | 56 - .../components/validation/TripsChart.js | 12 +- .../components/validation/ValidationMap.js | 62 - .../containers/ActiveDeploymentViewer.js | 21 +- .../containers/ActiveFeedSourceViewer.js | 32 +- .../containers/ActiveFeedVersionNavigator.js | 11 +- .../manager/containers/ActiveProjectViewer.js | 4 +- .../manager/containers/ActiveProjectsList.js | 7 +- .../ActiveGtfsValidationExplorer.js | 18 +- .../validation/ActiveGtfsValidationMap.js | 113 -- src/main/client/manager/reducers/languages.js | 8 +- src/main/client/manager/reducers/ui.js | 2 +- .../client/public/components/MarkerCluster.js | 2 - .../public/components/MarkerCluster2.js | 42 - .../components/PublicFeedSourceViewer.js | 31 +- .../client/public/components/PublicHeader.js | 27 +- .../client/public/components/PublicPage.js | 7 - .../client/public/components/UserAccount.js | 193 ++-- .../ActivePublicFeedSourceViewer.js | 7 +- .../containers/ActivePublicFeedsViewer.js | 4 +- .../public/containers/ActiveSignupPage.js | 4 +- .../public/containers/ActiveUserAccount.js | 11 +- .../client/scenario-editor/utils/reverse.js | 5 +- src/main/client/signs/actions/activeSign.js | 2 +- src/main/client/signs/actions/projects.js | 2 +- .../client/signs/components/CreateSign.js | 3 - .../client/signs/components/NoAccessScreen.js | 33 - src/main/client/signs/components/SignsList.js | 36 +- .../client/signs/components/SignsViewer.js | 18 +- .../signs/containers/ActiveSignEditor.js | 1 - .../signs/containers/MainSignsViewer.js | 1 - .../signs/containers/VisibleSignsList.js | 6 - src/main/client/signs/reducers/activeSign.js | 20 +- 230 files changed, 7058 insertions(+), 11130 deletions(-) create mode 100644 src/main/client/alerts/components/AffectedServices.js create mode 100644 src/main/client/alerts/components/AgencySelector.js create mode 100644 src/main/client/alerts/components/ModeSelector.js delete mode 100644 src/main/client/alerts/components/NoAccessScreen.js create mode 100644 src/main/client/alerts/components/RouteSelector.js create mode 100644 src/main/client/alerts/components/StopSelector.js delete mode 100644 src/main/client/common/components/NoAccessScreen.js delete mode 100644 src/main/client/common/util/currencies.js delete mode 100644 src/main/client/common/util/languages.js create mode 100644 src/main/client/editor/actions/active.js create mode 100644 src/main/client/editor/actions/map/index.js create mode 100644 src/main/client/editor/actions/map/stopStrategies.js delete mode 100644 src/main/client/editor/components/EditorMap.js create mode 100644 src/main/client/editor/components/EntityListButtons.js delete mode 100644 src/main/client/editor/components/GtfsInput.js delete mode 100644 src/main/client/editor/components/GtfsTable.js delete mode 100644 src/main/client/editor/components/GtfsTableEditor.js delete mode 100644 src/main/client/editor/components/RouteEditor.js delete mode 100644 src/main/client/editor/components/StopCircleMarker.js delete mode 100644 src/main/client/editor/components/StopMarkersLayer.js delete mode 100644 src/main/client/editor/components/TimetableHeader.js create mode 100644 src/main/client/editor/components/map/AddableStopsLayer.js create mode 100644 src/main/client/editor/components/map/ControlPointsLayer.js create mode 100644 src/main/client/editor/components/map/DirectionIconsLayer.js create mode 100644 src/main/client/editor/components/map/EditorMap.js create mode 100644 src/main/client/editor/components/map/EditorMapLayersControl.js rename src/main/client/editor/components/{ => map}/PatternStopPopup.js (93%) create mode 100644 src/main/client/editor/components/map/PatternStopsLayer.js create mode 100644 src/main/client/editor/components/map/PatternsLayer.js rename src/main/client/editor/components/{ => map}/RouteLayer.js (78%) create mode 100644 src/main/client/editor/components/map/StopMarkersLayer.js create mode 100644 src/main/client/editor/components/map/StopsLayer.js rename src/main/client/editor/components/{ => pattern}/CalculateDefaultTimesForm.js (100%) rename src/main/client/editor/components/{ => pattern}/EditSchedulePanel.js (100%) rename src/main/client/editor/components/{ => pattern}/EditShapePanel.js (61%) rename src/main/client/editor/components/{ => pattern}/PatternStopCard.js (98%) rename src/main/client/editor/components/{ => pattern}/PatternStopContainer.js (100%) rename src/main/client/editor/components/{ => pattern}/PatternStopsPanel.js (98%) rename src/main/client/editor/components/{ => pattern}/TripPatternList.js (93%) rename src/main/client/editor/components/{ => pattern}/TripPatternListControls.js (100%) rename src/main/client/editor/components/{ => pattern}/TripPatternViewer.js (100%) create mode 100644 src/main/client/editor/components/timetable/CalendarSelect.js rename src/main/client/{common/components => editor/components/timetable}/EditableCell.js (92%) create mode 100644 src/main/client/editor/components/timetable/HeaderCell.js create mode 100644 src/main/client/editor/components/timetable/PatternSelect.js create mode 100644 src/main/client/editor/components/timetable/RouteSelect.js rename src/main/client/editor/components/{ => timetable}/Timetable.js (87%) rename src/main/client/editor/components/{ => timetable}/TimetableEditor.js (85%) create mode 100644 src/main/client/editor/components/timetable/TimetableHeader.js delete mode 100644 src/main/client/editor/containers/ActiveGtfsTableEditor.js create mode 100644 src/main/client/editor/props/index.js create mode 100644 src/main/client/editor/reducers/data.js delete mode 100644 src/main/client/editor/reducers/editor.js create mode 100644 src/main/client/editor/reducers/mapState.js create mode 100644 src/main/client/editor/reducers/settings.js create mode 100644 src/main/client/editor/reducers/timetable.js create mode 100644 src/main/client/editor/util/map.js create mode 100644 src/main/client/editor/util/ui.js create mode 100644 src/main/client/gtfs/components/PatternGeoJson.js create mode 100644 src/main/client/gtfs/components/StopMarker.js create mode 100644 src/main/client/gtfs/components/TransferPerformance.js create mode 100644 src/main/client/manager/actions/notes.js create mode 100644 src/main/client/manager/actions/versions.js create mode 100644 src/main/client/manager/components/DeploymentSettings.js create mode 100644 src/main/client/manager/components/DeploymentsPanel.js create mode 100644 src/main/client/manager/components/FeedSourceDropdown.js create mode 100644 src/main/client/manager/components/GeneralSettings.js create mode 100644 src/main/client/manager/components/ManagerHeader.js create mode 100644 src/main/client/manager/components/ThirdPartySyncButton.js delete mode 100644 src/main/client/manager/components/validation/GtfsValidationMap.js delete mode 100644 src/main/client/manager/components/validation/IsochroneMap.js delete mode 100644 src/main/client/manager/components/validation/IssuesMap.js delete mode 100644 src/main/client/manager/components/validation/ValidationMap.js delete mode 100644 src/main/client/manager/containers/validation/ActiveGtfsValidationMap.js delete mode 100644 src/main/client/public/components/MarkerCluster2.js delete mode 100644 src/main/client/signs/components/NoAccessScreen.js diff --git a/src/main/client/admin/actions/admin.js b/src/main/client/admin/actions/admin.js index 8705363e0..f9b75d141 100644 --- a/src/main/client/admin/actions/admin.js +++ b/src/main/client/admin/actions/admin.js @@ -42,9 +42,9 @@ export function fetchUserCount () { return secureFetch(url, getState()) .then(response => response.json()) .then(data => { - console.log(data); - //const users = JSON.parse(data) - //return dispatch(receiveUsers(users)) + console.log(data) + // const users = JSON.parse(data) + // return dispatch(receiveUsers(users)) }) } } diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index d538bb00a..6469ff5cf 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -26,9 +26,6 @@ export default class UserAdmin extends Component { projects: PropTypes.array } - constructor (props) { - super(props) - } componentWillMount () { this.props.onComponentMount(this.props) } @@ -40,32 +37,34 @@ export default class UserAdmin extends Component { const messages = getComponentMessages('UserAdmin') return ( - - - - -

    - - - - {getMessage(messages, 'title')} -

    - -
    - - {this.isAdmin() - ?
    + + + + +

    + + + + {getMessage(messages, 'title')} +

    + +
    + + {this.isAdmin() + ?
    User management Application logs - {/*Organizations - Regions*/} + {/* + Organizations + Regions + */} @@ -75,47 +74,47 @@ export default class UserAdmin extends Component { this.props.projects && this.props.activeComponent === 'users' ? - : this.props.activeComponent === 'logs' - ?

    - -

    - : null - } + setUserPermission={this.props.setUserPermission} + saveUser={this.props.saveUser} + deleteUser={this.props.deleteUser} + fetchProjectFeeds={this.props.fetchProjectFeeds} + createUser={this.props.createUser} + setPage={this.props.setPage} + userSearch={this.props.userSearch} + /> + : this.props.activeComponent === 'logs' + ?

    + +

    + : null + }
    - :
    + :
    {this.props.user ?

    {getMessage(messages, 'noAccess')}

    :

    - -

    + className='text-center' + style={{marginTop: '150px'}}> + + }
    - } - + } + ) diff --git a/src/main/client/admin/components/UserSettings.js b/src/main/client/admin/components/UserSettings.js index 6abe26814..3a44b82ed 100644 --- a/src/main/client/admin/components/UserSettings.js +++ b/src/main/client/admin/components/UserSettings.js @@ -139,41 +139,42 @@ export default class UserSettings extends Component { } let projectPanel = ( - - Project Settings for  - this.projectSelected(key)} + + Project Settings for  + this.projectSelected(key)} - > - {this.props.projects.map((project, i) => { - let settings = this.state.projectSettings[project.id] - if (typeof settings !== 'undefined') { - return {project.name} {getProjectLabel(settings.access)} - } - })} - - - } + > + {this.props.projects.map((project, i) => { + let settings = this.state.projectSettings[project.id] + if (typeof settings !== 'undefined') { + return {project.name} {getProjectLabel(settings.access)} + } + })} + + + } > -
    - {this.props.projects.map((project, i) => { - if (i !== this.state.currentProjectIndex) return null - let settings = this.state.projectSettings[project.id] - return - })} -
    ) + {this.props.projects.map((project, i) => { + if (i !== this.state.currentProjectIndex) return null + let settings = this.state.projectSettings[project.id] + return + })} + + ) return ( @@ -237,7 +238,6 @@ class ProjectSettings extends Component { }) this.props.projectFeedsUpdated(this.props.project.id, selectedFeeds) } - permissionsUpdated () { let selectedPermissions = [] allPermissions.forEach((permission) => { @@ -246,7 +246,6 @@ class ProjectSettings extends Component { }) this.props.projectPermissionsUpdated(this.props.project.id, selectedPermissions) } - render () { const messages = getComponentMessages('UserSettings') @@ -258,7 +257,6 @@ class ProjectSettings extends Component { return 0 }) } - return ( diff --git a/src/main/client/admin/components/permissions.js b/src/main/client/admin/components/permissions.js index 6066bcfb1..64ba472c0 100644 --- a/src/main/client/admin/components/permissions.js +++ b/src/main/client/admin/components/permissions.js @@ -1,9 +1,9 @@ export default [ - /*{ + /* { type: 'administer-project', name: 'Administer Project', feedSpecific: false - },*/ + }, */ { type: 'manage-feed', name: 'Manage Feed Configuration', diff --git a/src/main/client/admin/containers/ActiveUserAdmin.js b/src/main/client/admin/containers/ActiveUserAdmin.js index 49cbe029a..205862edb 100644 --- a/src/main/client/admin/containers/ActiveUserAdmin.js +++ b/src/main/client/admin/containers/ActiveUserAdmin.js @@ -1,10 +1,7 @@ -import React from 'react' import { connect } from 'react-redux' import { browserHistory } from 'react-router' import UserAdmin from '../components/UserAdmin' -import { setVisibilitySearchText } from '../../manager/actions/visibilityFilter' - import { fetchUsers, createUser, @@ -12,7 +9,6 @@ import { setUserPage, setUserQueryString } from '../actions/admin' - import { updateUserData } from '../../manager/actions/user' import { fetchProjects } from '../../manager/actions/projects' import { fetchProjectFeeds } from '../../manager/actions/feeds' @@ -32,8 +28,9 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (!initialProps.activeComponent) { browserHistory.push('/admin/users') } - if (!initialProps.users) + if (!initialProps.users) { dispatch(fetchUsers()) + } // always load projects to prevent interference with public feeds viewer loading of projects dispatch(fetchProjects()) diff --git a/src/main/client/admin/reducers/admin.js b/src/main/client/admin/reducers/admin.js index f700bd298..f7cb4d4f7 100644 --- a/src/main/client/admin/reducers/admin.js +++ b/src/main/client/admin/reducers/admin.js @@ -10,7 +10,7 @@ const admin = (state = { }, action) => { switch (action.type) { case 'REQUESTING_USERS': - return update(state, { isFetching: { $set: true }}) + return update(state, {isFetching: { $set: true }}) case 'RECEIVE_USERS': return update(state, { isFetching: { $set: false }, @@ -18,12 +18,14 @@ const admin = (state = { userCount: { $set: action.totalUserCount } }) case 'CREATED_USER': - if (state.users) - return update(state, { users: { $push: [action.profile] }}) + if (state.users) { + return update(state, {users: { $push: [action.profile] }}) + } + break case 'SET_USER_PAGE': - return update(state, { page: { $set: action.page }}) + return update(state, {page: { $set: action.page }}) case 'SET_USER_QUERY_STRING': - return update(state, { userQueryString: { $set: action.queryString }}) + return update(state, {userQueryString: { $set: action.queryString }}) default: return state } diff --git a/src/main/client/alerts/actions/activeAlert.js b/src/main/client/alerts/actions/activeAlert.js index d43180207..e8ad50058 100644 --- a/src/main/client/alerts/actions/activeAlert.js +++ b/src/main/client/alerts/actions/activeAlert.js @@ -66,7 +66,7 @@ export const addActiveEntity = (field = 'AGENCY', value = null, agency = null, n } } // set agency of new entity - if (agency){ + if (agency) { newEntity.entity.agency = agency } newEntity.entity[field.toLowerCase()] = value diff --git a/src/main/client/alerts/actions/alerts.js b/src/main/client/alerts/actions/alerts.js index 5f7600a6b..c202513e4 100644 --- a/src/main/client/alerts/actions/alerts.js +++ b/src/main/client/alerts/actions/alerts.js @@ -1,12 +1,10 @@ -import { push } from 'react-router-redux' import { browserHistory } from 'react-router' -import clone from 'clone' +import fetch from 'isomorphic-fetch' +import { fetchStopsAndRoutes } from '../../gtfs/actions/general' -import { fetchStopsAndRoutes } from '../../gtfs/actions/general' import { secureFetch } from '../../common/util/util' import { getAlertsUrl, getFeedId } from '../../common/util/modules' import { setErrorMessage } from '../../manager/actions/status' -import moment from 'moment' // alerts management action @@ -26,8 +24,9 @@ export function createAlert (entity, agency) { type: type } - if (agency !== null) + if (agency !== null) { newEntity.agency = agency + } const typeKey = type.toLowerCase() newEntity[typeKey] = entity @@ -38,56 +37,15 @@ export function createAlert (entity, agency) { id: nextAlertId, title: '', // 'New Alert', affectedEntities: entities, - published: false, - // start: moment().unix()*1000, - // end: moment().add(30, 'day').unix()*1000 + published: false } browserHistory.push('/alerts/new') dispatch(updateActiveAlert(alert)) } } -/*export const createAlert = (entity) => { - nextAlertId-- - let entities = [] - if (entity) { - nextStopEntityId++ - let type = typeof entity.stop_id !== 'undefined' ? 'STOP' : 'ROUTE' - let newEntity = { - id: nextStopEntityId, - type: type - } - const typeKey = type.toLowerCase() - newEntity[typeKey] = entity - entities.push(newEntity) - } - return { - type: 'CREATE_ALERT', - alert: { - id: nextAlertId, - title: 'New Alert', - affectedEntities: entities, - published: false - } - } -}*/ - -/*export const saveAlert = (alert) => { - return { - type: 'SAVE_ALERT', - alert - } -}*/ - -/*export const editAlert = (alert) => { - return { - type: 'EDIT_ALERT', - alert - } -}*/ - export const deleteAlert = (alert) => { - return function (dispatch, getState){ + return function (dispatch, getState) { console.log('deleting', alert) const user = getState().user const url = getAlertsUrl() + '/' + alert.id @@ -101,7 +59,7 @@ export const deleteAlert = (alert) => { 'Authorization': 'Bearer ' + user.token } }).then((res) => { - console.log('status='+res.status) + // console.log('status=' + res.status) browserHistory.push('/alerts') dispatch(fetchRtdAlerts()) }) @@ -110,7 +68,7 @@ export const deleteAlert = (alert) => { export const requestRtdAlerts = () => { return { - type: 'REQUEST_RTD_ALERTS', + type: 'REQUEST_RTD_ALERTS' } } @@ -153,14 +111,14 @@ export const updateActiveAlert = (alert) => { } } -export function editAlert(alert) { +export function editAlert (alert) { return function (dispatch, getState) { dispatch(updateActiveAlert(alert)) - browserHistory.push('/alerts/alert/'+alert.id) + browserHistory.push(`/alerts/alert/${alert.id}`) } } -export function fetchEntity(entity, activeProject) { +export function fetchEntity (entity, activeProject) { const feed = activeProject.feedSources.find(f => getFeedId(f) === entity.entity.AgencyId) const feedId = getFeedId(feed) const url = entity.type === 'stop' ? `/api/manager/stops/${entity.entity.StopId}?feed=${feedId}` : `/api/manager/routes/${entity.entity.RouteId}?feed=${feedId}` @@ -171,11 +129,11 @@ export function fetchEntity(entity, activeProject) { .then((object) => { return object }).catch((error) => { - // console.log('caught', error) + console.log('caught', error) }) } -export function saveAlert(alert) { +export function saveAlert (alert) { return function (dispatch, getState) { console.log('saving...') const user = getState().user @@ -187,8 +145,8 @@ export function saveAlert(alert) { Cause: alert.cause || 'UNKNOWN_CAUSE', Effect: alert.effect || 'UNKNOWN_EFFECT', Published: alert.published ? 'Yes' : 'No', - StartDateTime: alert.start/1000 || 0, - EndDateTime: alert.end/1000 || 0, + StartDateTime: alert.start / 1000 || 0, + EndDateTime: alert.end / 1000 || 0, ServiceAlertEntities: alert.affectedEntities.map((entity) => { return { Id: entity.id, @@ -217,7 +175,7 @@ export function saveAlert(alert) { }, body: JSON.stringify(json) }).then((res) => { - console.log('status='+res.status) + console.log('status=' + res.status) browserHistory.push('/alerts') dispatch(fetchRtdAlerts()) }) diff --git a/src/main/client/alerts/components/AffectedEntity.js b/src/main/client/alerts/components/AffectedEntity.js index 5adb8c43d..bffeb8f52 100644 --- a/src/main/client/alerts/components/AffectedEntity.js +++ b/src/main/client/alerts/components/AffectedEntity.js @@ -1,15 +1,15 @@ -import React from 'react' - -import { ListGroupItem, Row, Col, Button, FormControl, Collapse } from 'react-bootstrap' +import React, { Component } from 'react' import {Icon} from '@conveyal/woonerf' -import GtfsSearch from '../../gtfs/components/gtfssearch' - -import modes from '../modes' -import { getFeed, getFeedId } from '../../common/util/modules' +import { ListGroupItem, Row, Col, Button, Collapse, Glyphicon } from 'react-bootstrap' +import { getFeed } from '../../common/util/modules' import { getRouteNameAlerts } from '../../editor/util/gtfs' +import AgencySelector from './AgencySelector' +import ModeSelector from './ModeSelector' +import StopSelector from './StopSelector' +import RouteSelector from './RouteSelector' -export default class AffectedEntity extends React.Component { +export default class AffectedEntity extends Component { constructor (props) { super(props) this.state = { @@ -26,44 +26,43 @@ export default class AffectedEntity extends React.Component { const feed = getFeed(this.props.feeds, entity.stop.feed_id) agencyName = typeof feed !== 'undefined' ? feed.name : 'Unknown agency' } - const routeName = entity.route ? getRouteNameAlerts(entity.route) : entity.route_id let stopName = entity.stop ? `${entity.stop.stop_name} (${entity.stop.stop_id}) ${agencyName}` : entity.stop_id let summary = '' - switch (type) { - case 'AGENCY' : - return ( - - {agencyName}
    - Note: this selection will apply to all stops and routes for {agencyName}. -
    - ) - case 'STOP' : - summary = stopName - if (routeName) { - summary += ` for ${routeName}` - } - return {summary} - case 'ROUTE' : - summary = routeName - if (stopName) { - summary += ` at ${stopName}` - } - return {summary} - case 'MODE' : - summary = val.name - if (stopName) { - summary += ` at ${stopName}` - } - return ( - - {type}: {summary}
    - Note: this selection will apply to all {val.name.toLowerCase()} routes{stopName && ` stopping at ${stopName}`}. -
    - ) - } + switch (type) { + case 'AGENCY' : + return ( + + {agencyName}
    + Note: this selection will apply to all stops and routes for {agencyName}. +
    + ) + case 'STOP' : + summary = stopName + if (routeName) { + summary += ` for ${routeName}` + } + return {summary} + case 'ROUTE' : + summary = routeName + if (stopName) { + summary += ` at ${stopName}` + } + return {summary} + case 'MODE' : + summary = val.name + if (stopName) { + summary += ` at ${stopName}` + } + return ( + + {type}: {summary}
    + Note: this selection will apply to all {val.name.toLowerCase()} routes{stopName && ` stopping at ${stopName}`}. +
    + ) + } } renderHeader () { return ( @@ -185,9 +184,6 @@ export default class AffectedEntity extends React.Component { } } render () { - const getMode = (id) => { - return modes.find((mode) => mode.gtfsType === +id) - } return ( { - return modes.find((mode) => mode.gtfsType === +id ) - } - return ( -
    - { - this.props.entityUpdated(this.props.entity, 'AGENCY', getFeed(this.props.feeds, evt.target.value)) - }} - //value={this.props.entity.type} - > - {this.props.feeds.map((feed) => { - return - })} - -
    - ) - } -} - -class ModeSelector extends React.Component { - render () { - const getMode = (id) => { - return modes.find((mode) => mode.gtfsType === +id ) - } - return ( -
    - { - this.props.entityUpdated(this.props.entity, 'MODE', getMode(evt.target.value)) - }} - //value={this.props.entity.type} - > - {modes.map((mode) => { - return - })} - -
    - ) - } -} - -class RouteSelector extends React.Component { - constructor (props) { - super(props) - } - render () { - const getMode = (id) => { - return modes.find((mode) => mode.gtfsType === +id ) - } - var routes = [] - const feed = this.props.route ? getFeed(this.props.feeds, this.props.route.feed_id) : null - const agencyName = feed ? feed.name : 'Unknown agency' - return ( -
    - { - if (typeof evt !== 'undefined' && evt !== null) - this.props.entityUpdated(this.props.entity, 'ROUTE', evt.route, evt.agency) - else if (evt == null) { - if (this.props.filterByStop) { - this.props.entityUpdated(this.props.entity, 'ROUTE', null, feed) - } - else { - this.props.entityUpdated(this.props.entity, 'ROUTE', null, null) - } - } - }} - value={ - this.props.route - ? { - route: this.props.route, - value: this.props.route.route_id, - label: `${getRouteNameAlerts(this.props.route)} (${agencyName})` - } - : '' - } - /> -
    - ) - } -} - -class StopSelector extends React.Component { - constructor (props) { - super(props) - } - render () { - const getMode = (id) => { - return modes.find((mode) => mode.gtfsType === +id ) - } - var stops = [] - const feed = this.props.stop ? getFeed(this.props.feeds, this.props.stop.feed_id) : null - const agencyName = feed ? feed.name : 'Unknown agency' - return ( -
    - { - if (evt) - this.props.entityUpdated(this.props.entity, 'STOP', evt.stop, evt.agency) - else if (evt == null) - this.props.entityUpdated(this.props.entity, 'STOP', null, null) - }} - value={ - this.props.stop - ? { - stop: this.props.stop, - value: this.props.stop.stop_id, - label: `${this.props.stop.stop_name} (${agencyName})` - } - : '' - } - /> - -
    - ) - } -} diff --git a/src/main/client/alerts/components/AffectedServices.js b/src/main/client/alerts/components/AffectedServices.js new file mode 100644 index 000000000..c081f520e --- /dev/null +++ b/src/main/client/alerts/components/AffectedServices.js @@ -0,0 +1,108 @@ +import React, {Component} from 'react' +import { Panel, Label, ListGroup, ListGroupItem, Row, Col, Button } from 'react-bootstrap' + +import AffectedEntity from './AffectedEntity' +import GtfsSearch from '../../gtfs/components/gtfssearch' + +export default class AffectedServices extends Component { + render () { + const { sortedFeeds, onAddEntityClick, newEntityId, alert, activeFeeds, onDeleteEntityClick, entityUpdated } = this.props + return ( + } + > + + + + + + + + + { + console.log('we need to add this entity to the store', evt) + if (typeof evt !== 'undefined' && evt !== null) { + if (evt.stop) { + onAddEntityClick('STOP', evt.stop, evt.agency, newEntityId) + } else if (evt.route) { + onAddEntityClick('ROUTE', evt.route, evt.agency, newEntityId) + } + } + }} + /> + + + + {alert.affectedEntities + .sort((a, b) => b.id - a.id) // reverse sort by entity id + .map((entity) => { + return ( + + ) + })} + + + ) + } +} + +class ServicesHeader extends Component { + render () { + const { entities } = this.props + const counts = [ + { + singular: 'agency', + plural: 'agencies', + count: entities.filter(e => e.type === 'AGENCY').length + }, + { + singular: 'route', + plural: 'routes', + count: entities.filter(e => e.type === 'ROUTE').length + }, + { + singular: 'stop', + plural: 'stops', + count: entities.filter(e => e.type === 'STOP').length + }, + { + singular: 'mode', + plural: 'modes', + count: entities.filter(e => e.type === 'MODE').length + } + ] + return ( + + Affected Service{counts.map(c => { + return c.count + ? + : null + })} + + ) + } +} diff --git a/src/main/client/alerts/components/AgencySelector.js b/src/main/client/alerts/components/AgencySelector.js new file mode 100644 index 000000000..f3ffd42f9 --- /dev/null +++ b/src/main/client/alerts/components/AgencySelector.js @@ -0,0 +1,30 @@ +import React, { Component, PropTypes } from 'react' +import { FormControl } from 'react-bootstrap' + +import { getFeed, getFeedId } from '../../common/util/modules' + +export default class AgencySelector extends Component { + static propTypes = { + feeds: PropTypes.array, + entity: PropTypes.object, + entityUpdated: PropTypes.func + } + render () { + const { feeds, entity, entityUpdated } = this.props + return ( +
    + { + entityUpdated(entity, 'AGENCY', getFeed(feeds, evt.target.value)) + }} + > + {feeds.map((feed) => { + return + })} + +
    + ) + } +} diff --git a/src/main/client/alerts/components/AlertEditor.js b/src/main/client/alerts/components/AlertEditor.js index 2450105a1..cc464eedf 100644 --- a/src/main/client/alerts/components/AlertEditor.js +++ b/src/main/client/alerts/components/AlertEditor.js @@ -2,105 +2,63 @@ import {Icon} from '@conveyal/woonerf' import React from 'react' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' -import { Grid, Row, Col, Label, ButtonGroup, Button, FormControl, ControlLabel, FormGroup, Panel, ListGroup, ListGroupItem } from 'react-bootstrap' +import { Grid, Row, Col, ButtonGroup, Button, FormControl, ControlLabel, FormGroup } from 'react-bootstrap' import DateTimeField from 'react-bootstrap-datetimepicker' import ManagerPage from '../../common/components/ManagerPage' import Loading from '../../common/components/Loading' -import AffectedEntity from './AffectedEntity' +import AffectedServices from './AffectedServices' import GtfsMapSearch from '../../gtfs/components/gtfsmapsearch' -import GtfsSearch from '../../gtfs/components/gtfssearch' import GlobalGtfsFilter from '../../gtfs/containers/GlobalGtfsFilter' import { checkEntitiesForFeeds } from '../../common/util/permissions' +import { CAUSES, EFFECTS } from '../util' import { browserHistory } from 'react-router' import moment from 'moment' -const causes = [ - 'UNKNOWN_CAUSE', - 'TECHNICAL_PROBLEM', - 'STRIKE', - 'DEMONSTRATION', - 'ACCIDENT', - 'HOLIDAY', - 'WEATHER', - 'MAINTENANCE', - 'CONSTRUCTION', - 'POLICE_ACTIVITY', - 'MEDICAL_EMERGENCY', - 'OTHER_CAUSE' -] - -const effects = [ - 'UNKNOWN_EFFECT', - 'NO_SERVICE', - 'REDUCED_SERVICE', - 'SIGNIFICANT_DELAYS', - 'DETOUR', - 'ADDITIONAL_SERVICE', - 'MODIFIED_SERVICE', - 'STOP_MOVED', - 'OTHER_EFFECT' -] - export default class AlertEditor extends React.Component { componentWillMount () { this.props.onComponentMount(this.props) } - renderServicesHeader (entities) { - const counts = [ - { - singular: 'agency', - plural: 'agencies', - count: entities.filter(e => e.type === 'AGENCY').length, - }, - { - singular: 'route', - plural: 'routes', - count: entities.filter(e => e.type === 'ROUTE').length, - }, - { - singular: 'stop', - plural: 'stops', - count: entities.filter(e => e.type === 'STOP').length, - }, - { - singular: 'mode', - plural: 'modes', - count: entities.filter(e => e.type === 'MODE').length, - }, - ] - return ( - - Affected Service{counts.map(c => { - return c.count - ? - : null - })} - - ) + validateAndSave = () => { + console.log('times', this.props.alert.end, this.props.alert.start) + if (!this.props.alert.title) { + window.alert('You must specify an alert title') + return + } + if (!this.props.alert.end || !this.props.alert.start) { + window.alert('Alert must have a start and end date') + return + } + if (this.props.alert.end < this.props.alert.start) { + window.alert('Alert end date cannot be before start date') + return + } + if (moment(this.props.alert.end).isBefore(moment())) { + window.alert('Alert end date cannot be before the current date') + return + } + if (this.props.alert.affectedEntities.length === 0) { + window.alert('You must specify at least one affected entity') + return + } + this.props.onSaveClick(this.props.alert) } render () { if (!this.props.alert) { return ( - + ) } var compare = function (a, b) { var aName = a.shortName || a.name var bName = b.shortName || b.name - // return 511 Staff as first in list to avoid 511 Emergency being first in list if (/511 Staff/.test(aName)) return -1 if (/511 Staff/.test(bName)) return 1 - if (aName < bName) return -1 if (aName > bName) return 1 return 0 @@ -123,83 +81,56 @@ export default class AlertEditor extends React.Component { : 1 return ( - - 0 ? `Alert ${this.props.alert.id}` : 'New Alert'} - /> - - - + + 0 ? `Alert ${this.props.alert.id}` : 'New Alert'} + /> + + + + + + + + + - - - - + - - - - - - + }) + }} + >Delete + + + - - - - + + + + Alert Title this.props.titleChanged(evt.target.value)} /> - - -
    Start
    - {this.props.alert.start - ? this.props.startChanged(time)} - /> - : this.props.startChanged(time)} - /> - } - - -
    End
    - {this.props.alert.end - ? this.props.endChanged(time)} - /> - : this.props.endChanged(time)} - /> - } - -
    - - - - Cause - this.props.causeChanged(evt.target.value)} - value={this.props.alert.cause} - > - {causes.map((cause) => { - return - })} - - - - - - Effect - this.props.effectChanged(evt.target.value)} - value={this.props.alert.effect} - > - {effects.map((effect) => { - return - })} - - - - - - - - - Description - this.props.descriptionChanged(evt.target.value)} - /> - - - - - URL - this.props.urlChanged(evt.target.value)} - /> - - - - - - {/* Affected Service panel */} - + +
    Start
    + {this.props.alert.start + ? this.props.startChanged(time)} + /> + : this.props.startChanged(time)} + /> + } + + +
    End
    + {this.props.alert.end + ? this.props.endChanged(time)} + /> + : this.props.endChanged(time)} + /> + } + +
    + + + + Cause + this.props.causeChanged(evt.target.value)} + value={this.props.alert.cause} > - - - - - - - - - { - console.log('we need to add this entity to the store', evt) - if (typeof evt !== 'undefined' && evt !== null){ - if (evt.stop){ - this.props.onAddEntityClick('STOP', evt.stop, evt.agency, newEntityId) - } - else if (evt.route) - this.props.onAddEntityClick('ROUTE', evt.route, evt.agency, newEntityId) - } - }} - /> - - - - {this.props.alert.affectedEntities - .sort((a, b) => b.id - a.id) // reverse sort by entity id - .map((entity) => { - return ( - - ) - })} - - - - - - - - - - { + return + })} + + + + + + Effect + this.props.effectChanged(evt.target.value)} + value={this.props.alert.effect} + > + {EFFECTS.map((effect) => { + return + })} + + + + + + + + Description + this.props.descriptionChanged(evt.target.value)} /> - - - - -
    -
    -
    + + + + + URL + this.props.urlChanged(evt.target.value)} + /> + + +
    + + + + + + + + + + + + + + + +
    + + ) } } diff --git a/src/main/client/alerts/components/AlertPreview.js b/src/main/client/alerts/components/AlertPreview.js index d31b0d559..641e80475 100644 --- a/src/main/client/alerts/components/AlertPreview.js +++ b/src/main/client/alerts/components/AlertPreview.js @@ -20,10 +20,10 @@ export default class AlertPreview extends React.Component { const editButtonMessage = this.props.alert.published && deleteIsDisabled ? 'Cannot edit because alert is published' : !canEdit ? 'Cannot alter alerts for other agencies' : 'Edit alert' const publishedLabel = this.props.alert.published - ? - : + ? + : const entitiesLabel = this.props.alert.affectedEntities.length - ? + ? : return ( @@ -61,9 +60,9 @@ export default class AlertPreview extends React.Component { {publishedLabel} {this.props.alert.published ? 'Published' : 'Draft'}

    {this.props.alert.description}

    -

    URL: {this.props.alert.url}

    +

    URL: {this.props.alert.url}

    - {entitiesLabel} affected service(s) + {entitiesLabel} affected service(s)

    ) diff --git a/src/main/client/alerts/components/AlertsList.js b/src/main/client/alerts/components/AlertsList.js index 2961e6cb2..d5f3fef1a 100644 --- a/src/main/client/alerts/components/AlertsList.js +++ b/src/main/client/alerts/components/AlertsList.js @@ -23,9 +23,6 @@ export default class AlertsList extends Component { searchTextChanged: PropTypes.func, visibilityFilterChanged: PropTypes.func } - constructor (props) { - super(props) - } render () { // console.log(this.props) var compare = function (a, b) { @@ -35,8 +32,7 @@ export default class AlertsList extends Component { if (aName > bName) return 1 return 0 } - let sortedFeeds = this.props.editableFeeds.sort(compare) - + let sortedFeeds = this.props.editableFeeds.sort(compare) return (
    diff --git a/src/main/client/alerts/components/AlertsViewer.js b/src/main/client/alerts/components/AlertsViewer.js index 9eb96ce95..cce1aaeb6 100644 --- a/src/main/client/alerts/components/AlertsViewer.js +++ b/src/main/client/alerts/components/AlertsViewer.js @@ -1,7 +1,7 @@ import React from 'react' import Helmet from 'react-helmet' import {Icon} from '@conveyal/woonerf' -import { Grid, Row, Col, Button } from 'react-bootstrap' +import { Grid, Row, Col } from 'react-bootstrap' import ManagerPage from '../../common/components/ManagerPage' import CreateAlert from '../components/CreateAlert' @@ -9,19 +9,10 @@ import VisibleAlertsList from '../containers/VisibleAlertsList' import GlobalGtfsFilter from '../../gtfs/containers/GlobalGtfsFilter' import GtfsMapSearch from '../../gtfs/components/gtfsmapsearch' -import { Link } from 'react-router' - export default class AlertsViewer extends React.Component { - - constructor (props) { - super(props) - //console.log("AV activeFeeds", this.props.activeFeeds); - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { const createDisabled = this.props.project && this.props.user ? !this.props.user.permissions.hasProjectPermission(this.props.project.id, 'edit-alert') : true return ( diff --git a/src/main/client/alerts/components/CreateAlert.js b/src/main/client/alerts/components/CreateAlert.js index 8a15f626a..8cf737f45 100644 --- a/src/main/client/alerts/components/CreateAlert.js +++ b/src/main/client/alerts/components/CreateAlert.js @@ -1,11 +1,7 @@ import React from 'react' -import { connect } from 'react-redux' import { Button } from 'react-bootstrap' export default class CreateAlert extends React.Component { - constructor (props) { - super(props) - } render () { return ( //feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name ) diff --git a/src/main/client/common/components/JobMonitor.js b/src/main/client/common/components/JobMonitor.js index 744278163..6c3ff2111 100644 --- a/src/main/client/common/components/JobMonitor.js +++ b/src/main/client/common/components/JobMonitor.js @@ -41,7 +41,7 @@ export default class JobMonitor extends Pure { return ( this.popover = SidebarPopover} + ref={(SidebarPopover) => { this.popover = SidebarPopover }} title='Server Jobs' {...this.props}> {this.props.jobMonitor.retired.map(job => { diff --git a/src/main/client/common/components/LanguageSelect.js b/src/main/client/common/components/LanguageSelect.js index c34102775..c4d2fe3cf 100644 --- a/src/main/client/common/components/LanguageSelect.js +++ b/src/main/client/common/components/LanguageSelect.js @@ -1,69 +1,46 @@ -import {Icon} from '@conveyal/woonerf' import React from 'react' -import fetch from 'isomorphic-fetch' -import {Label, FormControl} from 'react-bootstrap' -import { PureComponent, shallowEqual } from 'react-pure-render' +import { shallowEqual } from 'react-pure-render' import Select from 'react-select' +import ISO6391 from 'iso-639-1' -import languages from '../util/languages' import { getComponentMessages, getMessage } from '../util/config' export default class LanguageSelect extends React.Component { - constructor(props) { + constructor (props) { super(props) this.state = { value: this.props.value - }; - } - - cacheOptions (options) { - options.forEach(o => { - this.options[o.value] = o.feature - }) + } } - componentWillReceiveProps (nextProps) { if (!shallowEqual(nextProps.value, this.props.value)) { this.setState({value: nextProps.value}) console.log('props received', this.state.value) } } - renderOption (option) { - return {option.region ? : } {option.label} {option.link} - } - onChange (value) { + _onChange = (value) => { this.setState({value}) + this.props.onChange && this.props.onChange(value) + } + _getOptions = () => { + return ISO6391.getAllCodes().map(code => ({value: code, label: ISO6391.getName(code)})) } - render() { - // console.log('render search feeds', this.props.feeds) + render () { const messages = getComponentMessages('LanguageSelect') - - const options = languages.map(lang => ({value: lang.code, label: lang.name})) - const handleChange = (input) => { - this.onChange(input) - this.props.onChange && this.props.onChange(input) - } - - const onFocus = (input) => { - // clear options to onFocus to ensure only valid route/stop combinations are selected - // this.refs.gtfsSelect.loadOptions('') - } - const placeholder = getMessage(messages, 'placeholder') return ( - ) } } diff --git a/src/main/client/common/components/Loading.js b/src/main/client/common/components/Loading.js index 39c310732..c62a58576 100644 --- a/src/main/client/common/components/Loading.js +++ b/src/main/client/common/components/Loading.js @@ -2,7 +2,6 @@ import React from 'react' import { Row, Col } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' - export default class Loading extends React.Component { render () { diff --git a/src/main/client/common/components/MapModal.js b/src/main/client/common/components/MapModal.js index d59931350..fe710ff4a 100644 --- a/src/main/client/common/components/MapModal.js +++ b/src/main/client/common/components/MapModal.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { Component } from 'react' import { Modal, Button } from 'react-bootstrap' import { Map, Marker, TileLayer, FeatureGroup } from 'react-leaflet' // import { EditControl } from 'react-leaflet-draw' @@ -7,22 +7,13 @@ import { getConfigProperty } from '../util/config' let polyline -export default class MapModal extends React.Component { +export default class MapModal extends Component { constructor (props) { super(props) this.state = { showModal: false } } - // componentWillReceiveProps (newProps) { - // if (newProps.title) { - // this.setState({ - // showModal: true, - // title: newProps.title, - // body: newProps.body, - // }) - // } - // } close () { this.setState({ showModal: false @@ -43,7 +34,7 @@ export default class MapModal extends React.Component { draw: true }) } - getMap() { + getMap () { return this.refs.map } _onEditPath (e) { @@ -51,7 +42,7 @@ export default class MapModal extends React.Component { } _onCreate (e) { - polyline = e.layer; + polyline = e.layer this.setState({rectangle: e.layer}) // To edit this polyline call : polyline.handler.enable() console.log('Path created !', polyline) @@ -62,11 +53,11 @@ export default class MapModal extends React.Component { _onDeleted (e) { console.log('Path deleted !') - if (this.refs.drawnItems.getLayers().length === 0){ - this.setState({draw: true}) + if (this.refs.drawnItems.getLayers().length === 0) { + this.setState({draw: true}) } } - initializeMap() { + initializeMap () { if (this.mapInitialized || this.props.initialized) return const leafletMap = this.getMap().leafletElement leafletMap.invalidateSize() @@ -87,12 +78,12 @@ export default class MapModal extends React.Component { height: '300px', width: '100%' } - const marker = this.state.marker ? - - : null + const marker = this.state.marker + ? + : null const clickHandler = (e) => { console.log(e.latlng) if (this.state.markerSelect) { @@ -119,33 +110,34 @@ export default class MapModal extends React.Component { /> {this.state.rectangleSelect ? - this._onEditPath(e)} - onCreated={(e) => this._onCreate(e)} - onDeleted={(e) => this._onDeleted(e)} - position='topright' - draw={this.state.draw - ? { - circle: false, - polyline: false, - polygon: false, - marker: false - } - : { - circle: false, - polyline: false, - polygon: false, - marker: false, - rectangle: false - } + ref='drawnItems' + > + {/* this._onEditPath(e)} + onCreated={(e) => this._onCreate(e)} + onDeleted={(e) => this._onDeleted(e)} + position='topright' + draw={this.state.draw + ? { + circle: false, + polyline: false, + polygon: false, + marker: false } - /> - + : { + circle: false, + polyline: false, + polygon: false, + marker: false, + rectangle: false + } + } + /> + */} + : null } - {/*this.state.marker + {/* this.state.marker ? this.state.marker.map(m => { return ( - - - - - {(() => { - switch (this.props.reason) { - case 'NOT_LOGGED_ID': return You must be logged in to access this area. - case 'INSUFFICIENT_PERMISSIONS': return This user does not have permission to access this area. - default: return Unable to Access Module - } - })()} - - - -
    - ) - } -} diff --git a/src/main/client/common/components/PageNotFound.js b/src/main/client/common/components/PageNotFound.js index b82092fa1..345fe0a5b 100644 --- a/src/main/client/common/components/PageNotFound.js +++ b/src/main/client/common/components/PageNotFound.js @@ -4,10 +4,6 @@ import { Grid, Row, Col } from 'react-bootstrap' import ManagerPage from './ManagerPage' import { Link } from 'react-router' export default class PageNotFound extends React.Component { - constructor (props) { - super(props) - } - render () { return (
    @@ -16,7 +12,7 @@ export default class PageNotFound extends React.Component {

    Page Not Found.

    -

    Go to Home Page

    +

    Go to Home Page

    @@ -24,12 +20,4 @@ export default class PageNotFound extends React.Component {
    ) } - render() { - return ( -
    -

    Page Not Found.

    -

    Go to Home Page

    -
    - ) - } } diff --git a/src/main/client/common/components/SidebarNavItem.js b/src/main/client/common/components/SidebarNavItem.js index 7365226ce..1bfed07ab 100644 --- a/src/main/client/common/components/SidebarNavItem.js +++ b/src/main/client/common/components/SidebarNavItem.js @@ -39,7 +39,7 @@ export default class SidebarNavItem extends Component { borderLeft: this.props.active ? '4px solid #2889CA' : '4px solid rgba(0,0,0,0)', textDecoration: 'none', borderRight: this.props.active ? '5px solid rgba(0,0,0,0)' : '5px solid rgba(0,0,0,0)', - backgroundColor: this.props.active ? '#313131' : 'rgba(0,0,0,0)', + backgroundColor: this.props.active ? '#313131' : 'rgba(0,0,0,0)' } const iconContainerStyle = { @@ -67,24 +67,24 @@ export default class SidebarNavItem extends Component { } const icon = this.props.image ?
    - -
    + +
    :
    - -
    + +
    const tooltip = {this.props.label} let containerProps = { onMouseEnter: () => this.toggleHover(), - onMouseLeave: () => this.toggleHover(), + onMouseLeave: () => this.toggleHover() } if (!this.props.link) { containerProps.onClick = () => this.props.onClick() @@ -103,13 +103,13 @@ export default class SidebarNavItem extends Component { ) const navItem = this.props.link ? - {container} - + {container} + : container return this.props.expanded ? navItem : - {navItem} - + {navItem} + } } diff --git a/src/main/client/common/components/StatusMessage.js b/src/main/client/common/components/StatusMessage.js index 54c600944..b670173fd 100644 --- a/src/main/client/common/components/StatusMessage.js +++ b/src/main/client/common/components/StatusMessage.js @@ -1,5 +1,4 @@ import React from 'react' - import { Button } from 'react-bootstrap' export default class StatusMessage extends React.Component { @@ -36,12 +35,12 @@ export default class StatusMessage extends React.Component {
    {this.props.message && this.state.visible ? + bsStyle='info' + bsSize='large' + onClick={() => this.clear()} + > + {this.props.message} + : null }
    diff --git a/src/main/client/common/components/StatusModal.js b/src/main/client/common/components/StatusModal.js index 4b2a10c31..8414c89b3 100644 --- a/src/main/client/common/components/StatusModal.js +++ b/src/main/client/common/components/StatusModal.js @@ -14,7 +14,7 @@ export default class StatusModal extends React.Component { this.setState({ showModal: true, title: newProps.title, - body: newProps.body, + body: newProps.body }) } } diff --git a/src/main/client/common/components/TimezoneSelect.js b/src/main/client/common/components/TimezoneSelect.js index 91f87c7e7..beef5cd54 100644 --- a/src/main/client/common/components/TimezoneSelect.js +++ b/src/main/client/common/components/TimezoneSelect.js @@ -1,19 +1,21 @@ import React, { PropTypes } from 'react' -import fetch from 'isomorphic-fetch' -import { Glyphicon, Label, FormControl } from 'react-bootstrap' -import { PureComponent, shallowEqual } from 'react-pure-render' +import { Glyphicon } from 'react-bootstrap' +import { shallowEqual } from 'react-pure-render' import Select from 'react-select' // import timezones from '../util/timezones' -import moment_tz from 'moment-timezone' +import momentTZ from 'moment-timezone' import { getComponentMessages, getMessage } from '../util/config' export default class TimezoneSelect extends React.Component { - constructor(props) { + static propTypes = { + value: PropTypes.string + } + constructor (props) { super(props) this.state = { value: this.props.value - }; + } } cacheOptions (options) { @@ -29,16 +31,16 @@ export default class TimezoneSelect extends React.Component { } } renderOption (option) { - return {option.region ? : } {option.label} {option.link} + return {option.region ? : } {option.label} {option.link} } onChange (value) { this.setState({value}) } - render() { + render () { // console.log('render search feeds', this.props.feeds) const messages = getComponentMessages('TimezoneSelect') - const options = moment_tz.tz.names().map(tz => ({value: tz, label: tz})) + const options = momentTZ.tz.names().map(tz => ({value: tz, label: tz})) const handleChange = (input) => { this.onChange(input) this.props.onChange && this.props.onChange(input) @@ -51,19 +53,19 @@ export default class TimezoneSelect extends React.Component { const placeholder = getMessage(messages, 'placeholder') return ( - ) } } diff --git a/src/main/client/common/containers/ActiveSidebar.js b/src/main/client/common/containers/ActiveSidebar.js index 72071033b..27c9858a1 100644 --- a/src/main/client/common/containers/ActiveSidebar.js +++ b/src/main/client/common/containers/ActiveSidebar.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import Sidebar from '../components/Sidebar' diff --git a/src/main/client/common/containers/ActiveSidebarNavItem.js b/src/main/client/common/containers/ActiveSidebarNavItem.js index 346f57ba3..c2dcf72cf 100644 --- a/src/main/client/common/containers/ActiveSidebarNavItem.js +++ b/src/main/client/common/containers/ActiveSidebarNavItem.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import SidebarNavItem from '../components/SidebarNavItem' diff --git a/src/main/client/common/containers/App.js b/src/main/client/common/containers/App.js index a8f3f6e58..97d8d1ef1 100644 --- a/src/main/client/common/containers/App.js +++ b/src/main/client/common/containers/App.js @@ -23,7 +23,6 @@ import ActiveSignEditor from '../../signs/containers/ActiveSignEditor' import PageNotFound from '../components/PageNotFound' import ActiveGtfsPlusEditor from '../../gtfsplus/containers/ActiveGtfsPlusEditor' import ActiveGtfsEditor from '../../editor/containers/ActiveGtfsEditor' -import ActiveGtfsTableEditor from '../../editor/containers/ActiveGtfsTableEditor' class App extends Component { static propTypes = { @@ -32,21 +31,8 @@ class App extends Component { login: PropTypes.func, history: PropTypes.object } - constructor (props) { - super(props) - } - - componentDidMount () { - // set up status checkLogin - /* setInterval(() => { - console.log('status check!', this.props.user); - this.props.checkJobStatus() - }, 5000) */ - } - render () { const requireAuth = (nextState, replace, callback) => { - console.log(nextState) this.props.checkExistingLogin() .then((action) => { if (this.props.user.profile === null) { @@ -87,7 +73,7 @@ class App extends Component { {path: '/project', component: ActiveProjectsList, onEnter: requireAuth}, {path: '/project/:projectId(/:subpage)(/:subsubpage)', component: ActiveProjectViewer, onEnter: requireAuth}, { - path: '/feed/:feedSourceId/edit(/:subpage)(/:entity)(/:subsubpage)(/:subentity)(/:subsubcomponent)(/:subsubentity)', + path: '/feed/:feedSourceId/edit(/:activeComponent)(/:activeEntityId)(/:subComponent)(/:subEntityId)(/:subSubComponent)(/:activeSubSubEntity)', component: ActiveGtfsEditor, onEnter: requireAuth }, @@ -95,7 +81,6 @@ class App extends Component { {path: '/deployment/:deploymentId', component: ActiveDeploymentViewer, onEnter: requireAuth}, {path: '/gtfsplus/:feedSourceId/:feedVersionId', component: ActiveGtfsPlusEditor, onEnter: requireAuth}, - {path: '/feed/:feedSourceId/editTable/:feedVersionId(/:subpage)', component: ActiveGtfsTableEditor, onEnter: requireAuth}, {path: '*', component: PageNotFound} ] @@ -139,9 +124,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { } } -App = connect( +export default connect( mapStateToProps, mapDispatchToProps )(App) - -export default App diff --git a/src/main/client/common/containers/CurrentStatusMessage.js b/src/main/client/common/containers/CurrentStatusMessage.js index 9c98c159a..2fd0dc64a 100644 --- a/src/main/client/common/containers/CurrentStatusMessage.js +++ b/src/main/client/common/containers/CurrentStatusMessage.js @@ -1,7 +1,6 @@ -import React from 'react' import { connect } from 'react-redux' -import StatusMessage from '../components/StatusMessage' +import StatusMessage from '../components/StatusMessage' const mapStateToProps = (state, ownProps) => { return { diff --git a/src/main/client/common/containers/CurrentStatusModal.js b/src/main/client/common/containers/CurrentStatusModal.js index e41f5d7da..36b3fcc5d 100644 --- a/src/main/client/common/containers/CurrentStatusModal.js +++ b/src/main/client/common/containers/CurrentStatusModal.js @@ -1,18 +1,17 @@ -import React from 'react' import { connect } from 'react-redux' -import StatusModal from '../components/StatusModal' +import StatusModal from '../components/StatusModal' import { clearStatusModal } from '../../manager/actions/status' const mapStateToProps = (state, ownProps) => { return { title: state.status.modal ? state.status.modal.title : null, - body: state.status.modal ? state.status.modal.body : null, + body: state.status.modal ? state.status.modal.body : null } } const mapDispatchToProps = (dispatch, ownProps) => { return { - clearStatusModal: () => { dispatch(clearStatusModal()) }, + clearStatusModal: () => { dispatch(clearStatusModal()) } } } var CurrentStatusModal = connect( diff --git a/src/main/client/common/containers/FeedSourceActionButton.js b/src/main/client/common/containers/FeedSourceActionButton.js index 8e9150da6..5114c2cbf 100644 --- a/src/main/client/common/containers/FeedSourceActionButton.js +++ b/src/main/client/common/containers/FeedSourceActionButton.js @@ -1,72 +1,71 @@ -import {Icon} from '@conveyal/woonerf' -import React, { Component } from 'react' -import { Button, Glyphicon, MenuItem } from 'react-bootstrap' -import { updateTargetForSubscription } from '../../manager/actions/user' +// import {Icon} from '@conveyal/woonerf' +import { Component } from 'react' +// import { Button, Glyphicon, MenuItem, DropDown } from 'react-bootstrap' import { connect } from 'react-redux' -import { getComponentMessages, getMessage, getConfigProperty } from '../util/config' -import WatchButton from './WatchButton' +// import { getConfigProperty } from '../util/config' +// import WatchButton from './WatchButton' class FeedSourceActionButton extends Component { render () { - return ( - { - console.log(key) - switch (key) { - case 'delete': - return this.props.deleteFeedSourceClicked() - case 'update': - return this.props.updateFeedClicked() - case 'upload': - return this.props.uploadFeedSourceClicked() - case 'deploy': - return this.props.createDeployment(fs) - case 'public': - return browserHistory.push(`/public/feed/${fs.id}`) - } - }} - id={`feed-source-action-button`} - pullRight - > - - - - Update - Upload - {isModuleEnabled('deployment') || getConfigProperty('application.notifications_enabled') - ? - : null - } - {isModuleEnabled('deployment') - ? Deploy - : null - } - {getConfigProperty('application.notifications_enabled') - ? - : null - } - View public page - - Delete - - - ) + // return ( + // { + // console.log(key) + // switch (key) { + // case 'delete': + // return this.props.deleteFeedSourceClicked() + // case 'update': + // return this.props.updateFeedClicked() + // case 'upload': + // return this.props.uploadFeedSourceClicked() + // case 'deploy': + // return this.props.createDeployment(fs) + // case 'public': + // return browserHistory.push(`/public/feed/${fs.id}`) + // } + // }} + // id={`feed-source-action-button`} + // pullRight + // > + // + // + // + // Update + // Upload + // {isModuleEnabled('deployment') || getConfigProperty('application.notifications_enabled') + // ? + // : null + // } + // {isModuleEnabled('deployment') + // ? Deploy + // : null + // } + // {getConfigProperty('application.notifications_enabled') + // ? + // : null + // } + // View public page + // + // Delete + // + // + // ) } } -export default connect()(WatchButton); +export default connect()(FeedSourceActionButton) diff --git a/src/main/client/common/containers/ManagerNavbar.js b/src/main/client/common/containers/ManagerNavbar.js index 4d9dd7eed..3880cce4e 100644 --- a/src/main/client/common/containers/ManagerNavbar.js +++ b/src/main/client/common/containers/ManagerNavbar.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import DatatoolsNavbar from '../components/DatatoolsNavbar' diff --git a/src/main/client/common/containers/StarButton.js b/src/main/client/common/containers/StarButton.js index d2bcdc7fb..c4343d729 100644 --- a/src/main/client/common/containers/StarButton.js +++ b/src/main/client/common/containers/StarButton.js @@ -8,7 +8,7 @@ import { getComponentMessages, getMessage } from '../util/config' class StarButton extends React.Component { render () { - const {dispatch, isStarred, user, target, subscriptionType} = this.props + const {dispatch, isStarred, user, target} = this.props const messages = getComponentMessages('StarButton') return ( diff --git a/src/main/client/common/containers/WatchButton.js b/src/main/client/common/containers/WatchButton.js index 476c5df3f..c19ca3d90 100644 --- a/src/main/client/common/containers/WatchButton.js +++ b/src/main/client/common/containers/WatchButton.js @@ -24,8 +24,8 @@ class WatchButton extends Component { if (this.props.componentClass === 'menuItem') { return ( dispatch(updateTargetForSubscription(user.profile, target, subscriptionType)) } - > + onClick={() => dispatch(updateTargetForSubscription(user.profile, target, subscriptionType))} + > { isWatching ? {getMessage(messages, 'unwatch')} : {getMessage(messages, 'watch')} @@ -35,13 +35,13 @@ class WatchButton extends Component { } else { return ( + onClick={() => dispatch(updateTargetForSubscription(user.profile, target, subscriptionType))} + > + { + isWatching ? {getMessage(messages, 'unwatch')} + : {getMessage(messages, 'watch')} + } + ) } } diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index 5a255e4bd..76c71056e 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -7,7 +7,7 @@ import UserPermissions from './UserPermissions' import { getConfigProperty } from '../util/config' // TODO: Fix PNG import -const icon = "" // import icon from '../../assets/application_logo.png' +const icon = '' // import icon from '../../assets/application_logo.png' export default class Auth0Manager { @@ -38,7 +38,7 @@ export default class Auth0Manager { var hash = this.lock.parseHash() if (hash && hash.id_token) { // the user came back from the login (either SSO or regular login) // save the token - localStorage.setItem('userToken', hash.id_token) + window.localStorage.setItem('userToken', hash.id_token) // redirect to "targetUrl" if any let newLocation = hash.state || '' @@ -90,24 +90,24 @@ export default class Auth0Manager { reject(err) } // save token to localStorage - localStorage.setItem('userToken', token) + window.localStorage.setItem('userToken', token) // initialize auto log out check // this.setupSingleLogout() console.log(token, profile) - //this.userLoggedIn(token, profile) + // this.userLoggedIn(token, profile) resolve(constructUserObj(token, profile)) }) }) } logout () { - localStorage.removeItem('userToken') - var redirect = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port: '') + window.localStorage.removeItem('userToken') + var redirect = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '') window.location.replace('https://' + this.props.domain + '/v2/logout?returnTo=' + redirect) } - resetPassword() { + resetPassword () { this.lock.showReset((err) => { if (!err) this.lock.hide() }) @@ -116,7 +116,7 @@ export default class Auth0Manager { setupSingleLogout () { setInterval(() => { // if the token is not in local storage, there is nothing to check (i.e. the user is already logged out) - if (!localStorage.getItem('userToken')) return + if (!window.localStorage.getItem('userToken')) return this.auth0.getSSOData((err, data) => { // if there is still a session, do nothing @@ -124,7 +124,7 @@ export default class Auth0Manager { // if we get here, it means there is no session on Auth0, // then remove the token and redirect to #login - localStorage.removeItem('userToken') + window.localStorage.removeItem('userToken') window.location.href = '/' }) }, 5000) diff --git a/src/main/client/common/util/currencies.js b/src/main/client/common/util/currencies.js deleted file mode 100644 index 729366a56..000000000 --- a/src/main/client/common/util/currencies.js +++ /dev/null @@ -1,386 +0,0 @@ -export default -{ - "AED": { - "name": "Emirati Dirham", - "iso": { - "code": "AED", - "num": "784" - }, - "symbol": { - "default": { - "display": "Dhs", - "code": "Dhs" - }, - "native": { - "display": "د.Ø¥", - "code": "د.إ" - } - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Dirham", - "minor": "Fil" - } - }, - "format": "-###,###,###.## Dhs" - }, - "AFN": { - "name": "Afghan Afghani", - "iso": { - "code": "AFN", - "num": "971" - }, - "symbol": { - "default": { - "display": "Ø‹", - "code": "؋" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Afghani", - "minor": "Pul" - } - }, - "format": "-###,###,###.##" - }, - "ALL": { - "name": "Albanian Lek", - "iso": { - "code": "ALL", - "num": "008" - }, - "symbol": { - "default": { - "display": "L", - "code": "L" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Lek", - "minor": "Qindarka" - } - }, - "format": "L -###,###,###.##" - }, - "AMD": { - "name": "Armenian Dram", - "iso": { - "code": "AMD", - "num": "051" - }, - "symbol": { - "default": { - "display": "AMD", - "code": "֏" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Dram", - "minor": "Luma" - } - }, - "format": "AMD -###,###,###.##" - }, - "ANG": { - "name": "Netherlands Antillean Guilder", - "iso": { - "code": "ANG", - "num": "532" - }, - "symbol": { - "default": { - "display": "NAÆ’", - "code": "NAƒ" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Guilder", - "minor": "Cent" - } - }, - "format": "NAÆ’ -###,###,###.##" - }, - "AOA": { - "name": "Angolan Kwanza", - "iso": { - "code": "AOA", - "num": "973" - }, - "symbol": { - "default": { - "display": "Kz", - "code": "Kz" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Kwanza", - "minor": "Cêntimos" - } - }, - "format": "-###,###,###.## Kz" - }, - "ARS": { - "name": "Argentine Peso", - "iso": { - "code": "ARS", - "num": "032" - }, - "symbol": { - "default": { - "display": "AR$", - "code": "AR$" - }, - "native": { - "display": "$", - "code": "$" - } - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Peso", - "minor": "Centavo" - } - }, - "format": "AR$-###.###.###,##" - }, - "AUD": { - "name": "Australian Dollar", - "iso": { - "code": "AUD", - "num": "036" - }, - "symbol": { - "default": { - "display": "AU$", - "code": "AU$" - }, - "native": { - "display": "$", - "code": "$" - } - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Dollar", - "minor": "Cent" - } - }, - "format": "-AU$###,###,###.##" - }, - "AWG": { - "name": "Aruban Florin", - "iso": { - "code": "AWG", - "num": "533" - }, - "symbol": { - "default": { - "display": "Afl.", - "code": "Afl." - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Florin", - "minor": "Cent" - } - }, - "format": "Afl. -###,###,###.##" - }, - "AZN": { - "name": "Azerbaijani Manat", - "iso": { - "code": "BRL", - "num": "944" - }, - "symbol": { - "default": { - "display": "man.", - "code": "man." - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Manat", - "minor": "QÉ™pik" - } - }, - "format": "man. -$###.###.###,##" - }, - "CAD": { - "name": "Canadian Dollar", - "iso": { - "code": "CAD", - "num": "124" - }, - "symbol": { - "default": { - "display": "CA$", - "code": "CA$" - }, - "native": { - "display": "$", - "code": "$" - } - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Dollar", - "minor": "Cent" - } - }, - "format": "-CA$###,###,###.##" - }, - "DKK": { - "name": "Danish Krone", - "iso": { - "code": "DKK", - "num": "208" - }, - "symbol": { - "default": { - "display": "kr", - "code": "kr" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Krone", - "minor": "Øre" - } - }, - "format": "kr -###.###.###,##" - }, - "EUR": { - "name": "Euro", - "iso": { - "code": "EUR", - "num": "978" - }, - "symbol": { - "default": { - "display": "€", - "code": "€" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Euro", - "minor": "Cent" - } - }, - "format": "-€###.###.###,##" - }, - "GBP": { - "name": "Pound Sterling", - "iso": { - "code": "GBP", - "num": "826" - }, - "symbol": { - "default": { - "display": "£", - "code": "£" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Pound", - "minor": "Pence" - } - }, - "format": "-£###,###,###.##" - }, - "JPY": { - "name": "Japanese Yen", - "iso": { - "code": "JPY", - "num": "392" - }, - "symbol": { - "default": { - "display": "Â¥", - "code": "¥" - }, - "native": { - "display": "円", - "code": "円" - } - }, - "units": { - "decimal": 0, - "sub": 0, - "name": { - "major": "Yen", - "minor": null - } - }, - "format": "-###,###,###円" - }, - "USD": { - "name": "US Dollar", - "iso": { - "code": "USD", - "num": "840" - }, - "symbol": { - "default": { - "display": "$", - "code": "$" - }, - "native": null - }, - "units": { - "decimal": 2, - "sub": 100, - "name": { - "major": "Dollar", - "minor": "Cent" - } - }, - "format": "-$###,###,###.##" - } -} diff --git a/src/main/client/common/util/geo.js b/src/main/client/common/util/geo.js index 554a33b98..3cd3fbbaf 100644 --- a/src/main/client/common/util/geo.js +++ b/src/main/client/common/util/geo.js @@ -2,26 +2,23 @@ export const getFeedsBounds = (feeds) => { let feedsWithBounds = feeds.filter(feed => feed.latestValidation && feed.latestValidation.bounds) if (feedsWithBounds.length === 1) { return feedsWithBounds[0].latestValidation.bounds - } - else if (feedsWithBounds.length === 0) { + } else if (feedsWithBounds.length === 0) { return null - } - else { + } else { return feedsWithBounds.reduce((previousFeed, currentFeed) => { - if (previousFeed.latestValidation){ + if (previousFeed.latestValidation) { return { east: currentFeed.latestValidation.bounds.east > previousFeed.latestValidation.bounds.east ? currentFeed.latestValidation.bounds.east : previousFeed.latestValidation.bounds.east, west: currentFeed.latestValidation.bounds.west < previousFeed.latestValidation.bounds.west ? currentFeed.latestValidation.bounds.west : previousFeed.latestValidation.bounds.west, north: currentFeed.latestValidation.bounds.north > previousFeed.latestValidation.bounds.north ? currentFeed.latestValidation.bounds.north : previousFeed.latestValidation.bounds.north, south: currentFeed.latestValidation.bounds.south < previousFeed.latestValidation.bounds.south ? currentFeed.latestValidation.bounds.south : previousFeed.latestValidation.bounds.south } - } - else { + } else { return { east: currentFeed.latestValidation.bounds.east, west: currentFeed.latestValidation.bounds.west, north: currentFeed.latestValidation.bounds.north, - south: currentFeed.latestValidation.bounds.south, + south: currentFeed.latestValidation.bounds.south } } }) diff --git a/src/main/client/common/util/languages.js b/src/main/client/common/util/languages.js deleted file mode 100644 index c3b3e0bfc..000000000 --- a/src/main/client/common/util/languages.js +++ /dev/null @@ -1,738 +0,0 @@ -export default [ - { - 'code': 'ab', - 'name': 'Abkhaz' - }, - { - 'code': 'aa', - 'name': 'Afar' - }, - { - 'code': 'af', - 'name': 'Afrikaans' - }, - { - 'code': 'ak', - 'name': 'Akan' - }, - { - 'code': 'sq', - 'name': 'Albanian' - }, - { - 'code': 'am', - 'name': 'Amharic' - }, - { - 'code': 'ar', - 'name': 'Arabic' - }, - { - 'code': 'an', - 'name': 'Aragonese' - }, - { - 'code': 'hy', - 'name': 'Armenian' - }, - { - 'code': 'as', - 'name': 'Assamese' - }, - { - 'code': 'av', - 'name': 'Avaric' - }, - { - 'code': 'ae', - 'name': 'Avestan' - }, - { - 'code': 'ay', - 'name': 'Aymara' - }, - { - 'code': 'az', - 'name': 'Azerbaijani' - }, - { - 'code': 'bm', - 'name': 'Bambara' - }, - { - 'code': 'ba', - 'name': 'Bashkir' - }, - { - 'code': 'eu', - 'name': 'Basque' - }, - { - 'code': 'be', - 'name': 'Belarusian' - }, - { - 'code': 'bn', - 'name': 'Bengali; Bangla' - }, - { - 'code': 'bh', - 'name': 'Bihari' - }, - { - 'code': 'bi', - 'name': 'Bislama' - }, - { - 'code': 'bs', - 'name': 'Bosnian' - }, - { - 'code': 'br', - 'name': 'Breton' - }, - { - 'code': 'bg', - 'name': 'Bulgarian' - }, - { - 'code': 'my', - 'name': 'Burmese' - }, - { - 'code': 'ca', - 'name': 'Catalan; Valencian' - }, - { - 'code': 'ch', - 'name': 'Chamorro' - }, - { - 'code': 'ce', - 'name': 'Chechen' - }, - { - 'code': 'ny', - 'name': 'Chichewa; Chewa; Nyanja' - }, - { - 'code': 'zh', - 'name': 'Chinese' - }, - { - 'code': 'cv', - 'name': 'Chuvash' - }, - { - 'code': 'kw', - 'name': 'Cornish' - }, - { - 'code': 'co', - 'name': 'Corsican' - }, - { - 'code': 'cr', - 'name': 'Cree' - }, - { - 'code': 'hr', - 'name': 'Croatian' - }, - { - 'code': 'cs', - 'name': 'Czech' - }, - { - 'code': 'da', - 'name': 'Danish' - }, - { - 'code': 'dv', - 'name': 'Divehi; Dhivehi; Maldivian;' - }, - { - 'code': 'nl', - 'name': 'Dutch' - }, - { - 'code': 'dz', - 'name': 'Dzongkha' - }, - { - 'code': 'en', - 'name': 'English' - }, - { - 'code': 'eo', - 'name': 'Esperanto' - }, - { - 'code': 'et', - 'name': 'Estonian' - }, - { - 'code': 'ee', - 'name': 'Ewe' - }, - { - 'code': 'fo', - 'name': 'Faroese' - }, - { - 'code': 'fj', - 'name': 'Fijian' - }, - { - 'code': 'fi', - 'name': 'Finnish' - }, - { - 'code': 'fr', - 'name': 'French' - }, - { - 'code': 'ff', - 'name': 'Fula; Fulah; Pulaar; Pular' - }, - { - 'code': 'gl', - 'name': 'Galician' - }, - { - 'code': 'ka', - 'name': 'Georgian' - }, - { - 'code': 'de', - 'name': 'German' - }, - { - 'code': 'el', - 'name': 'Greek, Modern' - }, - { - 'code': 'gn', - 'name': 'Guaraní' - }, - { - 'code': 'gu', - 'name': 'Gujarati' - }, - { - 'code': 'ht', - 'name': 'Haitian; Haitian Creole' - }, - { - 'code': 'ha', - 'name': 'Hausa' - }, - { - 'code': 'he', - 'name': 'Hebrew (modern)' - }, - { - 'code': 'hz', - 'name': 'Herero' - }, - { - 'code': 'hi', - 'name': 'Hindi' - }, - { - 'code': 'ho', - 'name': 'Hiri Motu' - }, - { - 'code': 'hu', - 'name': 'Hungarian' - }, - { - 'code': 'ia', - 'name': 'Interlingua' - }, - { - 'code': 'id', - 'name': 'Indonesian' - }, - { - 'code': 'ie', - 'name': 'Interlingue' - }, - { - 'code': 'ga', - 'name': 'Irish' - }, - { - 'code': 'ig', - 'name': 'Igbo' - }, - { - 'code': 'ik', - 'name': 'Inupiaq' - }, - { - 'code': 'io', - 'name': 'Ido' - }, - { - 'code': 'is', - 'name': 'Icelandic' - }, - { - 'code': 'it', - 'name': 'Italian' - }, - { - 'code': 'iu', - 'name': 'Inuktitut' - }, - { - 'code': 'ja', - 'name': 'Japanese' - }, - { - 'code': 'jv', - 'name': 'Javanese' - }, - { - 'code': 'kl', - 'name': 'Kalaallisut, Greenlandic' - }, - { - 'code': 'kn', - 'name': 'Kannada' - }, - { - 'code': 'kr', - 'name': 'Kanuri' - }, - { - 'code': 'ks', - 'name': 'Kashmiri' - }, - { - 'code': 'kk', - 'name': 'Kazakh' - }, - { - 'code': 'km', - 'name': 'Khmer' - }, - { - 'code': 'ki', - 'name': 'Kikuyu, Gikuyu' - }, - { - 'code': 'rw', - 'name': 'Kinyarwanda' - }, - { - 'code': 'ky', - 'name': 'Kyrgyz' - }, - { - 'code': 'kv', - 'name': 'Komi' - }, - { - 'code': 'kg', - 'name': 'Kongo' - }, - { - 'code': 'ko', - 'name': 'Korean' - }, - { - 'code': 'ku', - 'name': 'Kurdish' - }, - { - 'code': 'kj', - 'name': 'Kwanyama, Kuanyama' - }, - { - 'code': 'la', - 'name': 'Latin' - }, - { - 'code': 'lb', - 'name': 'Luxembourgish, Letzeburgesch' - }, - { - 'code': 'lg', - 'name': 'Ganda' - }, - { - 'code': 'li', - 'name': 'Limburgish, Limburgan, Limburger' - }, - { - 'code': 'ln', - 'name': 'Lingala' - }, - { - 'code': 'lo', - 'name': 'Lao' - }, - { - 'code': 'lt', - 'name': 'Lithuanian' - }, - { - 'code': 'lu', - 'name': 'Luba-Katanga' - }, - { - 'code': 'lv', - 'name': 'Latvian' - }, - { - 'code': 'gv', - 'name': 'Manx' - }, - { - 'code': 'mk', - 'name': 'Macedonian' - }, - { - 'code': 'mg', - 'name': 'Malagasy' - }, - { - 'code': 'ms', - 'name': 'Malay' - }, - { - 'code': 'ml', - 'name': 'Malayalam' - }, - { - 'code': 'mt', - 'name': 'Maltese' - }, - { - 'code': 'mi', - 'name': 'MÄori' - }, - { - 'code': 'mr', - 'name': 'Marathi (MarÄá¹­hÄ«)' - }, - { - 'code': 'mh', - 'name': 'Marshallese' - }, - { - 'code': 'mn', - 'name': 'Mongolian' - }, - { - 'code': 'na', - 'name': 'Nauru' - }, - { - 'code': 'nv', - 'name': 'Navajo, Navaho' - }, - { - 'code': 'nb', - 'name': 'Norwegian BokmÃ¥l' - }, - { - 'code': 'nd', - 'name': 'North Ndebele' - }, - { - 'code': 'ne', - 'name': 'Nepali' - }, - { - 'code': 'ng', - 'name': 'Ndonga' - }, - { - 'code': 'nn', - 'name': 'Norwegian Nynorsk' - }, - { - 'code': 'no', - 'name': 'Norwegian' - }, - { - 'code': 'ii', - 'name': 'Nuosu' - }, - { - 'code': 'nr', - 'name': 'South Ndebele' - }, - { - 'code': 'oc', - 'name': 'Occitan' - }, - { - 'code': 'oj', - 'name': 'Ojibwe, Ojibwa' - }, - { - 'code': 'cu', - 'name': 'Old Church Slavonic, Church Slavic, Church Slavonic, Old Bulgarian, Old Slavonic' - }, - { - 'code': 'om', - 'name': 'Oromo' - }, - { - 'code': 'or', - 'name': 'Oriya' - }, - { - 'code': 'os', - 'name': 'Ossetian, Ossetic' - }, - { - 'code': 'pa', - 'name': 'Panjabi, Punjabi' - }, - { - 'code': 'pi', - 'name': 'PÄli' - }, - { - 'code': 'fa', - 'name': 'Persian (Farsi)' - }, - { - 'code': 'pl', - 'name': 'Polish' - }, - { - 'code': 'ps', - 'name': 'Pashto, Pushto' - }, - { - 'code': 'pt', - 'name': 'Portuguese' - }, - { - 'code': 'qu', - 'name': 'Quechua' - }, - { - 'code': 'rm', - 'name': 'Romansh' - }, - { - 'code': 'rn', - 'name': 'Kirundi' - }, - { - 'code': 'ro', - 'name': 'Romanian' - }, - { - 'code': 'ru', - 'name': 'Russian' - }, - { - 'code': 'sa', - 'name': 'Sanskrit (Saá¹ská¹›ta)' - }, - { - 'code': 'sc', - 'name': 'Sardinian' - }, - { - 'code': 'sd', - 'name': 'Sindhi' - }, - { - 'code': 'se', - 'name': 'Northern Sami' - }, - { - 'code': 'sm', - 'name': 'Samoan' - }, - { - 'code': 'sg', - 'name': 'Sango' - }, - { - 'code': 'sr', - 'name': 'Serbian' - }, - { - 'code': 'gd', - 'name': 'Scottish Gaelic; Gaelic' - }, - { - 'code': 'sn', - 'name': 'Shona' - }, - { - 'code': 'si', - 'name': 'Sinhala, Sinhalese' - }, - { - 'code': 'sk', - 'name': 'Slovak' - }, - { - 'code': 'sl', - 'name': 'Slovene' - }, - { - 'code': 'so', - 'name': 'Somali' - }, - { - 'code': 'st', - 'name': 'Southern Sotho' - }, - { - 'code': 'es', - 'name': 'Spanish; Castilian' - }, - { - 'code': 'su', - 'name': 'Sundanese' - }, - { - 'code': 'sw', - 'name': 'Swahili' - }, - { - 'code': 'ss', - 'name': 'Swati' - }, - { - 'code': 'sv', - 'name': 'Swedish' - }, - { - 'code': 'ta', - 'name': 'Tamil' - }, - { - 'code': 'te', - 'name': 'Telugu' - }, - { - 'code': 'tg', - 'name': 'Tajik' - }, - { - 'code': 'th', - 'name': 'Thai' - }, - { - 'code': 'ti', - 'name': 'Tigrinya' - }, - { - 'code': 'bo', - 'name': 'Tibetan Standard, Tibetan, Central' - }, - { - 'code': 'tk', - 'name': 'Turkmen' - }, - { - 'code': 'tl', - 'name': 'Tagalog' - }, - { - 'code': 'tn', - 'name': 'Tswana' - }, - { - 'code': 'to', - 'name': 'Tonga (Tonga Islands)' - }, - { - 'code': 'tr', - 'name': 'Turkish' - }, - { - 'code': 'ts', - 'name': 'Tsonga' - }, - { - 'code': 'tt', - 'name': 'Tatar' - }, - { - 'code': 'tw', - 'name': 'Twi' - }, - { - 'code': 'ty', - 'name': 'Tahitian' - }, - { - 'code': 'ug', - 'name': 'Uyghur, Uighur' - }, - { - 'code': 'uk', - 'name': 'Ukrainian' - }, - { - 'code': 'ur', - 'name': 'Urdu' - }, - { - 'code': 'uz', - 'name': 'Uzbek' - }, - { - 'code': 've', - 'name': 'Venda' - }, - { - 'code': 'vi', - 'name': 'Vietnamese' - }, - { - 'code': 'vo', - 'name': 'Volapük' - }, - { - 'code': 'wa', - 'name': 'Walloon' - }, - { - 'code': 'cy', - 'name': 'Welsh' - }, - { - 'code': 'wo', - 'name': 'Wolof' - }, - { - 'code': 'fy', - 'name': 'Western Frisian' - }, - { - 'code': 'xh', - 'name': 'Xhosa' - }, - { - 'code': 'yi', - 'name': 'Yiddish' - }, - { - 'code': 'yo', - 'name': 'Yoruba' - }, - { - 'code': 'za', - 'name': 'Zhuang, Chuang' - }, - { - 'code': 'zu', - 'name': 'Zulu' - } -] diff --git a/src/main/client/common/util/util.js b/src/main/client/common/util/util.js index 0783661fd..5a4da7132 100644 --- a/src/main/client/common/util/util.js +++ b/src/main/client/common/util/util.js @@ -1,5 +1,5 @@ import fetch from 'isomorphic-fetch' -import { setErrorMessage } from '../../manager/actions/status' +// import { setErrorMessage } from '../../manager/actions/status' import gravatar from 'gravatar' export function defaultSorter (a, b) { @@ -62,20 +62,20 @@ export function secureFetch (url, state, method, payload, raw) { } export function generateUID () { - return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4) + return ('0000' + (Math.random() * Math.pow(36, 4) << 0).toString(36)).slice(-4) } export function generateRandomInt (min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min + return Math.floor(Math.random() * (max - min + 1)) + min } export function generateRandomColor () { - var letters = '0123456789ABCDEF'.split('') - var color = '' - for (var i = 0; i < 6; i++) { - color += letters[Math.floor(Math.random() * 16)] - } - return color + var letters = '0123456789ABCDEF'.split('') + var color = '' + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)] + } + return color } // export function invertHex (hexnum) { // if (hexnum.length != 6) { @@ -116,17 +116,16 @@ export function idealTextColor (bgColor) { return ((255 - bgDelta) < nThreshold) ? '000000' : 'FFFFFF' } -function getRGBComponents(color) { - - var r = color.substring(1, 3) - var g = color.substring(3, 5) - var b = color.substring(5, 7) +function getRGBComponents (color) { + var r = color.substring(1, 3) + var g = color.substring(3, 5) + var b = color.substring(5, 7) - return { - R: parseInt(r, 16), - G: parseInt(g, 16), - B: parseInt(b, 16) - } + return { + R: parseInt(r, 16), + G: parseInt(g, 16), + B: parseInt(b, 16) + } } // export const UserIsAuthenticated = UserAuthWrapper({ // authSelector: state => state.user, diff --git a/src/main/client/editor/actions/active.js b/src/main/client/editor/actions/active.js new file mode 100644 index 000000000..56d4bae1c --- /dev/null +++ b/src/main/client/editor/actions/active.js @@ -0,0 +1,195 @@ +import clone from 'clone' +import { browserHistory } from 'react-router' + +import { secureFetch } from '../../common/util/util' +import { saveFeedInfo } from './feedInfo' +import { saveAgency } from './agency' +import { saveStop } from './stop' +import { saveRoute } from './route' +import { saveFare } from './fare' +import { fetchTripPatternsForRoute, saveTripPattern } from './tripPattern' +// import { fetchTripsForCalendar } from './trip' +import { saveCalendar, saveScheduleException } from './calendar' +import { getGtfsTable, createGtfsEntity } from './editor' +import { componentList, subComponentList, subSubComponentList } from '../util/gtfs' +import { getStopsForPattern, getTimetableColumns } from '../util' + +export function updateEditSetting (setting, value, activePattern) { + return { + type: 'UPDATE_EDIT_SETTING', + setting, + value, + activePattern + } +} + +export function settingActiveGtfsEntity (feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId, activeEntity, activeSubEntity, activeColumns) { + return { + type: 'SETTING_ACTIVE_GTFS_ENTITY', + feedSourceId, + component, + entityId, + subComponent, + subEntityId, + subSubComponent, + subSubEntityId, + activeEntity, + activeSubEntity, + activeColumns + } +} + +export function setActiveGtfsEntity (feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId) { + return function (dispatch, getState) { + // TODO: figure out a good way to handle route changes without confirm window + // if (getState().editor.data.active.edited && !window.confirm('You have unsaved changes. Discard changes?')) { + // return false + // } + let previousFeedSourceId = getState().editor.feedSourceId + if (previousFeedSourceId && feedSourceId !== previousFeedSourceId) { + dispatch(clearGtfsContent()) + } + // stop editing geometry if currently editing + if (getState().editor.editSettings.editGeometry) { + dispatch(updateEditSetting('editGeometry', false)) + } + const pathItems = ['feed', feedSourceId, 'edit', component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId].filter(item => item) + let url = '/' + pathItems.join('/') + // console.log(url) + // ensure component is valid + if (component && componentList.indexOf(component) === -1) { + url = `/feed/${feedSourceId}/edit/` + } else if (subComponent && subComponentList.indexOf(subComponent) === -1) { + // ensure subComponent is valid + url = `/feed/${feedSourceId}/edit/${component}/${entityId}/` + } else if (subSubComponent && subSubComponentList.indexOf(subSubComponent) === -1) { + // ensure subSubComponent is valid + url = `/feed/${feedSourceId}/edit/${component}/${entityId}/${subComponent}/${subEntityId}/` + } + if (entityId === 'new' && (!getState().editor.data.tables[component] || getState().editor.data.tables[component].findIndex(e => e.id === 'new') === -1)) { + dispatch(createGtfsEntity(feedSourceId, component)) + } + if (!getState().routing.locationBeforeTransitions || !getState().routing.locationBeforeTransitions.pathname || getState().routing.locationBeforeTransitions.pathname !== url) { + browserHistory.push(url) + } + const activeEntity = component === 'feedinfo' + ? clone(getState().editor.data.tables[component]) + : getState().editor.data.tables[component] && entityId + ? clone(getState().editor.data.tables[component].find(e => e.id === entityId)) + : null + const activeSubEntity = activeEntity && activeEntity.tripPatterns + ? clone(activeEntity.tripPatterns.find(p => p.id === subEntityId)) + : null + const activePatternStops = getStopsForPattern(activeSubEntity, getState().editor.data.tables.stop) + const activeColumns = getTimetableColumns(activeSubEntity, activePatternStops) + dispatch(settingActiveGtfsEntity(feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId, activeEntity, activeSubEntity, activeColumns)) + } +} + +export function savingActiveGtfsEntity (component, entity) { + return { + type: 'SAVING_ACTIVE_GTFS_ENTITY', + component, + entity + } +} + +export function saveActiveGtfsEntity (component, optionalEntity) { + return function (dispatch, getState) { + let entity, feedId + switch (component) { + case 'stop': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveStop(feedId, entity)) + case 'route': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveRoute(feedId, entity)) + case 'agency': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveAgency(feedId, entity)) + case 'trippattern': + // let route = getState().editor.data.active.entity + // let patternId = getState().editor.data.active.subEntityId + entity = optionalEntity || getState().editor.data.active.subEntity // route.tripPatterns.find(p => p.id === patternId) + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveTripPattern(feedId, entity)) + case 'calendar': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveCalendar(feedId, entity)) + case 'scheduleexception': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveScheduleException(feedId, entity)) + case 'fare': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.feedId || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveFare(feedId, entity)) + case 'feedinfo': + entity = optionalEntity || getState().editor.data.active.entity + feedId = entity.id || getState().editor.data.active.feedSourceId + dispatch(savingActiveGtfsEntity(component, entity)) + return dispatch(saveFeedInfo(feedId, entity)) + default: + console.log('no action specified!') + return + } + } +} +export function deletingEntity (feedId, component, entityId) { + return { + type: 'DELETING_ENTITY', + feedId, + component, + entityId + } +} + +export function deleteGtfsEntity (feedId, component, entityId, routeId) { + return function (dispatch, getState) { + dispatch(deletingEntity(feedId, component, entityId)) + if (entityId === 'new') { + return dispatch(getGtfsTable(component, feedId)) + } + const url = `/api/manager/secure/${component}/${entityId}?feedId=${feedId}` + return secureFetch(url, getState(), 'delete') + .then(res => res.json()) + .then(entity => { + if (component === 'trippattern' && routeId) { + dispatch(fetchTripPatternsForRoute(feedId, routeId)) + } else { + dispatch(getGtfsTable(component, feedId)) + } + }) + } +} + +export function updateActiveGtfsEntity (entity, component, props) { + return function (dispatch, getState) { + dispatch(entity, component, props, getState().editor.data.active) + } +} + +export function resetActiveGtfsEntity (entity, component) { + return { + type: 'RESET_ACTIVE_GTFS_ENTITY', + entity, + component + } +} + +export function clearGtfsContent () { + return { + type: 'CLEAR_GTFSEDITOR_CONTENT' + } +} diff --git a/src/main/client/editor/actions/agency.js b/src/main/client/editor/actions/agency.js index 64a4c45f7..d97fa945f 100644 --- a/src/main/client/editor/actions/agency.js +++ b/src/main/client/editor/actions/agency.js @@ -1,7 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' - -//// AGENCY +import { setActiveGtfsEntity } from './active' export function savingAgency (feedId, agency) { return { @@ -35,7 +33,7 @@ export function saveAgency (feedId, agency) { timezone: agency.agency_timezone, url: agency.agency_url, agencyFareUrl: agency.agency_fare_url, - agencyBrandingUrl: agency.agency_branding_url, + agencyBrandingUrl: agency.agency_branding_url } const method = agency.id !== 'new' ? 'put' : 'post' const url = agency.id !== 'new' diff --git a/src/main/client/editor/actions/calendar.js b/src/main/client/editor/actions/calendar.js index 69e631efc..4736cd8e7 100644 --- a/src/main/client/editor/actions/calendar.js +++ b/src/main/client/editor/actions/calendar.js @@ -1,7 +1,7 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' +import { setActiveGtfsEntity } from './active' -//// CALENDAR + SCHEDULE_EXCEPTION +// CALENDAR + SCHEDULE_EXCEPTION export function requestingCalendars (feedId) { return { @@ -62,7 +62,7 @@ export function saveCalendar (feedId, calendar) { sunday: calendar.sunday === 1, startDate: calendar.start_date, endDate: calendar.end_date, - id: calendar.id === 'new' ? null : calendar.id, + id: calendar.id === 'new' ? null : calendar.id } return secureFetch(url, getState(), method, data) .then(res => res.json()) diff --git a/src/main/client/editor/actions/editor.js b/src/main/client/editor/actions/editor.js index 008c9db4f..0edddafa1 100644 --- a/src/main/client/editor/actions/editor.js +++ b/src/main/client/editor/actions/editor.js @@ -1,247 +1,13 @@ -import JSZip from 'jszip' -import shp from 'shpjs' +import fetch from 'isomorphic-fetch' import { secureFetch, generateUID, generateRandomInt, generateRandomColor, idealTextColor } from '../../common/util/util' -import { componentList, subComponentList, subSubComponentList } from '../util/gtfs' -import { fetchFeedVersions } from '../../manager/actions/feeds' -import { getConfigProperty } from '../../common/util/config' -import { browserHistory } from 'react-router' -import { - fetchFeedInfo, - saveFeedInfo, - deleteFeedInfo, - updateFeedInfo, - createFeedInfo -} from '../actions/feedInfo' -import { - fetchAgencies, - fetchAgency, - saveAgency, - deleteAgency, - updateAgency -} from '../actions/agency' -import { - fetchStops, - fetchStop, - saveStop, - deleteStop, - updateStop, - fetchStopsForTripPattern -} from '../actions/stop' -import { - fetchRoutes, - fetchRoute, - saveRoute, - deleteRoute, - updateRoute -} from '../actions/route' -import { - fetchFares, - fetchFare, - saveFare, - deleteFare, - updateFare -} from '../actions/fare' -import { - fetchTripPatternsForRoute, - fetchTripPattern, - saveTripPattern, - deleteTripPattern, - updateTripPattern -} from '../actions/tripPattern' -import { - fetchTripsForCalendar, - fetchTrip, - saveTrip, - deleteTrip, - updateTrip -} from '../actions/trip' -import { - fetchCalendars, - fetchCalendar, - saveCalendar, - deleteCalendar, - updateCalendar, - fetchScheduleExceptions, - fetchScheduleException, - saveScheduleException, - deleteScheduleException, - updateScheduleException -} from '../actions/calendar' - -export function updateEditSetting (setting, value) { - return { - type: 'UPDATE_EDIT_SETTING', - setting, - value - } -} - -export function updateMapSetting (props) { - return { - type: 'UPDATE_MAP_SETTING', - props - } -} - -//// SINGLE ENTITY ACTIONS - -export function settingActiveGtfsEntity (feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId) { - return { - type: 'SETTING_ACTIVE_GTFS_ENTITY', - feedSourceId, - component, - entityId, - subComponent, - subEntityId, - subSubComponent, - subSubEntityId - } -} - -export function setActiveGtfsEntity (feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId) { - return function (dispatch, getState) { - // TODO: figure out a good way to handle route changes without confirm window - // if (getState().editor.active.edited && !window.confirm('You have unsaved changes. Discard changes?')) { - // return false - // } - let previousFeedSourceId = getState().editor.feedSourceId - if (previousFeedSourceId && feedSourceId !== previousFeedSourceId) { - dispatch(clearGtfsContent()) - } - if (getState().editor.editGeometry) { - dispatch(toggleEditGeometry()) - } - const pathItems = ['feed', feedSourceId, 'edit', component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId].filter(item => item) - let url = '/' + pathItems.join('/') - // console.log(url) - // ensure component is valid - if (component && componentList.indexOf(component) === -1) { - url = `/feed/${feedSourceId}/edit/` - } - // ensure subComponent is valid - else if (subComponent && subComponentList.indexOf(subComponent) === -1) { - url = `/feed/${feedSourceId}/edit/${component}/${entityId}/` - } - // ensure subSubComponent is valid - else if (subSubComponent && subSubComponentList.indexOf(subSubComponent) === -1) { - url = `/feed/${feedSourceId}/edit/${component}/${entityId}/${subComponent}/${subEntityId}/` - } - if (entityId === 'new' && (!getState().editor.tableData[component] || getState().editor.tableData[component].findIndex(e => e.id === 'new') === -1)) { - dispatch(createGtfsEntity(feedSourceId, component)) - } - if (!getState().routing.locationBeforeTransitions || !getState().routing.locationBeforeTransitions.pathname || getState().routing.locationBeforeTransitions.pathname !== url) { - browserHistory.push(url) - } - dispatch(settingActiveGtfsEntity(feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId)) - } -} - -export function savingActiveGtfsEntity (component, entity) { - return { - type: 'SAVING_ACTIVE_GTFS_ENTITY', - component, - entity - } -} - -export function saveActiveGtfsEntity (component, optionalEntity) { - return function (dispatch, getState) { - let entity, feedId - switch (component) { - case 'stop': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveStop(feedId, entity)) - case 'route': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveRoute(feedId, entity)) - case 'agency': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveAgency(feedId, entity)) - case 'trippattern': - // let route = getState().editor.active.entity - // let patternId = getState().editor.active.subEntityId - entity = optionalEntity || getState().editor.active.subEntity // route.tripPatterns.find(p => p.id === patternId) - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveTripPattern(feedId, entity)) - case 'calendar': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveCalendar(feedId, entity)) - case 'scheduleexception': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveScheduleException(feedId, entity)) - case 'fare': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.feedId || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveFare(feedId, entity)) - case 'feedinfo': - entity = optionalEntity || getState().editor.active.entity - feedId = entity.id || getState().editor.active.feedSourceId - dispatch(savingActiveGtfsEntity(component, entity)) - return dispatch(saveFeedInfo(feedId, entity)) - default: - console.log('no action specified!') - return - } - } -} -export function deletingEntity (feedId, component, entityId) { - return { - type: 'DELETING_ENTITY', - feedId, - component, - entityId - } -} - -export function deleteGtfsEntity (feedId, component, entityId, routeId) { - return function (dispatch, getState) { - dispatch(deletingEntity(feedId, component, entityId)) - if (entityId === 'new') { - return dispatch(getGtfsTable(component, feedId)) - } - const url = `/api/manager/secure/${component}/${entityId}?feedId=${feedId}` - return secureFetch(url, getState(), 'delete') - .then(res => res.json()) - .then(entity => { - if (component === 'trippattern' && routeId) { - dispatch(fetchTripPatternsForRoute(feedId, routeId)) - } - else { - dispatch(getGtfsTable(component, feedId)) - } - }) - } -} - -export function updateActiveGtfsEntity (entity, component, props) { - return { - type: 'UPDATE_ACTIVE_GTFS_ENTITY', - entity, - component, - props - } -} - -export function resetActiveGtfsEntity (entity, component) { - return { - type: 'RESET_ACTIVE_GTFS_ENTITY', - entity, - component - } -} +import { fetchFeedInfo } from './feedInfo' +import { fetchAgencies } from './agency' +import { fetchStops } from './stop' +import { fetchRoutes } from './route' +import { fetchFares } from './fare' +import { fetchCalendars, fetchScheduleExceptions } from './calendar' +import { saveActiveGtfsEntity, setActiveGtfsEntity } from './active' export function createGtfsEntity (feedSourceId, component, props) { return { @@ -260,11 +26,11 @@ export function cloneGtfsEntity (feedSourceId, component, entityId, save) { let props switch (component) { case 'trippattern': - props = {...getState().editor.active.entity.tripPatterns.find(tp => tp.id === entityId)} + props = {...getState().editor.data.active.entity.tripPatterns.find(tp => tp.id === entityId)} props.name = props.name + ' copy' break default: - props = {...getState().editor.tableData[component].find(e => e.id === entityId)} + props = {...getState().editor.data.tables[component].find(e => e.id === entityId)} break } props.id = 'new' @@ -286,7 +52,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { return function (dispatch, getState) { if (!props) { const generateProps = (component) => { - let agency = getState().editor.tableData.agency ? getState().editor.tableData.agency[0] : null + let agency = getState().editor.data.tables.agency ? getState().editor.data.tables.agency[0] : null let color = generateRandomColor() switch (component) { case 'route': @@ -297,7 +63,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { route_long_name: null, route_color: color, route_text_color: idealTextColor(color), - route_type: getState().editor.tableData.feedinfo && getState().editor.tableData.feedinfo.defaultRouteType !== null ? getState().editor.tableData.feedinfo.defaultRouteType : 3, + route_type: getState().editor.data.tables.feedinfo && getState().editor.data.tables.feedinfo.defaultRouteType !== null ? getState().editor.data.tables.feedinfo.defaultRouteType : 3 } case 'stop': let stopId = generateUID() @@ -305,10 +71,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { stop_id: stopId, stop_name: null, stop_lat: 0, - stop_lon: 0, - // route_color: color, - // route_text_color: idealTextColor(color), - // route_type: getState().editor.tableData.feedinfo && getState().editor.tableData.feedinfo.routeTypeId !== null ? getState().editor.tableData.feedinfo.routeTypeId : 3, + stop_lon: 0 } case 'scheduleexception': return { @@ -329,8 +92,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { if (props && 'routeId' in props) { // console.log('setting after create') dispatch(setActiveGtfsEntity(feedSourceId, 'route', props.routeId, component, 'new')) - } - else { + } else { console.log('setting after create', feedSourceId, component, 'new') dispatch(setActiveGtfsEntity(feedSourceId, component, 'new')) } @@ -338,7 +100,7 @@ export function newGtfsEntity (feedSourceId, component, props, save) { } } -/////// GENERIC TABLE ACTIONS + OLD GTFS+ ACTIONS PORTED OVER +// GENERIC TABLE ACTIONS export function receiveGtfsTable (tableId, entities) { return { @@ -381,9 +143,8 @@ export function getGtfsTable (tableId, feedId) { export function uploadBrandingAsset (feedId, entityId, component, file) { return function (dispatch, getState) { if (!file) return null - var data = new FormData() + var data = new window.FormData() data.append('file', file) - console.log(data) const url = `/api/manager/secure/${component}/${entityId}/uploadbranding?feedId=${feedId}` return fetch(url, { method: 'post', @@ -406,315 +167,9 @@ export function fetchBaseGtfs (feedId) { } } - - - - -// EDIT ACTIVE GTFS+ ACTIONS - -export function addGtfsRow (tableId) { - const table = getConfigProperty('modules.editor.spec').find(t => t.id === tableId) - - let rowData = {} - for(const field of table.fields) { - const editorField = field.name.split(/_(.+)?/)[1] - rowData[editorField] = null - } - - return { - type: 'ADD_GTFSEDITOR_ROW', - tableId, - rowData - } -} - -export function updateGtfsField (tableId, rowIndex, fieldName, newValue) { - return { - type: 'UPDATE_GTFSEDITOR_FIELD', - tableId, - rowIndex, - fieldName, - newValue - } -} - -export function deleteGtfsRow (tableId, rowIndex) { - return { - type: 'DELETE_GTFSEDITOR_ROW', - tableId, - rowIndex - } -} - - -// DOWNLOAD/RECEIVE DATA ACTIONS - -export function requestingGtfsContent () { - return { - type: 'REQUESTING_GTFSEDITOR_CONTENT', - } -} - -export function clearGtfsContent () { - return { - type: 'CLEAR_GTFSEDITOR_CONTENT', - } -} - -export function receiveGtfsContent (feedVersionId, filenames, fileContent, timestamp) { - return { - type: 'RECEIVE_GTFSEDITOR_CONTENT', - feedVersionId, - filenames, - fileContent, - timestamp - } -} - -export function downloadGtfsFeed (feedVersionId) { - return function (dispatch, getState) { - dispatch(requestingGtfsContent()) - - const fetchFeed = fetch('/api/manager/secure/gtfsplus/'+ feedVersionId, { - method: 'get', - cache: 'default', - headers: { 'Authorization': 'Bearer ' + getState().user.token } - }).then((response) => { - if (response.status !== 200) { - console.log('error downloading gtfs+ feed', response.statusCode) - dispatch(clearGtfsContent()) - } - return response.blob() - }) - - const fetchTimestamp = secureFetch(`/api/manager/secure/gtfsplus/${feedVersionId}/timestamp`, getState()) - .then(response => response.json()) - - Promise.all([fetchFeed, fetchTimestamp]).then(([feed, timestamp]) => { - JSZip.loadAsync(feed).then((zip) => { - let filenames = [] - let filePromises = [] - zip.forEach((path,file) => { - filenames.push(path) - filePromises.push(file.async('string')) - }) - Promise.all(filePromises).then(fileContent => { - dispatch(receiveGtfsContent(feedVersionId, filenames, fileContent, timestamp)) - dispatch(validateGtfsFeed(feedVersionId)) - }) - }) - }) - } -} - -// VALIDATION ACTIONS - -export function validatingGtfsFeed () { - return { - type: 'VALIDATING_GTFSEDITOR_FEED', - } -} - -export function receiveGtfsValidation (validationIssues) { - return { - type: 'RECEIVE_GTFSEDITOR_VALIDATION', - validationIssues - } -} - -export function validateGtfsFeed (feedVersionId) { - return function (dispatch, getState) { - dispatch(validatingGtfsFeed()) - const url = `/api/manager/secure/gtfsplus/${feedVersionId}/validation` - return secureFetch(url, getState()) - .then(res => res.json()) - .then(validationIssues => { - //console.log('got GTFS+ val result', validationResult) - dispatch(receiveGtfsValidation(validationIssues)) - }) - } -} - -// UPLOAD ACTIONS - -export function uploadingGtfsFeed () { - return { - type: 'UPLOADING_GTFSEDITOR_FEED', - } -} - -export function uploadedGtfsFeed () { - return { - type: 'UPLOADED_GTFSEDITOR_FEED', - } -} - -export function uploadGtfsFeed (feedVersionId, file) { - return function (dispatch, getState) { - dispatch(uploadingGtfsFeed()) - const url = `/api/manager/secure/gtfsplus/${feedVersionId}` - var data = new FormData() - data.append('file', file) - - return fetch(url, { - method: 'post', - headers: { 'Authorization': 'Bearer ' + getState().user.token }, - body: data - }).then(result => { - return dispatch(uploadedGtfsFeed()) - }) - } -} - -// GTFS ENTITY LOOKUP ACTIONS - export function receiveGtfsEntities (gtfsEntities) { return { type: 'RECEIVE_GTFS_ENTITIES', gtfsEntities } } - -export function loadGtfsEntities (tableId, rows, feedSource) { - - return function (dispatch, getState) { - - // lookup table for mapping tableId:fieldName keys to inputType values - const typeLookup = {} - const getDataType = function(tableId, fieldName) { - const lookupKey = tableId + ':' + fieldName - if (lookupKey in typeLookup) return typeLookup[lookupKey] - const fieldInfo = getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).fields.find(f => f.name === fieldName) - if (!fieldInfo) return null - typeLookup[lookupKey] = fieldInfo.inputType - return fieldInfo.inputType - } - - // determine which routes, stops, etc. aren't currently in the gtfsEntityLookup table and need to be loaded from the API - const routesToLoad = [] - const stopsToLoad = [] - - const currentLookup = getState().editor.gtfsEntityLookup - - for(const rowData of rows) { - for(const fieldName in rowData) { - switch(getDataType(tableId, fieldName)) { - case 'GTFS_ROUTE': - const routeId = rowData[fieldName] - if (routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) - break; - case 'GTFS_STOP': - const stopId = rowData[fieldName] - if (stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) - break; - } - } - } - - if (routesToLoad.length === 0 && stopsToLoad.length === 0) return - - var loadRoutes = Promise.all(routesToLoad.map(routeId => { - const url = `/api/manager/routes/${routeId}?feed=${feedSource.externalProperties.MTC.AgencyId}` - return fetch(url) - .then((response) => { - return response.json() - }) - })) - - var loadStops = Promise.all(stopsToLoad.map(stopId => { - const url = `/api/manager/stops/${stopId}?feed=${feedSource.externalProperties.MTC.AgencyId}` - return fetch(url) - .then((response) => { - return response.json() - }) - })) - - Promise.all([loadRoutes, loadStops]).then(results => { - const loadedRoutes = results[0] - const loadedStops = results[1] - dispatch(receiveGtfsEntities(loadedRoutes.concat(loadedStops))) - }) - } -} - -// MAP ACTIONS - -function receivedRoutesShapefile (feedSource, geojson) { - return { - type: 'RECEIVED_ROUTES_SHAPEFILE', - feedSource, - geojson - } -} - -export function displayRoutesShapefile (feedSource, file) { - return function (dispatch, getState) { - // JSZip.loadAsync(file).then((zip) => { - // console.log(zip) - // shp(zip).then(geojson => { - // console.log(geojson) - // }) - // }) - let reader = new FileReader() - reader.onload = e => { - let arrayBuff = reader.result - shp(arrayBuff).then(geojson => { - console.log(geojson) - geojson.key = Math.random() - dispatch(receivedRoutesShapefile(feedSource, geojson)) - }) - } - reader.readAsArrayBuffer(file) - } -} - - -// PUBLISH ACTIONS - -export function publishingGtfsFeed () { - return { - type: 'PUBLISHING_GTFSEDITOR_FEED', - } -} - -export function publishGtfsFeed (feedVersion) { - return function (dispatch, getState) { - dispatch(publishingGtfsFeed()) - const url = `/api/manager/secure/gtfsplus/${feedVersion.id}/publish` - return secureFetch(url, getState(), 'post') - .then((res) => { - console.log('published done'); - return dispatch(fetchFeedVersions(feedVersion.feedSource)) - }) - } -} - -// SNAPSHOT ACTIONS - -export function requestingSnapshots () { - return { - type: 'REQUESTING_GTFSEDITOR_SNAPSHOTS' - } -} - -export function receiveSnapshots (feedSource, snapshots) { - return { - type: 'RECEIVE_GTFSEDITOR_SNAPSHOTS', - feedSource, - snapshots - } -} - -export function fetchSnapshots (feedSource) { - return function (dispatch, getState) { - dispatch(requestingSnapshots()) - const url = `/api/manager/secure/snapshot?feedSourceId=${feedSource.id}` - return secureFetch(url, getState(), 'get') - .then((response) => { - return response.json() - }).then((snapshots) => { - dispatch(receiveSnapshots(feedSource, snapshots)) - }) - } -} diff --git a/src/main/client/editor/actions/fare.js b/src/main/client/editor/actions/fare.js index 9137bbbd6..3488d0e2e 100644 --- a/src/main/client/editor/actions/fare.js +++ b/src/main/client/editor/actions/fare.js @@ -1,7 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' - -//// FARES +import { setActiveGtfsEntity } from './active' export function savingFare (feedId, fare) { return { @@ -34,7 +32,7 @@ export function saveFare (feedId, fare) { currencyType: fare.currency_type, paymentMethod: fare.payment_method, transfers: fare.transfers, - transferDuration: fare.transfer_duration, + transferDuration: fare.transfer_duration } const method = fare.id !== 'new' ? 'put' : 'post' const url = fare.id !== 'new' @@ -43,7 +41,6 @@ export function saveFare (feedId, fare) { return secureFetch(url, getState(), method, data) .then(res => res.json()) .then(f => { - // dispatch(receiveFare(feedId, fare)) dispatch(fetchFares(feedId)) .then(() => { diff --git a/src/main/client/editor/actions/feedInfo.js b/src/main/client/editor/actions/feedInfo.js index b2ecd5d99..d26df270b 100644 --- a/src/main/client/editor/actions/feedInfo.js +++ b/src/main/client/editor/actions/feedInfo.js @@ -1,7 +1,4 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' - -//// FEED_INFO export function requestingFeedInfo (feedId) { return { @@ -42,15 +39,13 @@ export function fetchFeedInfo (feedId) { } } -////// Create new feed info - - +// Create new feed info export function createFeedInfo (feedId) { return function (dispatch, getState) { dispatch(savingFeedInfo(feedId)) const data = { // datatools props - id: feedId, + id: feedId // color: feedInfo.color, // defaultLat: feedInfo.defaultLat, // defaultLon: feedInfo.defaultLon, @@ -89,10 +84,10 @@ export function saveFeedInfo (feedId, feedInfo) { feedLang: feedInfo.feed_lang, feedPublisherName: feedInfo.feed_publisher_name, feedPublisherUrl: feedInfo.feed_publisher_url, - feedVersion: feedInfo.feed_version, + feedVersion: feedInfo.feed_version } const url = `/api/manager/secure/feedinfo/${feedId}` - const method = getState().editor.tableData.feedinfo ? 'put' : 'post' + const method = getState().editor.data.tables.feedinfo ? 'put' : 'post' return secureFetch(url, getState(), method, data) .then((res) => { return dispatch(fetchFeedInfo(feedId)) diff --git a/src/main/client/editor/actions/map/index.js b/src/main/client/editor/actions/map/index.js new file mode 100644 index 000000000..581f368dd --- /dev/null +++ b/src/main/client/editor/actions/map/index.js @@ -0,0 +1,256 @@ +import shp from 'shpjs' +import pointOnLine from 'turf-point-on-line' +import lineSlice from 'turf-line-slice' +import point from 'turf-point' +import lineDistance from 'turf-line-distance' +import lineString from 'turf-linestring' +import ll from 'lonlng' + +import { updateActiveEntity, saveActiveEntity } from '../active' +import {polyline as getPolyline, getSegment} from '../../../scenario-editor/utils/valhalla' + +export function updateMapSetting (props) { + return { + type: 'UPDATE_MAP_SETTING', + props + } +} + +export function addControlPoint (controlPoint, index) { + return { + type: 'ADD_CONTROL_POINT', + controlPoint, + index + } +} + +export function removingControlPoint (pattern, index, begin, end) { + return { + type: 'REMOVE_CONTROL_POINT', + pattern, + index, + begin, + end + } +} + +export function constructControlPoint (pattern, latlng, controlPoints) { + return function (dispatch, getState) { + // slice line + let beginPoint = point(pattern.shape.coordinates[0]) + let clickPoint = point(ll.toCoordinates(latlng)) + let lineSegment = lineSlice(beginPoint, clickPoint, pattern.shape) + + // measure line segment + let distTraveled = lineDistance(lineSegment, 'meters') + let controlPoint = {distance: distTraveled, point: clickPoint} + + // find splice index based on shape dist traveled + let index = 0 + for (var i = 0; i < controlPoints.length; i++) { + if (distTraveled > controlPoints[i].distance) { + index = i + 1 + } else { + break + } + } + // add control point + dispatch(addControlPoint(controlPoint, index)) + } +} + +export function removeControlPoint (pattern, index, begin, end, polyline) { + return async function (dispatch, getState) { + let coordinates = await dispatch(handlePatternEdit(null, begin, end, polyline, pattern)) + // update pattern + dispatch(updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: coordinates}})) + // remove controlPoint + dispatch(removingControlPoint(index)) + } +} + +export function updateControlPoint (index, point, distance) { + return { + type: 'UPDATE_CONTROL_POINT', + index, + point, + distance + } +} + +function receivedRoutesShapefile (feedSource, geojson) { + return { + type: 'RECEIVED_ROUTES_SHAPEFILE', + feedSource, + geojson + } +} + +export function displayRoutesShapefile (feedSource, file) { + return function (dispatch, getState) { + let reader = new window.FileReader() + reader.onload = e => { + let arrayBuff = reader.result + shp(arrayBuff).then(geojson => { + console.log(geojson) + geojson.key = Math.random() + dispatch(receivedRoutesShapefile(feedSource, geojson)) + }) + } + reader.readAsArrayBuffer(file) + } +} + +export function handleControlPointDragEnd (e, timer, controlPoint, controlPointIndex, polyline, pattern) { + return function (dispatch, getState) { + let geojson = polyline.leafletElement.toGeoJSON() + // this.refs[this.props.pattern.id] + + // snap control point to line + let controlPointLocation = e.target.toGeoJSON() + let snapPoint = pointOnLine(geojson, controlPointLocation) + controlPoint.leafletElement.setLatLng(ll.toLatlng(snapPoint.geometry.coordinates)) + // this.refs[controlPointRef] + + dispatch(updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: geojson.geometry.coordinates}})) + + if (typeof controlPointIndex !== 'undefined') { + let lineSegment = lineSlice(point(geojson.geometry.coordinates[0]), snapPoint, geojson) + + // measure line segment + let distTraveled = lineDistance(lineSegment, 'meters') + dispatch(updateControlPoint(controlPointIndex, snapPoint, distTraveled)) + // var stateUpdate = { controlPoints: {[controlPointIndex]: {point: { $set : snapPoint }, distance: {$set: distTraveled} }}} + // this.setState(update(this.state, stateUpdate)) + } + // clear timer + if (timer) clearInterval(timer) + } +} + +export async function extendPatternToPoint (pattern, endPoint, newEndPoint) { + return async function (dispatch, getState) { + let newShape = await getPolyline([endPoint, newEndPoint]) + + // get single coordinate if polyline fails + if (!newShape) { + newShape = ll.toCoordinates(newEndPoint) + } + const updatedShape = {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]} + dispatch(updateActiveEntity(pattern, 'trippattern', {shape: updatedShape})) + await dispatch(saveActiveEntity('trippattern')) + return updatedShape + } +} + +export async function handlePatternEdit (controlPoint, begin, end, polyline, pattern) { + return async function (dispatch, getState) { + let followRoad = getState().editor.editSettings.followStreets // !e.originalEvent.shiftKey + let leafletPattern = polyline.leafletElement + // this.refs[this.props.pattern.id] + let originalLatLngs + let originalEndPoint + let from, to + let markerLatLng + + if (controlPoint !== null) { + markerLatLng = controlPoint.leafletElement.getLatLng() + } + + // set from, to for endPoint if we have markerLatLng + if (begin && markerLatLng) { + from = begin + to = [markerLatLng.lng, markerLatLng.lat] + } else if (begin) { // set just from (when controlPoint is removed) + from = begin + } else if (end) { // set from for beginPoint + from = [markerLatLng.lng, markerLatLng.lat] + // TODO: this.state.newLatLngs should no longer exist anywhere, but this is just commented out for now + // } else if (this.state.newLatLngs) { // if pattern has been previously edited, use that endpoint + // originalLatLngs = this.state.newLatLngs + // originalEndPoint = originalLatLngs[originalLatLngs.length - 1] + // from = [originalEndPoint.lng, originalEndPoint.lat] + } else { // otherwise use the original endpoint + originalLatLngs = pattern.shape.coordinates.map(c => ([c[1], c[0]])) + originalEndPoint = originalLatLngs[originalLatLngs.length - 1] + from = [originalEndPoint[1], originalEndPoint[0]] // [latLngs[latLngs.length - 1].lng, latLngs[latLngs.length - 1].lat] + to = [markerLatLng.lng, markerLatLng.lat] + } + + let latLngs = leafletPattern.toGeoJSON().geometry.coordinates + + if (from) { + let points = [ + from.geometry ? from.geometry.coordinates : from + ] + if (to) { + points.push(to) + } + if (end) { + points.push(end.geometry.coordinates) + } + let newCoordinates + let newSegment = await getSegment(points, followRoad) + let originalSegment = lineString(latLngs) + + // slice line if middle control point + if (end && begin) { + let beginPoint = point(latLngs[0]) + let beginSlice = lineSlice(beginPoint, from, originalSegment) + let endPoint = point(latLngs[latLngs.length - 1]) + let endSlice = lineSlice(end, endPoint, originalSegment) + newCoordinates = [ + ...beginSlice.geometry.coordinates, + ...newSegment.coordinates, + ...endSlice.geometry.coordinates + ] + } else if (end) { // handle begin control point + let endPoint = point(latLngs[latLngs.length - 1]) + let endSlice = lineSlice(end, endPoint, originalSegment) + newCoordinates = [ + ...newSegment.coordinates, + ...endSlice.geometry.coordinates + ] + } else { // append latlngs if end control point + let beginPoint = point(latLngs[0]) + let beginSlice = lineSlice(beginPoint, from, originalSegment) + newCoordinates = [ + ...beginSlice.geometry.coordinates, + ...newSegment.coordinates + ] + } + let leafletCoords = newCoordinates.map(coord => ll.fromCoordinates(coord)) + leafletPattern.setLatLngs(leafletCoords) + // // add last coordinate as "stop" + // let endPoint = newPath[newPath.length - 1] + // // this.setState({patternStops: }) + + return leafletCoords // newCoordinates + } + } +} + +function removingStopFromPattern (pattern, stop, index, controlPoints, polyline) { + return { + type: 'REMOVING_STOP_FROM_PATTERN', + pattern, + stop, + index, + controlPoints, + polyline + } +} + +export async function removeStopFromPattern (pattern, stop, index, controlPoints, polyline) { + return async function (dispatch, getState) { + dispatch(removingStopFromPattern(pattern, stop, index, controlPoints, polyline)) + const cpIndex = controlPoints.findIndex(cp => cp.stopId === stop.id) + let begin = controlPoints[cpIndex - 2] ? controlPoints[cpIndex - 1].point : null + let end = controlPoints[cpIndex + 2] ? controlPoints[cpIndex + 1].point : null + let coordinates = await dispatch(handlePatternEdit(null, begin, end, polyline, pattern)) + let patternStops = [...pattern.patternStops] + patternStops.splice(index, 1) + dispatch(updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}})) + dispatch(saveActiveEntity('trippattern')) + } +} diff --git a/src/main/client/editor/actions/map/stopStrategies.js b/src/main/client/editor/actions/map/stopStrategies.js new file mode 100644 index 000000000..3b59c50e7 --- /dev/null +++ b/src/main/client/editor/actions/map/stopStrategies.js @@ -0,0 +1,131 @@ +import along from 'turf-along' +import ll from 'lonlng' +import lineDistance from 'turf-line-distance' + +import { constructStop, stopToStopTime } from '../../util/map' +import { stopToGtfs } from '../../util/gtfs' +import { extendPatternToPoint } from '../map' +import { updateActiveEntity, saveActiveEntity } from '../active' +// import { newGtfsEntities } from '../editor' +import {getSegment} from '../../../scenario-editor/utils/valhalla' + +export async function addStopAtPoint (latlng, addToPattern = false, index, activePattern) { + return async function (dispatch, getState) { + // create stop + const stop = await constructStop(latlng, activePattern.feedId) + const s = await this.props.newGtfsEntity(activePattern.feedId, 'stop', stop, true) + const gtfsStop = stopToGtfs(s) + // add stop to end of pattern + if (addToPattern) { + await dispatch(addStopToPattern(activePattern, gtfsStop, index)) + } + return gtfsStop + } +} + +export async function addStopAtIntersection (latlng, activePattern) { + return async function (dispatch, getState) { + console.log('adding stop at intersection!') + // TODO: implement intersection strategy + // 1. extend pattern to click point + // 2. get intersections from OSRM + // 3. add stops at intersections (using afterIntersection, intersectionStep, distanceFromIntersection) + } +} + +export async function addStopAtInterval (latlng, activePattern) { + return async function (dispatch, getState) { + // create first stop if none exist + if (activePattern.patternStops.length === 0) { + dispatch(addStopAtPoint(latlng, true, activePattern)) + } else { + let coordinates = activePattern.shape && activePattern.shape.coordinates + let patternStops = [...activePattern.patternStops] + let initialDistance = lineDistance(activePattern.shape, 'meters') + + // extend pattern to click point + let endPoint + if (coordinates) { + endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) + } else { + endPoint = {lng: patternStops[0].stop_lon, lat: patternStops[0].stop_lat} + } + const updatedShape = await dispatch(extendPatternToPoint(activePattern, endPoint, latlng)) + let totalDistance = lineDistance(activePattern.shape, 'meters') + let distanceAdded = totalDistance - initialDistance + let numIntervals = distanceAdded / getState().editor.editSettings.stopInterval + const latlngList = [] + for (var i = 1; i < numIntervals; i++) { + let stopDistance = initialDistance + i * getState().editor.editSettings.stopInterval + + // add stops along shape at interval (stopInterval) + let position = along(updatedShape, stopDistance, 'meters') + let stopLatlng = ll.toLatlng(position.geometry.coordinates) + latlngList.push(stopLatlng) + + // pass patternStops.length as index to ensure pattern not extended to locaton + const newStop = await dispatch(addStopAtPoint(stopLatlng, false, patternStops.length, activePattern)) + // add new stop to array + patternStops.push(stopToStopTime(newStop)) + } + // TODO: switch to adding multiple stops per action (Java controller and action promise need updating) + // const newStops = await this.addStopsAtPoints(latlngList) + // // add new stop to array + // patternStops = [...patternStops, ...newStops.map(s => stopToStopTime(s))] + + // update and save all new stops to pattern + updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + saveActiveEntity('trippattern') + } + } +} + +export async function addStopToPattern (pattern, stop, index) { + return async function (dispatch, getState) { + let patternStops = [...pattern.patternStops] + let coordinates = pattern.shape && pattern.shape.coordinates + let newStop = stopToStopTime(stop) + // if adding stop to end, also a proxy for extending pattern to point + if (typeof index === 'undefined' || index === null) { + // if shape coordinates already exist, just extend them + if (coordinates) { + let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) + patternStops.push(newStop) + updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) + // saveActiveEntity('trippattern') + extendPatternToPoint(pattern, endPoint, {lng: stop.stop_lon, lat: stop.stop_lat}) + } else { // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) + patternStops.push(newStop) + if (patternStops.length > 1) { + let previousStop = getState().editor.data.tables.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) + console.log(previousStop) + let geojson = await getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], getState().editor.editSettings.followStreets) + updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}}) + saveActiveEntity('trippattern') + } else { + updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) + saveActiveEntity('trippattern') + } + } + // if not following roads + // updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) + } else { // if adding stop in middle + patternStops.splice(index, 0, newStop) + updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) + saveActiveEntity('trippattern') + } + // TODO: add strategy for stop at beginning + } +} + +// async function addStopsAtPoints (latlngList) { +// return async function (dispatch, getState) { +// const stops = [] +// for (var i = 0; i < latlngList.length; i++) { +// const stop = await constructStop(latlngList[i], this.props.feedSource.id) +// stops.push(stop) +// } +// const newStops = await dispatch(newGtfsEntities(this.props.feedSource.id, 'stop', stops, true)) +// return newStops.map(s => stopToGtfs(s)) +// } +// } diff --git a/src/main/client/editor/actions/route.js b/src/main/client/editor/actions/route.js index 013aec77a..7e3f2cb95 100644 --- a/src/main/client/editor/actions/route.js +++ b/src/main/client/editor/actions/route.js @@ -1,7 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' - -//// ROUTES +import { updateEditSetting, setActiveGtfsEntity } from './active' export function savingRoute (feedId, route) { return { @@ -40,7 +38,7 @@ export function saveRoute (feedId, route) { routeUrl: route.route_url, routeColor: route.route_color, routeTextColor: route.route_text_color, - id: route.id === 'new' ? null : route.id, + id: route.id === 'new' ? null : route.id } return secureFetch(url, getState(), method, data) .then(res => res.json()) @@ -102,6 +100,13 @@ export function fetchRoutes (feedId) { .then(res => res.json()) .then(routes => { dispatch(receiveRoutes(feedId, routes)) + // update followStreets value + // TODO: update value when setting active entity + if (getState().editor.data.active.entity) { + let routeIndex = getState().editor.data.active.entity && routes.findIndex(r => r.id === getState().editor.data.active.entity) + let followStreets = routeIndex !== -1 ? routes[routeIndex].route_type === 3 || routes[routeIndex].route_type === 0 : true + dispatch(updateEditSetting('followStreets', followStreets)) + } return routes }) } diff --git a/src/main/client/editor/actions/snapshots.js b/src/main/client/editor/actions/snapshots.js index f67340234..7c04619e4 100644 --- a/src/main/client/editor/actions/snapshots.js +++ b/src/main/client/editor/actions/snapshots.js @@ -1,5 +1,6 @@ import { secureFetch } from '../../common/util/util' -import { clearGtfsContent, fetchBaseGtfs } from './editor' +import { fetchBaseGtfs } from './editor' +import { clearGtfsContent } from './active' import { startJobMonitor } from '../../manager/actions/status' // SNAPSHOT ACTIONS diff --git a/src/main/client/editor/actions/stop.js b/src/main/client/editor/actions/stop.js index 014025f9c..e75c544be 100644 --- a/src/main/client/editor/actions/stop.js +++ b/src/main/client/editor/actions/stop.js @@ -1,5 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { setActiveGtfsEntity } from './editor' +import { setActiveGtfsEntity } from './active' import { isNew, stopFromGtfs } from '../util/gtfs' // STOPS diff --git a/src/main/client/editor/actions/trip.js b/src/main/client/editor/actions/trip.js index 82f52a6e7..330b7776a 100644 --- a/src/main/client/editor/actions/trip.js +++ b/src/main/client/editor/actions/trip.js @@ -1,8 +1,6 @@ import { secureFetch } from '../../common/util/util' import { setErrorMessage } from '../../manager/actions/status' -//// TRIP - export function requestingTripsForCalendar (feedId, pattern, calendarId) { return { type: 'REQUESTING_TRIPS_FOR_CALENDAR', @@ -30,11 +28,11 @@ export function fetchTripsForCalendar (feedId, pattern, calendarId) { .then(res => res.json()) .then(trips => { dispatch(receiveTripsForCalendar(feedId, pattern, calendarId, trips)) + return trips }) } } - export function savingTrips (feedId, pattern, calendarId, trips) { return { type: 'SAVING_TRIPS', @@ -88,37 +86,36 @@ export function saveTripsForCalendar (feedId, pattern, calendarId, trips) { } } -export function saveTripsForCalendar2 (feedId, pattern, calendarId, trips) { - return function (dispatch, getState) { - dispatch(savingTrips(feedId, pattern, calendarId, trips)) - const tripExists = trip.id !== 'new' && trip.id !== null - const method = tripExists ? 'put' : 'post' - const url = tripExists - ? `/api/manager/secure/trip/${trip.id}?feedId=${feedId}` - : `/api/manager/secure/trip?feedId=${feedId}` - trip.id = tripExists ? trip.id : null - return secureFetch(url, getState(), method, trip) - .then(res => { - if (res.status >= 300) { - errorCount++ - errorIndexes.push(index) - return null - } else { - return res.json() - } - }) - .then(trips => { - // console.log(trips) - if (errorCount) { - dispatch(setErrorMessage(`Unknown error encountered while saving trips. Could not save ${errorCount} trips`)) - } - dispatch(fetchTripsForCalendar(feedId, pattern, calendarId)) - return errorIndexes - }) - // return result - } -} - +// export function saveTripsForCalendar2 (feedId, pattern, calendarId, trips) { +// return function (dispatch, getState) { +// dispatch(savingTrips(feedId, pattern, calendarId, trips)) +// const tripExists = trip.id !== 'new' && trip.id !== null +// const method = tripExists ? 'put' : 'post' +// const url = tripExists +// ? `/api/manager/secure/trip/${trip.id}?feedId=${feedId}` +// : `/api/manager/secure/trip?feedId=${feedId}` +// trip.id = tripExists ? trip.id : null +// return secureFetch(url, getState(), method, trip) +// .then(res => { +// if (res.status >= 300) { +// errorCount++ +// errorIndexes.push(index) +// return null +// } else { +// return res.json() +// } +// }) +// .then(trips => { +// // console.log(trips) +// if (errorCount) { +// dispatch(setErrorMessage(`Unknown error encountered while saving trips. Could not save ${errorCount} trips`)) +// } +// dispatch(fetchTripsForCalendar(feedId, pattern, calendarId)) +// return errorIndexes +// }) +// // return result +// } +// } // export function saveTrip (feedId, trip) { // // return function (dispatch, getState) { @@ -180,7 +177,6 @@ export function deleteTripsForCalendar (feedId, pattern, calendarId, trips) { } } - export function updateCellValue (value, rowIndex, key) { return { type: 'UPDATE_TIMETABLE_CELL_VALUE', diff --git a/src/main/client/editor/actions/tripPattern.js b/src/main/client/editor/actions/tripPattern.js index 08be6ca58..6f24e880e 100644 --- a/src/main/client/editor/actions/tripPattern.js +++ b/src/main/client/editor/actions/tripPattern.js @@ -1,6 +1,7 @@ import { secureFetch } from '../../common/util/util' import { setErrorMessage } from '../../manager/actions/status' -import { setActiveGtfsEntity } from './editor' +import { setActiveGtfsEntity } from './active' +import { getStopsForPattern, getTimetableColumns } from '../util' // TRIP PATTERNS @@ -65,30 +66,6 @@ export function undoActiveTripPatternEdits () { } } -export function addControlPoint (controlPoint, index) { - return { - type: 'ADD_CONTROL_POINT', - controlPoint, - index - } -} - -export function removeControlPoint (index) { - return { - type: 'REMOVE_CONTROL_POINT', - index - } -} - -export function updateControlPoint (index, point, distance) { - return { - type: 'UPDATE_CONTROL_POINT', - index, - point, - distance - } -} - // TODO: merge the following with the above? export function requestingTripPatternsForRoute (feedId, routeId) { @@ -99,12 +76,14 @@ export function requestingTripPatternsForRoute (feedId, routeId) { } } -export function receiveTripPatternsForRoute (feedId, routeId, tripPatterns) { +export function receiveTripPatternsForRoute (feedId, routeId, tripPatterns, activePattern, activePatternStops) { return { type: 'RECEIVE_TRIP_PATTERNS_FOR_ROUTE', feedId, routeId, - tripPatterns + tripPatterns, + activePattern, + activePatternStops } } @@ -124,7 +103,10 @@ export function fetchTripPatternsForRoute (feedId, routeId) { return res.json() }) .then(tripPatterns => { - dispatch(receiveTripPatternsForRoute(feedId, routeId, tripPatterns)) + const activePattern = getState().editor.data.active.subEntityId && tripPatterns.find(p => p.id === getState().editor.data.active.subEntityId) + const activePatternStops = getStopsForPattern(activePattern, getState().editor.data.tables.stop) + const activeColumns = getTimetableColumns(activePattern, activePatternStops) + dispatch(receiveTripPatternsForRoute(feedId, routeId, tripPatterns, activePattern, activeColumns)) return tripPatterns }) } diff --git a/src/main/client/editor/components/CreateSnapshotModal.js b/src/main/client/editor/components/CreateSnapshotModal.js index 3e788a482..c70a0f99c 100644 --- a/src/main/client/editor/components/CreateSnapshotModal.js +++ b/src/main/client/editor/components/CreateSnapshotModal.js @@ -1,9 +1,11 @@ import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' -import { Modal, Button, Glyphicon, FormGroup, FormControl, ControlLabel } from 'react-bootstrap' +import { Modal, Button, FormGroup, FormControl, ControlLabel } from 'react-bootstrap' export default class CreateSnapshotModal extends Component { - + static propTypes = { + onOkClicked: PropTypes.func + } constructor (props) { super(props) this.state = { diff --git a/src/main/client/editor/components/EditorMap.js b/src/main/client/editor/components/EditorMap.js deleted file mode 100644 index cecb4b505..000000000 --- a/src/main/client/editor/components/EditorMap.js +++ /dev/null @@ -1,1007 +0,0 @@ -import React, { Component, PropTypes } from 'react' -import { Map, Marker, Popup, Polyline, TileLayer, FeatureGroup, ZoomControl, LayersControl, GeoJson } from 'react-leaflet' -import { divIcon, Browser } from 'leaflet' -import { MenuItem, SplitButton } from 'react-bootstrap' -import { shallowEqual } from 'react-pure-render' -import {Icon} from '@conveyal/woonerf' -import ll from 'lonlng' -import lineString from 'turf-linestring' -import bearing from 'turf-bearing' -import point from 'turf-point' -import along from 'turf-along' -import lineDistance from 'turf-line-distance' -import lineSlice from 'turf-line-slice' -import pointOnLine from 'turf-point-on-line' - -import { generateUID } from '../../common/util/util' -import { stopToGtfs } from '../util/gtfs' -import { getUserMetadataProperty } from '../../common/util/user' -import { getConfigProperty } from '../../common/util/config' -import PatternStopPopup from './PatternStopPopup' -// import StopMarkersLayer from './StopMarkersLayer' -import StopLayer from '../../scenario-editor/components/StopLayer' -import {polyline as getPolyline, getSegment} from '../../scenario-editor/utils/valhalla' -import { reverseEsri as reverse } from '../../scenario-editor/utils/reverse' - -export default class EditorMap extends Component { - static propTypes = { - subEntityId: PropTypes.string, - activeEntityId: PropTypes.string, - activeComponent: PropTypes.string, - subComponent: PropTypes.string, - currentPattern: PropTypes.object, - - stops: PropTypes.array, - stopTree: PropTypes.object, - - editSettings: PropTypes.object, - mapState: PropTypes.object, - feedSource: PropTypes.object, - feedInfo: PropTypes.object, - zoomToTarget: PropTypes.string, - - entities: PropTypes.array, - activeEntity: PropTypes.object, - entityEdited: PropTypes.bool, - offset: PropTypes.number, - tripPatterns: PropTypes.array, - - updateActiveEntity: PropTypes.func, - saveActiveEntity: PropTypes.func, - setActiveEntity: PropTypes.func, - updateControlPoint: PropTypes.func, - newGtfsEntity: PropTypes.func, - fetchTripPatterns: PropTypes.func, - - updateMapSetting: PropTypes.func, - addControlPoint: PropTypes.func, - removeControlPoint: PropTypes.func, - - updateUserMetadata: PropTypes.func, - user: PropTypes.object, - - sidebarExpanded: PropTypes.bool, - hidden: PropTypes.bool, - drawStops: PropTypes.bool // whether to draw stops or not (based on zoom level) - } - constructor (props) { - super(props) - this.state = { - controlPoints: this.props.editSettings.controlPoints && this.props.editSettings.controlPoints.length - ? this.props.editSettings.controlPoints[this.props.editSettings.controlPoints.length - 1] - : [] - } - } - _onResize = () => { - this.setState({width: window.innerWidth, height: window.innerHeight}) - this.refs.map && setTimeout(() => this.refs.map.leafletElement.invalidateSize(), 500) - } - componentWillMount () { - this._onResize() - this.setState({willMount: true}) - } - componentDidMount () { - window.addEventListener('resize', this._onResize) - this.setState({willMount: false}) - } - componentWillUnmount () { - window.removeEventListener('resize', this._onResize) - } - componentWillReceiveProps (nextProps) { - if (nextProps.offset !== this.props.offset || nextProps.hidden !== this.props.hidden) { - this._onResize() - } - if (nextProps.editSettings.controlPoints && nextProps.editSettings.controlPoints.length && !shallowEqual(nextProps.editSettings.controlPoints, this.props.editSettings.controlPoints)) { - this.setState({controlPoints: nextProps.editSettings.controlPoints[nextProps.editSettings.controlPoints.length - 1]}) - } - if (nextProps.zoomToTarget && !shallowEqual(nextProps.zoomToTarget, this.props.zoomToTarget)) { - // this._onResize() - this.setState({zoomToTarget: true}) - } - } - shouldComponentUpdate (nextProps, nextState) { - // return true - const shouldUpdate = - !shallowEqual(nextState, this.state) || - this.props.hidden !== nextProps.hidden || - !shallowEqual(nextProps.mapState.routesGeojson, this.props.mapState.routesGeojson) || - (nextProps.activeComponent === 'stop' || nextProps.activeComponent === 'route') && nextProps.activeEntityId !== this.props.activeEntityId || - nextProps.activeEntity && this.props.activeEntity && nextProps.activeEntity.tripPatterns && !this.props.activeEntity.tripPatterns || - !shallowEqual(nextProps.feedSource, this.props.feedSource) || - nextProps.activeComponent !== this.props.activeComponent || - !this.props.activeEntity && nextProps.activeEntity || - // TODO: add bounds to shouldComponentUpdate and move mapZoom/bounds to mapState reducer - nextProps.drawStops && !shallowEqual(nextProps.mapState.bounds, this.props.mapState.bounds) || - !shallowEqual(nextProps.drawStops, this.props.drawStops) || - !shallowEqual(nextProps.tripPatterns, this.props.tripPatterns) || - !shallowEqual(nextProps.zoomToTarget, this.props.zoomToTarget) || - !shallowEqual(nextProps.editSettings, this.props.editSettings) || - !shallowEqual(nextProps.subEntityId, this.props.subEntityId) || - !shallowEqual(nextProps.currentPattern, this.props.activePattern) || - // nextProps.activeComponent === 'stop' && this.props.mapState.zoom !== nextProps.mapState.zoom || - nextProps.activeComponent === 'stop' && this.props.activeEntity && nextProps.activeEntity && (this.props.activeEntity.stop_lon !== nextProps.activeEntity.stop_lon || this.props.activeEntity.stop_lat !== nextProps.activeEntity.stop_lat) || - nextProps.activeEntityId !== this.props.activeEntityId || - nextProps.sidebarExpanded !== this.props.sidebarExpanded - - return shouldUpdate - } - zoomToEntity (entity) { - if (entity && entity.id) { - this.refs.map.leafletElement.panTo([entity.stop_lat, entity.stop_lon]) - } - } - getStopLatLng (latlng) { - const precision = 100000000 // eight decimal places is accurate up to 1.1 meters - return {stop_lat: Math.round(latlng.lat * precision) / precision, stop_lon: Math.round(latlng.lng % 180 * precision) / precision} - } - async extendPatternToPoint (pattern, endPoint, newEndPoint) { - let newShape = await getPolyline([endPoint, newEndPoint]) - - // get single coordinate if polyline fails - if (!newShape) { - newShape = ll.toCoordinates(newEndPoint) - } - const updatedShape = {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]} - this.props.updateActiveEntity(pattern, 'trippattern', {shape: updatedShape}) - await this.props.saveActiveEntity('trippattern') - return updatedShape - } - stopToStopTime (stop) { - return {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} - } - async addStopToPattern (pattern, stop, index) { - let patternStops = [...pattern.patternStops] - - let coordinates = pattern.shape && pattern.shape.coordinates - let newStop = this.stopToStopTime(stop) - // if adding stop to end, also a proxy for extending pattern to point - if (typeof index === 'undefined' || index === null) { - // if shape coordinates already exist, just extend them - if (coordinates) { - let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) - patternStops.push(newStop) - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) - // this.props.saveActiveEntity('trippattern') - this.extendPatternToPoint(pattern, endPoint, {lng: stop.stop_lon, lat: stop.stop_lat}) - .then(() => { - // patternStops.push(newStop) - // this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) - // this.props.saveActiveEntity('trippattern') - }) - } else { // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) - patternStops.push(newStop) - if (patternStops.length > 1) { - let previousStop = this.props.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) - console.log(previousStop) - let geojson = await getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], this.props.editSettings.followStreets) - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}}) - this.props.saveActiveEntity('trippattern') - } else { - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - } - } - - // if not following roads - // this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) - } else { // if adding stop in middle - patternStops.splice(index, 0, newStop) - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - } - // TODO: add strategy for stop at beginning - } - async removeStopFromPattern (pattern, stop, index) { - let patternGeojson = this.refs[pattern.id].leafletElement.toGeoJSON() - console.log(patternGeojson.geometry.coordinates.length) - - let controlPointIndex = this.state.controlPoints.findIndex(cp => cp.stopId === stop.id) - console.log('stop is controlPoint #' + controlPointIndex) - let begin = this.state.controlPoints[controlPointIndex - 2] ? this.state.controlPoints[controlPointIndex - 1].point : null - let end = this.state.controlPoints[controlPointIndex + 2] ? this.state.controlPoints[controlPointIndex + 1].point : null - - let coordinates = await this.handlePatternEdit(null, begin, end) - - // // update pattern - // let patternGeojson = this.refs[pattern.id].leafletElement.toGeoJSON() - // this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: coordinates}}) - - let patternStops = [...pattern.patternStops] - patternStops.splice(index, 1) - this.props.updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) - this.props.saveActiveEntity('trippattern') - } - handleControlPointDragEnd (e, timer, controlPointRef, controlPointIndex) { - let patternGeojson = this.refs[this.props.activePattern.id].leafletElement.toGeoJSON() - - // snap control point to line - let controlPointLocation = e.target.toGeoJSON() - let snapPoint = pointOnLine(patternGeojson, controlPointLocation) - this.refs[controlPointRef].leafletElement.setLatLng(ll.toLatlng(snapPoint.geometry.coordinates)) - - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {shape: {type: 'LineString', coordinates: patternGeojson.geometry.coordinates}}) - - if (typeof controlPointIndex !== 'undefined') { - let lineSegment = lineSlice(point(patternGeojson.geometry.coordinates[0]), snapPoint, patternGeojson) - - // measure line segment - let distTraveled = lineDistance(lineSegment, 'meters') - this.props.updateControlPoint(controlPointIndex, snapPoint, distTraveled) - // var stateUpdate = { controlPoints: {[controlPointIndex]: {point: { $set : snapPoint }, distance: {$set: distTraveled} }}} - // this.setState(update(this.state, stateUpdate)) - } - // clear timer - if (timer) clearInterval(timer) - } - async constructStop (latlng) { - let stopLatLng = this.getStopLatLng(latlng) - let result = await reverse(latlng) - let stopId = generateUID() - let stopName = `New Stop (${stopId})` - if (result && result.address) { - stopName = result.address.Address - } - return { - stop_id: stopId, - stop_name: stopName, - feedId: this.props.feedSource.id, - ...stopLatLng - } - } - async mapRightClicked (e) { - if (this.props.activeComponent === 'stop') { - // if newly created stop is selected - let stopLatLng = this.getStopLatLng(e.latlng) - if (this.props.activeEntity && this.props.activeEntity.id === 'new') { - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, stopLatLng) - this.refs[this.props.activeEntity.id].leafletElement.setLatLng(e.latlng) - } else if (this.props.entities && this.props.entities.findIndex(e => e.id === 'new') === -1) { - const stop = await this.constructStop(e.latlng) - this.props.newGtfsEntity(this.props.feedSource.id, this.props.activeComponent, stop) - } - } - } - mapBaseLayerChanged (e, layers) { - const layer = layers.find(l => l.name === e.name) - console.log('base layer changed', e) - this.props.updateUserMetadata(this.props.user.profile, {editor: {map_id: layer.id}}) - } - addControlPoint (pattern, latlng) { - // console.log('adding control point at ' + latlng) - // slice line - let beginPoint = point(pattern.shape.coordinates[0]) - let clickPoint = point(ll.toCoordinates(latlng)) - let lineSegment = lineSlice(beginPoint, clickPoint, pattern.shape) - - // measure line segment - let distTraveled = lineDistance(lineSegment, 'meters') - let controlPoint = {distance: distTraveled, point: clickPoint} - - // find splice index based on shape dist traveled - let index = 0 - for (var i = 0; i < this.state.controlPoints.length; i++) { - if (distTraveled > this.state.controlPoints[i].distance) { - index = i + 1 - } else { - break - } - } - // add control point - this.props.addControlPoint(controlPoint, index) - } - async removeControlPoint (pattern, index, begin, end) { - let coordinates = await this.handlePatternEdit(null, begin, end) - - // update pattern - this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: coordinates}}) - - // remove controlPoint - this.props.removeControlPoint(index) - } - async addStopsAtPoints (latlngList) { - const stops = [] - for (var i = 0; i < latlngList.length; i++) { - const stop = await this.constructStop(latlngList[i]) - stops.push(stop) - } - const newStops = await this.props.newGtfsEntities(this.props.feedSource.id, 'stop', stops, true) - return newStops.map(s => stopToGtfs(s)) - } - async addStopAtPoint (latlng, addToPattern = false, index) { - // create stop - const stop = await this.constructStop(latlng) - const s = await this.props.newGtfsEntity(this.props.feedSource.id, 'stop', stop, true) - const gtfsStop = stopToGtfs(s) - // add stop to end of pattern - if (addToPattern) { - await this.addStopToPattern(this.props.activePattern, gtfsStop, index) - } - return gtfsStop - } - async mapClicked (e) { - // TODO: replace with spatial tree - if (this.props.activeComponent === 'stop') { - // find stop based on latlng - let selectedStop = this.props.entities.find(stop => Math.round(stop.stop_lat * 1000) / 1000 === Math.round(e.latlng.lat * 1000) / 1000 && Math.round(stop.stop_lon * 1000) / 1000 === Math.round(e.latlng.lng * 1000) / 1000) - console.log('map click selected -->', selectedStop) - if (selectedStop) { - if (this.props.activeEntity && this.props.activeEntity.id === selectedStop.id) { - // do nothing, the user just clicked the current stop - } else { - this.props.setActiveEntity(this.props.feedSource.id, this.props.activeComponent, selectedStop) - } - } - } - - if (this.props.subComponent === 'trippattern' && this.props.editSettings.editGeometry) { - switch (this.props.editSettings.onMapClick) { - case 'NO_ACTION': - break - case 'ADD_STOP_AT_CLICK': - this.addStopAtPoint(e.latlng, true) - break - case 'ADD_STOPS_AT_INTERSECTIONS': - // TODO: implement intersection strategy - - // extend pattern to click point - - // get intersections from OSRM - - // add stops at intersections (using afterIntersection, intersectionStep, distanceFromIntersection) - - break - case 'ADD_STOPS_AT_INTERVAL': - // create first stop if none exist - if (this.props.activePattern.patternStops.length === 0) { - this.addStopAtPoint(e.latlng, true) - } else { - let coordinates = this.props.activePattern.shape && this.props.activePattern.shape.coordinates - let patternStops = [...this.props.activePattern.patternStops] - let initialDistance = lineDistance(this.props.activePattern.shape, 'meters') - - // extend pattern to click point - let endPoint - if (coordinates) { - endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) - } else { - endPoint = {lng: patternStops[0].stop_lon, lat: patternStops[0].stop_lat} - } - const updatedShape = await this.extendPatternToPoint(this.props.activePattern, endPoint, e.latlng) - let totalDistance = lineDistance(this.props.activePattern.shape, 'meters') - let distanceAdded = totalDistance - initialDistance - let numIntervals = distanceAdded / this.props.editSettings.stopInterval - const latlngList = [] - for (var i = 1; i < numIntervals; i++) { - let stopDistance = initialDistance + i * this.props.editSettings.stopInterval - - // add stops along shape at interval (stopInterval) - let position = along(updatedShape, stopDistance, 'meters') - let stopLatlng = ll.toLatlng(position.geometry.coordinates) - latlngList.push(stopLatlng) - - // pass patternStops.length as index to ensure pattern not extended to locaton - const newStop = await this.addStopAtPoint(stopLatlng, false, patternStops.length) - // add new stop to array - patternStops.push(this.stopToStopTime(newStop)) - } - // TODO: switch to adding multiple stops per action (Java controller and action promise need updating) - // const newStops = await this.addStopsAtPoints(latlngList) - // // add new stop to array - // patternStops = [...patternStops, ...newStops.map(s => this.stopToStopTime(s))] - - // update and save all new stops to pattern - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - } - break - default: - break - } - } - } - mapBoundsChanged (e) { - if (this.state.zoomToTarget) { - setTimeout(() => { - this.setState({zoomToTarget: false}) - }, 200) - return false - } else { - const zoom = e.target.getZoom() - const bounds = e.target.getBounds() - if (this.props.mapState.zoom !== zoom) { - this.props.updateMapSetting({zoom}) - } - if (!bounds.equals(this.props.mapState.bounds)) { - this.props.updateMapSetting({bounds: e.target.getBounds()}) - } - } - } - async handlePatternEdit (controlPointRef, begin, end) { - let followRoad = this.props.editSettings.followStreets // !e.originalEvent.shiftKey - let leafletPattern = this.refs[this.props.activePattern.id].leafletElement - let originalLatLngs - let originalEndPoint - let from, to - let markerLatLng - - if (controlPointRef !== null) { - markerLatLng = this.refs[controlPointRef].leafletElement.getLatLng() - } - - // set from, to for endPoint if we have markerLatLng - if (begin && markerLatLng) { - from = begin - to = [markerLatLng.lng, markerLatLng.lat] - } else if (begin) { // set just from (when controlPoint is removed) - from = begin - } else if (end) { // set from for beginPoint - from = [markerLatLng.lng, markerLatLng.lat] - } else if (this.state.newLatLngs) { // if pattern has been previously edited, use that endpoint - originalLatLngs = this.state.newLatLngs - originalEndPoint = originalLatLngs[originalLatLngs.length - 1] - from = [originalEndPoint.lng, originalEndPoint.lat] - } else { // otherwise use the original endpoint - originalLatLngs = this.props.activePattern.shape.coordinates.map(c => ([c[1], c[0]])) - originalEndPoint = originalLatLngs[originalLatLngs.length - 1] - from = [originalEndPoint[1], originalEndPoint[0]] // [latLngs[latLngs.length - 1].lng, latLngs[latLngs.length - 1].lat] - to = [markerLatLng.lng, markerLatLng.lat] - } - - let latLngs = leafletPattern.toGeoJSON().geometry.coordinates - - if (from) { - let points = [ - from.geometry ? from.geometry.coordinates : from - ] - if (to) { - points.push(to) - } - if (end) { - points.push(end.geometry.coordinates) - } - let newCoordinates - let newSegment = await getSegment(points, followRoad) - let originalSegment = lineString(latLngs) - - // slice line if middle control point - if (end && begin) { - let beginPoint = point(latLngs[0]) - let beginSlice = lineSlice(beginPoint, from, originalSegment) - let endPoint = point(latLngs[latLngs.length - 1]) - let endSlice = lineSlice(end, endPoint, originalSegment) - newCoordinates = [ - ...beginSlice.geometry.coordinates, - ...newSegment.coordinates, - ...endSlice.geometry.coordinates - ] - } else if (end) { // handle begin control point - let endPoint = point(latLngs[latLngs.length - 1]) - let endSlice = lineSlice(end, endPoint, originalSegment) - newCoordinates = [ - ...newSegment.coordinates, - ...endSlice.geometry.coordinates - ] - } else { // append latlngs if end control point - let beginPoint = point(latLngs[0]) - let beginSlice = lineSlice(beginPoint, from, originalSegment) - newCoordinates = [ - ...beginSlice.geometry.coordinates, - ...newSegment.coordinates - ] - } - let leafletCoords = newCoordinates.map(coord => ll.fromCoordinates(coord)) - leafletPattern.setLatLngs(leafletCoords) - // // add last coordinate as "stop" - // let endPoint = newPath[newPath.length - 1] - // // this.setState({patternStops: }) - - return newCoordinates - } - } - getMapComponents (component, entity, subEntity) { - switch (component) { - case 'route': - let route = entity - let bounds = this.refs.map && this.refs.map.leafletElement.getBounds() - // get intervals along path for arrow icons - let patternLength = this.props.activePattern && this.props.activePattern.shape ? lineDistance(this.props.activePattern.shape, 'meters') : 0 - let zoom = this.refs.map ? this.refs.map.leafletElement.getZoom() : 11 - let iconInterval = zoom > 15 - ? 200 - : zoom > 14 - ? 500 - : zoom > 12 - ? 2000 - : zoom > 10 - ? 4000 - : zoom > 6 - ? 8000 - : 10000 - // : 0.001 * Math.exp(zoom - 6) < 3 ? 3 : 0.001 * Math.exp(zoom - 3) - - // let directionIconInterval = patternLength / iconInterval // 2000 // meters - let lengthsAlongPattern = [] - for (var i = 0; i < Math.floor(patternLength / iconInterval); i++) { - let distance = i ? iconInterval * i : iconInterval / 2 - let position = along(this.props.activePattern.shape, distance, 'meters') - if (!bounds) continue - if (position.geometry.coordinates[1] > bounds.getNorth() || position.geometry.coordinates[1] < bounds.getSouth() || position.geometry.coordinates[0] > bounds.getEast() || position.geometry.coordinates[0] < bounds.getWest()) { - continue - } - lengthsAlongPattern.push([distance, position]) - } - const circleIcon = divIcon({ - className: '', - // iconSize: [24, 24], - html: `` - }) - let beginPoint - if (!route) { - return null - } - let timer = null - return ( - [ - - {route && route.tripPatterns - ? route.tripPatterns - .map(tp => { - const isActive = this.props.subEntityId === tp.id - let pattern = isActive ? this.props.activePattern : tp - const latLngs = pattern.shape ? pattern.shape.coordinates.map(c => ([c[1], c[0]])) : [] - // skip pattern if latlngs don't exist or some other pattern is active - if (!latLngs || !isActive && this.props.subEntityId) { - return null - } - beginPoint = latLngs[0] - let lineColor = this.props.activeEntity.route_color && this.props.editSettings.editGeometry - ? '#F3F315' // yellow if editing - : this.props.activeEntity.route_color // otherwise, use route color if it exists - ? `#${this.props.activeEntity.route_color}` - : this.props.editSettings.editGeometry - ? '#F3F315' // yellow if editing - : 'blue' - return ( - isActive ? this.addControlPoint(pattern, e.latlng) : this.setState({editable: this.refs[pattern.id].leafletElement})} - lineCap='butt' - color={lineColor} - opacity={isActive ? 0.8 : 0.5} - /> - ) - }) - : null - } - , - - {lengthsAlongPattern.length && this.refs[this.props.activePattern.id] // && false - ? lengthsAlongPattern.map((length, index) => { - let distance = length[0] - let position = length[1] - - let nextPosition = along(this.props.activePattern.shape, distance + 5, 'meters') - const dir = position && nextPosition ? bearing(position, nextPosition) : 0 - const color = '#000' - const arrowIcon = divIcon({ - // html: ``, - html: ``, - className: '' - }) - if (!position || !position.geometry || !position.geometry.coordinates) { - return null - } - return ( - - ) - }) - : null - } - , - - {this.props.stops.length && this.props.activePattern && this.props.activePattern.shape && this.props.editSettings.editGeometry - ? this.state.controlPoints.map((s, index) => { - // don't include controlPoint on end of segment (for now) or hidden controlPoints - if (s.stopId && this.props.editSettings.snapToStops) { - return null - } - let prevControlPoint = this.state.controlPoints[index - 1] - let nextControlPoint = this.state.controlPoints[index + 1] - - let begin = prevControlPoint - ? prevControlPoint.point - : along(this.props.activePattern.shape, 0, 'meters') - let end - if (nextControlPoint) { - end = nextControlPoint.point - } - let position = s.point - const color = s.permanent ? '#000' : '#888' - const iconType = s.stopId ? 'fa-square' : 'fa-times' - if (!position || !position.geometry || !position.geometry.coordinates) { - return null - } - const timesIcon = divIcon({ - className: '', - // iconSize: [24, 24], - html: `` - }) - return ( - { - const timerFunction = () => { - this.handlePatternEdit(`controlPoint-${index}`, begin, end) - } - timerFunction() - timer = setInterval(timerFunction, 500) - }} - onDragEnd={(e) => { - this.handleControlPointDragEnd(e, timer, `controlPoint-${index}`, index) - }} - onClick={(e) => { - console.log('control point clicked', e) - // only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop) - if (!s.permanent) { - this.removeControlPoint(this.props.activePattern, index, begin, end) - } - }} - color='black' - /> - ) - }) - : null - } - {beginPoint && this.props.editSettings.editGeometry && this.props.activePattern - ? { - let beginStop = this.props.stops.find(s => s.id === this.props.activePattern.patternStops[0].stopId) - let begin = point([beginStop.stop_lon, beginStop.stop_lat]) - const timerFunction = () => { - this.handlePatternEdit('controlPointBegin', null, begin) - } - timerFunction() - timer = setInterval(timerFunction, 1000) - }} - onDragEnd={(e) => { - this.handleControlPointDragEnd(e, timer, 'controlPointBegin') - }} - color='black' - /> - : null - } - , - - {this.props.stops.length && this.props.activePattern && !this.props.editSettings.hideStops - ? this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((s, index) => { - const stop = this.props.stops.find(ps => ps.id === s.stopId) - if (!stop - // || this.props.mapState.zoom <= 11 && index > 0 && index < this.props.activePattern.patternStops.length - 1 - ) return null - const patternStopIcon = divIcon({ - html: ` - - ${index + 1} - `, - className: '', - iconSize: [24, 24] - }) - return ( - - - - - - ) - }) - : null - } - - , - - { - this.props.stops.length && this.props.activePattern && this.props.editSettings.addStops && this.props.mapState.zoom > 14 - ? this.props.stops - .filter(stop => { - if (!bounds) return false - if (stop.stop_lat > bounds.getNorth() || stop.stop_lat < bounds.getSouth() || stop.stop_lon > bounds.getEast() || stop.stop_lon < bounds.getWest()) { - return false - } else { - return true - } - }) - .map((stop, index) => { - if (!stop) return null - let patternStop = this.props.activePattern.patternStops.find(ps => ps.stopId === stop.id) - if (patternStop) return null - const color = 'blue' - const busIcon = divIcon({ - html: ` - - - `, - // html: ``, - className: '', - iconSize: [24, 24] - }) - return ( - - -
    -
    {stop.stop_name}
    - Add stop} - id={`split-button-basic-${i}`} - bsStyle='success' - onSelect={(key) => { - this.addStopToPattern(this.props.activePattern, stop, key) - }} - onClick={(e) => { - this.addStopToPattern(this.props.activePattern, stop) - }} - > - - Add to end (default) - - {this.props.activePattern.patternStops && this.props.activePattern.patternStops.map((stop, i) => { - let index = this.props.activePattern.patternStops.length - i - return ( - - {index === 1 ? 'Add to beginning' : `Insert as stop #${index}`} - - ) - })} - -
    -
    -
    - ) - }) - : null - } -
    - ] - ) - case 'stop': - const paddedBounds = this.props.mapState.bounds.pad(0.05) - var results = this.props.stopTree && this.props.drawStops - ? this.props.stopTree.search({ - minX: paddedBounds.getWest(), - minY: paddedBounds.getSouth(), - maxX: paddedBounds.getEast(), - maxY: paddedBounds.getNorth() - }) - : [] - - if (this.props.activeEntity && results.findIndex(r => r[2].id === this.props.activeEntity.id) === -1) { - results.push([0, 0, this.props.activeEntity]) - } - // console.log(results) - const outOfZoom = !this.props.drawStops - // console.log(this.props.mapState.bounds, paddedBounds) - return [ - results ? results.map(result => { - const stop = result[2] - const isActive = this.props.activeEntity && this.props.activeEntity.id === stop.id - const busIcon = divIcon({ - html: ` - - - `, - className: '', - iconSize: [24, 24] - }) - const activeBusIcon = divIcon({ - html: ` - - - `, - className: '', - iconSize: [24, 24], - }) - const hidden = !isActive && outOfZoom - if (hidden) { - return null - } - if (isNaN(stop.stop_lat) || isNaN(stop.stop_lon)) { - return null - } - const marker = ( - { - console.log(e) - let latlng = e.target.getLatLng() - let stopLatLng = this.getStopLatLng(latlng) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, stopLatLng) - this.refs[`${stop.id}`].leafletElement.setLatLng(latlng) - }} - onClick={(e) => { - // set active entity - if (!isActive) { - this.props.setActiveEntity(this.props.feedSource.id, 'stop', stop) - } - }} - /> - ) - return marker - }) - : null - ] - default: - return null - } - } - overlayAdded (e) { - if (e.name === 'Route alignments' && !this.props.tripPatterns) { - this.props.fetchTripPatterns(this.props.feedSource.id) - } - } - render () { - const mapLayers = [ - { - name: 'Streets', - id: getConfigProperty('mapbox.map_id') - }, - { - name: 'Light', - id: 'mapbox.light' - }, - { - name: 'Dark', - id: 'mapbox.dark' - }, - { - name: 'Satellite', - id: 'mapbox.streets-satellite' - } - ] - const OVERLAYS = [ - { - name: 'Route alignments', - component: - {this.props.tripPatterns ? this.props.tripPatterns.map((tp) => { - if (!tp.latLngs) return null - return - }) : null} - - }, - { - name: 'Stop locations', - component: - } - ] - const { feedSource } = this.props - const offset = 0.005 - let feedSourceBounds = feedSource && feedSource.latestValidation && feedSource.latestValidation.bounds - ? [[feedSource.latestValidation.bounds.north + offset, feedSource.latestValidation.bounds.west - offset], [feedSource.latestValidation.bounds.south - offset, feedSource.latestValidation.bounds.east + offset]] - : [[60, 60], [-60, -20]] - let bounds = this.state.zoomToTarget - ? this.props.mapState.bounds - : this.refs.map - ? this.refs.map.leafletElement.getBounds() - : feedSourceBounds - - let mapWidth = this.state.width - this.props.offset - (this.props.sidebarExpanded ? 130 : 50) - - const mapStyle = { - height: '100%', - width: `${mapWidth}px`, - position: 'absolute', - left: `${this.props.offset}px` - } - if (this.props.hidden) { - mapStyle.display = 'none' - } - let mapProps = { - ref: 'map', - zoomControl: false, - style: mapStyle, - maxBounds: [[200, 180], [-200, -180]], - onContextMenu: (e) => this.mapRightClicked(e), - onClick: (e) => this.mapClicked(e), - onZoomEnd: (e) => this.mapBoundsChanged(e), - onMoveEnd: (e) => this.mapBoundsChanged(e), - onBaseLayerChange: (e) => this.mapBaseLayerChanged(e, mapLayers), - scrollWheelZoom: true, - onOverlayAdd: (e) => this.overlayAdded(e) - } - if (this.state.willMount || this.state.zoomToTarget) { - mapProps.bounds = bounds - } - const activeMapLayerIndex = mapLayers.findIndex(l => l.id === getUserMetadataProperty(this.props.user.profile, 'editor.map_id')) - return ( - - - - {mapLayers.map((layer, index) => ( - - - - ))} - {OVERLAYS.map((overlay, i) => ( - - {overlay.component} - - ))} - - { - this.getMapComponents(this.props.activeComponent, this.props.activeEntity, this.props.subEntityId) - } - {this.props.mapState.routesGeojson - ? - : null - } - - ) - } -} diff --git a/src/main/client/editor/components/EditorSidebar.js b/src/main/client/editor/components/EditorSidebar.js index 9070d9b1e..122017229 100644 --- a/src/main/client/editor/components/EditorSidebar.js +++ b/src/main/client/editor/components/EditorSidebar.js @@ -1,10 +1,6 @@ import React, {Component, PropTypes} from 'react' -import { Nav, NavItem, OverlayTrigger, Tooltip } from 'react-bootstrap' -import { browserHistory } from 'react-router' -import { shallowEqual } from 'react-pure-render' - -import { gtfsIcons } from '../util/gtfs' +import { gtfsIcons } from '../util/ui' import ActiveSidebarNavItem from '../../common/containers/ActiveSidebarNavItem' import ActiveSidebar from '../../common/containers/ActiveSidebar' @@ -20,7 +16,7 @@ export default class EditorSidebar extends Component { return component === item.id || component === 'scheduleexception' && item.id === 'calendar' } render () { - const { activeComponent, feedSource, setActiveEntity } = this.props + const { activeComponent, feedSource } = this.props return ( @@ -34,14 +30,14 @@ export default class EditorSidebar extends Component { return item.hideSidebar ? null : + icon={item.icon} label={item.label} + active={this.isActive(item, activeComponent)} + link={!feedSource + ? '/home' + : activeComponent === item.id + ? `/feed/${feedSource.id}/edit/` + : `/feed/${feedSource.id}/edit/${item.id}`} + /> })} ) diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index cdd3715ee..5ad42c9c1 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -17,7 +17,8 @@ import TimezoneSelect from '../../common/components/TimezoneSelect' import LanguageSelect from '../../common/components/LanguageSelect' import ActiveTripPatternList from '../containers/ActiveTripPatternList' import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import { getEntityName, gtfsIcons, getEntityBounds } from '../util/gtfs' +import { getEntityName, getEntityBounds } from '../util/gtfs' +import { gtfsIcons } from '../util/ui' import { getConfigProperty } from '../../common/util/config' export default class EntityDetails extends Component { diff --git a/src/main/client/editor/components/EntityList.js b/src/main/client/editor/components/EntityList.js index 729e3d3ce..28b33ab7f 100644 --- a/src/main/client/editor/components/EntityList.js +++ b/src/main/client/editor/components/EntityList.js @@ -1,16 +1,14 @@ import React, {Component, PropTypes} from 'react' -import { Button, ButtonToolbar, Nav, NavItem, Tooltip, OverlayTrigger } from 'react-bootstrap' +import { Button, Nav, NavItem } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' import { Table, Column } from 'react-virtualized' import { shallowEqual } from 'react-pure-render' import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import GtfsTable from './GtfsTable' +import EntityListButtons from './EntityListButtons' import { getEntityName } from '../util/gtfs' -import { getConfigProperty } from '../../common/util/config' export default class EntityList extends Component { - static propTypes = { feedSource: PropTypes.object, entities: PropTypes.array, @@ -23,7 +21,6 @@ export default class EntityList extends Component { newGtfsEntity: PropTypes.func.isRequired, activeComponent: PropTypes.string.isRequired } - constructor (props) { super(props) this.state = {} @@ -100,17 +97,13 @@ export default class EntityList extends Component { cursor: 'pointer', outline: 'none' } - const activeRowStyle = { - backgroundColor: activeColor, - borderBottom: 'solid 1px #ddd', - cursor: 'pointer', - outline: 'none' - } if (list[index] && (list[index].isActive || list[index].isSelected)) { - return activeRowStyle - } else { - return rowStyle + rowStyle.backgroundColor = activeColor } + return rowStyle + } + updateIndexes = (fromIndex, toIndex) => { + this.setState({fromIndex, toIndex}) } _onRowClick (index, list, shiftKey) { let fromIndex, toIndex @@ -133,7 +126,6 @@ export default class EntityList extends Component { // // } render () { - // console.log(this.props) const sidePadding = '5px' let panelWidth = !this.props.tableView ? `${this.props.width}px` : '100%' let panelStyle = { @@ -181,8 +173,6 @@ export default class EntityList extends Component { rowClassName='noselect' rowStyle={({ index }) => this._getRowStyle(index, list)} rowCount={list.length} - // onRowMouseOver={} - // onRowMouseOut={} onRowClick={({ index }) => { // timeout set in order to get shiftkey from div event listener setTimeout(() => { @@ -211,134 +201,18 @@ export default class EntityList extends Component { Create first {this.props.activeComponent === 'scheduleexception' ? 'exception' : this.props.activeComponent}
    - - const activeTable = getConfigProperty('modules.editor.spec') - .find(t => t.id === this.props.activeComponent) - const entityTable = this.props.tableView - ? ( - { - this.props.gtfsEntitySelected(type, entity) - }} - getGtfsEntity={(type, id) => { - return entArray.find(ent => ent.id === id) - // return this.props.gtfsEntityLookup[`${type}_${id}`] - }} - showHelpClicked={(tableId, fieldName) => { - const helpContent = fieldName - ? getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).fields - .find(f => f.name === fieldName).helpContent - : getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).helpContent - this.refs.page.showInfoModal({ - title: `Help for ${tableId}.txt` + (fieldName ? `: ${fieldName}` : ''), - body: helpContent || '(No help content found for this field)' - }) - }} - newRowsDisplayed={(rows) => { - this.props.newRowsDisplayed(activeTable.id, rows, this.props.feedSource) - }} - /> - ) - : null - return ( -
    +
    -
    - - {this.props.activeComponent === 'route' - ? Merge routes}> - - - : null - } - Duplicate {this.props.activeComponent}}> - - - Delete {this.props.activeComponent}}> - - - - {// Create new entity - this.props.activeComponent === 'stop' - ? Right-click map for new stop - : - } -
    + {/* Table view button */}
    {this.props.activeComponent === 'calendar' || this.props.activeComponent === 'scheduleexception' @@ -384,9 +258,8 @@ export default class EntityList extends Component { } {!this.props.tableView ? entityList - : entityTable + : null // GtfsTable fully removed from repo, last available at fe29528569f5f64c23a49d2af0bd224f3d63d010 } -
    ) } diff --git a/src/main/client/editor/components/EntityListButtons.js b/src/main/client/editor/components/EntityListButtons.js new file mode 100644 index 000000000..ffc0acf98 --- /dev/null +++ b/src/main/client/editor/components/EntityListButtons.js @@ -0,0 +1,106 @@ +import React, {Component} from 'react' +import { ButtonToolbar, Tooltip, OverlayTrigger, Button } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' + +export default class EntityListButtons extends Component { + render () { + const { + activeComponent, + cloneEntity, + feedSource, + showConfirmModal, + deleteEntity, + setActiveEntity, + entities, + newGtfsEntity, + toIndex, + fromIndex, + activeEntity, + list, + updateIndexes + } = this.props + return ( +
    + + {activeComponent === 'route' + ? Merge routes}> + + + : null + } + Duplicate {activeComponent}}> + + + Delete {activeComponent}}> + + + + {// Create new entity + activeComponent === 'stop' + ? Right-click map for new stop + : + } +
    + ) + } +} diff --git a/src/main/client/editor/components/FeedInfoPanel.js b/src/main/client/editor/components/FeedInfoPanel.js index 748a41bd2..cb26c0b73 100644 --- a/src/main/client/editor/components/FeedInfoPanel.js +++ b/src/main/client/editor/components/FeedInfoPanel.js @@ -5,7 +5,7 @@ import { browserHistory } from 'react-router' import CreateSnapshotModal from './CreateSnapshotModal' import SelectFileModal from '../../common/components/SelectFileModal.js' -import { gtfsIcons } from '../util/gtfs' +import { gtfsIcons } from '../util/ui' export default class FeedInfoPanel extends Component { @@ -31,8 +31,7 @@ export default class FeedInfoPanel extends Component { let nameArray = files[0].name.split('.') if (files[0].type !== 'application/zip' || nameArray[nameArray.length - 1] !== 'zip') { return false - } - else { + } else { this.props.displayRoutesShapefile(feedSource, files[0]) return true } @@ -66,13 +65,13 @@ export default class FeedInfoPanel extends Component { return (
    - -
    - { - this.props.createSnapshot(feedSource, name, comment) - }} - /> + +
    + { + this.props.createSnapshot(feedSource, name, comment) + }} + /> {/* Hide toolbar toggle */} {toolbarVisible ? 'Hide toolbar' : 'Show toolbar'}}> @@ -80,8 +79,7 @@ export default class FeedInfoPanel extends Component { onClick={() => { if (toolbarVisible) { this.setState({right: 30 - panelWidth}) - } - else { + } else { this.setState({right: 5}) } }} @@ -156,26 +154,26 @@ export default class FeedInfoPanel extends Component { - { - this.props.getSnapshots(feedSource) - }} - /> - - {this.props.feedSource && this.props.feedSource.editorSnapshots - ? this.props.feedSource.editorSnapshots.map(snapshot => { - return ( - Revert to {snapshot.name} - ) - }) - : No snapshots - } - + { + this.props.getSnapshots(feedSource) + }} + /> + + {this.props.feedSource && this.props.feedSource.editorSnapshots + ? this.props.feedSource.editorSnapshots.map(snapshot => { + return ( + Revert to {snapshot.name} + ) + }) + : No snapshots + } + } -
    +
    ) } diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 54defcced..5d5f02f8d 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -1,12 +1,11 @@ import React, {Component, PropTypes} from 'react' import Helmet from 'react-helmet' import { shallowEqual } from 'react-pure-render' -import { browserHistory } from 'react-router' import CurrentStatusMessage from '../../common/containers/CurrentStatusMessage' import ConfirmModal from '../../common/components/ConfirmModal.js' import CurrentStatusModal from '../../common/containers/CurrentStatusModal' -import EditorMap from './EditorMap' +import EditorMap from './map/EditorMap' import EditorHelpModal from './EditorHelpModal' import EditorSidebar from './EditorSidebar' import ActiveEntityList from '../containers/ActiveEntityList' @@ -62,7 +61,6 @@ export default class GtfsEditor extends Component { activeTableId: this.props.currentTable } } - componentWillMount () { this.props.onComponentMount(this.props) } @@ -85,8 +83,7 @@ export default class GtfsEditor extends Component { if (nextProps.feedSource && nextProps.activeEntity && (!this.props.activeEntity || nextProps.activeEntity.id !== this.props.activeEntity.id)) { // console.log(nextProps.activeComponent) // console.log(nextProps.activeEntity, nextProps.activeEntityId) - if (nextProps.activeComponent === 'route') { - console.log('getting trip patterns') + if (nextProps.activeComponent === 'route' && this.props.activeComponent !== 'route') { this.props.fetchTripPatternsForRoute(nextProps.feedSource.id, nextProps.activeEntity.id) } } @@ -94,8 +91,6 @@ export default class GtfsEditor extends Component { if (nextProps.subSubComponent && nextProps.activeSubSubEntity && !shallowEqual(nextProps.activeSubSubEntity, this.props.activeSubSubEntity)) { switch (nextProps.subSubComponent) { case 'timetable': - console.log(nextProps.subEntityId) - console.log(nextProps.activeSubSubEntity) let pattern = nextProps.activeEntity.tripPatterns.find(p => p.id === nextProps.subEntityId) // fetch trips if they haven't been fetched if (!pattern[nextProps.activeSubSubEntity]) { @@ -109,34 +104,66 @@ export default class GtfsEditor extends Component { showConfirmModal (props) { this.refs.confirmModal.open(props) } - + getMapOffset (activeComponent, dWidth, activeEntityId, lWidth) { + return activeComponent === 'feedinfo' + ? dWidth + : activeEntityId + ? lWidth + dWidth + : activeComponent + ? lWidth + : 0 + } render () { // console.log(this.props) - const feedSource = this.props.feedSource - const editingIsDisabled = this.props.feedSource ? !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') : true - if (this.props.feedSource && editingIsDisabled) { + const { + feedSource, + user, + activeEntityId, + tableData, + entities, + activeComponent, + sidebarExpanded, + feedInfo, + setActiveEntity, + subSubComponent, + activeEntity, + subEntityId, + activeSubSubEntity, + deleteEntity, + updateActiveEntity, + resetActiveEntity, + saveActiveEntity, + fetchTripsForCalendar, + updateCellValue, + cloneEntity, + newGtfsEntity, + mapState, + hideTutorial, + setTutorialHidden, + project + } = this.props + const editingIsDisabled = feedSource ? !user.permissions.hasFeedPermission(feedSource.projectId, feedSource.id, 'edit-gtfs') : true + if (feedSource && editingIsDisabled) { console.log('editing disabled') - // browserHistory.push(`/feed/${this.props.feedSource.id}`) + // browserHistory.push(`/feed/${feedSource.id}`) } - let listWidth = 220 - let detailsWidth = 300 - let entityDetails = this.props.activeEntityId - ? ( - this.showConfirmModal(props)} - {...this.props} - getGtfsEntity={(type, id) => { - return this.props.entities.find(ent => ent.id === id) - }} - getGtfsEntityIndex={(type, id) => { - return this.props.entities.findIndex(ent => ent.id === id) - }} - /> - ) + let LIST_WIDTH = 220 + let DETAILS_WIDTH = 300 + let entityDetails = activeEntityId + ? this.showConfirmModal(props)} + {...this.props} + getGtfsEntity={(type, id) => { + return entities.find(ent => ent.id === id) + }} + getGtfsEntityIndex={(type, id) => { + return entities.findIndex(ent => ent.id === id) + }} + /> : null const defaultTitle = `${getConfigProperty('application.title')}: GTFS Editor` return ( @@ -146,57 +173,55 @@ export default class GtfsEditor extends Component { titleTemplate={`${defaultTitle} - %s`} />
    - {this.props.subSubComponent === 'timetable' // && this.props.activeEntity + {subSubComponent === 'timetable' // && activeEntity ? this.showConfirmModal(props)} - activePatternId={this.props.subEntityId} - activeScheduleId={this.props.activeSubSubEntity} - setActiveEntity={this.props.setActiveEntity} - tableData={this.props.tableData} - deleteEntity={this.props.deleteEntity} - updateActiveEntity={this.props.updateActiveEntity} - resetActiveEntity={this.props.resetActiveEntity} - saveActiveEntity={this.props.saveActiveEntity} - fetchTripsForCalendar={this.props.fetchTripsForCalendar} - sidebarExpanded={this.props.sidebarExpanded} - updateCellValue={this.props.updateCellValue} + activePatternId={subEntityId} + activeScheduleId={activeSubSubEntity} + setActiveEntity={setActiveEntity} + tableData={tableData} + deleteEntity={deleteEntity} + updateActiveEntity={updateActiveEntity} + resetActiveEntity={resetActiveEntity} + saveActiveEntity={saveActiveEntity} + fetchTripsForCalendar={fetchTripsForCalendar} + sidebarExpanded={sidebarExpanded} + updateCellValue={updateCellValue} /> - : this.props.activeComponent === 'feedinfo' + : activeComponent === 'feedinfo' ? - : this.props.activeComponent + width={DETAILS_WIDTH} + {...this.props} + /> + : activeComponent ? [ this.showConfirmModal(props)} - - entities={this.props.entities} - activeEntityId={this.props.activeEntityId} - activeComponent={this.props.activeComponent} - feedSource={this.props.feedSource} + entities={entities} + activeEntityId={activeEntityId} + activeComponent={activeComponent} + feedSource={feedSource} key='entity-list' />, entityDetails @@ -204,35 +229,28 @@ export default class GtfsEditor extends Component { : null }
    diff --git a/src/main/client/editor/components/GtfsInput.js b/src/main/client/editor/components/GtfsInput.js deleted file mode 100644 index 8c445598a..000000000 --- a/src/main/client/editor/components/GtfsInput.js +++ /dev/null @@ -1,648 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Checkbox, Radio, Button, ButtonToolbar, Form, FormControl, FormGroup, ControlLabel, Nav, NavItem, Tooltip, OverlayTrigger, Panel } from 'react-bootstrap' -import validator from 'validator' -import Select from 'react-select' -import { sentence as toSentenceCase } from 'change-case' -import DateTimeField from 'react-bootstrap-datetimepicker' -import moment from 'moment' -import { SketchPicker } from 'react-color' - -import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import GtfsSearch from '../../gtfs/components/gtfssearch' -import TimezoneSelect from '../../common/components/TimezoneSelect' -import LanguageSelect from '../../common/components/LanguageSelect' - -export default class GtfsInput extends Component { - - static propTypes = { - row: PropTypes.number, - field: PropTypes.object, - // currentValue: PropTypes.object, - index: PropTypes.number, - - } - - constructor (props) { - super(props) - } - // onChange (value) { - // this.props.onChange(value) - // } - render () { - let { field, row, currentValue, index } = this.props - let validationErrors = [] - const editorField = field.name //.split(/_(.+)?/)[1] - let isNotValid - const standardLabel = {toSentenceCase(editorField.split(/_(.+)?/)[1])} ({editorField}) - const basicLabel = field.helpContent - ? {field.helpContent}}> - {editorField}{field.required ? ' *' : ''} - - : {editorField}{field.required ? ' *' : ''} - switch(field.inputType) { - case 'ID': - isNotValid = field.required && !currentValue - let indices = [] - let idList = this.props.entities.map(e => e[field.name]) - let idx = idList.indexOf(currentValue) - while (idx !== -1) { - indices.push(idx) - idx = idList.indexOf(currentValue, idx + 1) - } - let isNotUnique = currentValue && (indices.length > 1 || indices.length && this.props.entities[indices[0]].id !== this.props.activeEntity.id) - if (isNotValid || isNotUnique) { - validationErrors.push({field: field.name, invalid: isNotValid || isNotUnique}) - } - return ( - - {basicLabel} - { - let props = {} - props[editorField] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'TEXT': - case 'GTFS_TRIP': - case 'GTFS_SHAPE': - case 'GTFS_BLOCK': - case 'GTFS_FARE': - case 'GTFS_SERVICE': - isNotValid = field.required && !currentValue - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - let props = {} - props[editorField] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'URL': - isNotValid = field.required && !currentValue || currentValue && !validator.isURL(currentValue) - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - let props = {} - props[editorField] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'EMAIL': - isNotValid = field.required && !currentValue || currentValue && !validator.isEmail(currentValue) - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - let props = {} - props[editorField] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'GTFS_ZONE': - isNotValid = field.required && (currentValue === null || typeof currentValue === 'undefined') - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - console.log(input) - let props = {} - let val = input ? input.value : null - props[editorField] = val - this.setState({[editorField]: val}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - - }} - options={this.props.agencies - ? this.props.agencies.map(agency => { - return { - value: agency.id, - label: agency.agency_name, - agency - } - }) - : [] - } - /> - - ) - case 'GTFS_STOP': - const stopIndex = this.props.getGtfsEntityIndex('stop', currentValue) - const stop = this.props.getGtfsEntity('stop', currentValue) - - let stops = [...this.props.stops] - - // remove current entity from list of stops - if (stopIndex !== -1) { - stops.splice(stopIndex, 1) - } - return ( - - {basicLabel} - { - console.log(input) - let props = {} - let val = input ? input.value : null - props[editorField] = val - this.setState({[editorField]: val}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - } - } -} diff --git a/src/main/client/editor/components/GtfsTable.js b/src/main/client/editor/components/GtfsTable.js deleted file mode 100644 index c315f10fe..000000000 --- a/src/main/client/editor/components/GtfsTable.js +++ /dev/null @@ -1,422 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Row, Col, Table, Button, Glyphicon, Tooltip, OverlayTrigger, FormControl } from 'react-bootstrap' - -import GtfsSearch from '../../gtfs/components/gtfssearch' - -import EditableTextField from '../../common/components/EditableTextField' -import TimezoneSelect from '../../common/components/TimezoneSelect' - -const recordsPerPage = 25 - -export default class GtfsTable extends Component { - - constructor (props) { - super(props) - - this.state = { - currentPage: 1, - visibility: 'all' - } - } - - componentWillReceiveProps (nextProps) { - if (this.props.table !== nextProps.table) { - this.setState({ currentPage: 1 }) - } - } - - componentDidMount () { - this.props.newRowsDisplayed(this.getActiveRowData(this.state.currentPage)) - } - - componentDidUpdate () { - this.props.newRowsDisplayed(this.getActiveRowData(this.state.currentPage)) - } - - getActiveRowData (currentPage) { - if (!this.props.tableData) return [] - const tableValidation = this.props.validation || [] - if (this.state.visibility === 'validation' && tableValidation.length < 5000) { - return this.props.tableData - .filter(record => (tableValidation.find(v => v.rowIndex === record.origRowIndex))) - .slice((currentPage - 1) * recordsPerPage, - Math.min(currentPage * recordsPerPage, this.props.tableData.length)) - } - return this.props.tableData.slice((currentPage - 1) * recordsPerPage, - Math.min(currentPage * recordsPerPage, this.props.tableData.length)) - } - - render () { - const table = this.props.table - const rowData = this.getActiveRowData(this.state.currentPage) - console.log('table data', table) - console.log('rows', rowData) - const getInput = (row, field, currentValue, index) => { - const editorField = field.name //.split(/_(.+)?/)[1] - switch(field.inputType) { - case 'TEXT': - case 'URL': - case 'GTFS_AGENCY': - case 'GTFS_TRIP': - case 'GTFS_SHAPE': - case 'GTFS_BLOCK': - case 'GTFS_FARE': - case 'GTFS_SERVICE': - case 'GTFS_ZONE': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'TIMEZONE': - return ( - { - this.props.fieldEdited(table.id, row, editorField, option.value) - }} - /> - ) - case 'TIME': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'LATITUDE': - case 'LONGITUDE': - case 'NUMBER': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'DATE': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'COLOR': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'POSITIVE_INT': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'POSITIVE_NUM': - return ( - { - this.props.fieldEdited(table.id, row, editorField, value) - }} - /> - ) - case 'DROPDOWN': - return ( - { - this.props.fieldEdited(table.id, row, editorField, evt.target.value) - }} - > - {field.options.map(option => { - return - })} - - ) - case 'GTFS_ROUTE': - const routeEntity = this.props.getGtfsEntity('route', currentValue) - - const routeValue = routeEntity - ? { 'value': routeEntity.route_id, - 'label': routeEntity.route_short_name - ? `${routeEntity.route_short_name} - ${routeEntity.route_long_name}` - : routeEntity.route_long_name - } - : '' - - return ( - { - this.props.fieldEdited(table.id, row, editorField, evt.route.route_id) - this.props.gtfsEntitySelected('route', evt.route) - }} - value={routeValue} - /> - ) - case 'GTFS_STOP': - const stopEntity = this.props.getGtfsEntity('stop', currentValue) - const stopValue = stopEntity ? {'value': stopEntity.stop_id, 'label': stopEntity.stop_name } : '' - - return ( - { - this.props.fieldEdited(table.id, row, editorField, evt.stop.stop_id) - this.props.gtfsEntitySelected('stop', evt.stop) - }} - value={stopValue} - /> - ) - - } - } - - const headerStyle = { - fontWeight: 'bold', - fontSize: '24px', - borderBottom: '2px solid black', - marginBottom: '16px' - } - - const subHeaderStyle = { - marginBottom: '24px', - textAlign: 'right' - } - - const colHeaderStyle = { - background: 'gray', - borderRight: '4px solid white', - borderBottom: '0px', - color: 'white' - } - - const pageCount = this.props.tableData ? Math.ceil(this.props.tableData.length / recordsPerPage) : 0 - - return (
    - - -
    - {table.name} - { - this.props.showHelpClicked(table.id) - }} - /> -
    - -
    - - - -
    - {(pageCount > 1) - ? - - - - Page {this.state.currentPage} of {pageCount} - - - - - - Go to { - if (e.keyCode == 13) { - const newPage = parseInt(e.target.value) - if (newPage > 0 && newPage <= pageCount) { - e.target.value = '' - this.setState({ currentPage: newPage }) - } - } - }} - onFocus={(e) => e.target.select()} - /> - - - : null - } - - Show  - { - console.log('evt', evt.target.value); - this.setState({ - visibility: evt.target.value, - currentPage: 1 - }) - }} - > - - - - -
    - - - - Click (?) icon for details on field.
    Required fields denoted by (*)
    - -
    - - - - - {table.fields.map(field => { - return () - })} - - - - - {rowData && rowData.length > 0 - ? rowData.map((data, rowIndex) => { - const tableRowIndex = (this.state.currentPage - 1) * recordsPerPage + rowIndex - return ( - - { - table.fields.map((field, colIndex) => { - // get editor field by splitting on first underscore - const editorField = field.name // .split(/_(.+)?/)[1] - const validationIssue = this.props.validation - ? this.props.validation.find(v => - (v.rowIndex === data.origRowIndex && v.fieldName === field.name)) - : null - - const tooltip = validationIssue ? ( - {validationIssue.description} - ) : null - - return () - })} - - ) - }) - : null - } - -
    - {field.name}{field.required ? ' *' : ''} - { - this.props.showHelpClicked(table.id, field.name) - }} - /> -
    - {validationIssue - ?
    - - - -
    - : null - } -
    - {getInput(tableRowIndex, field, data[editorField], (rowIndex * table.fields.length) + colIndex + 1)} -
    -
    - - -
    - - {!rowData || rowData.length === 0 - ? - No entries exist for this table. - - : null - } - - - - - -
    ) - } -} diff --git a/src/main/client/editor/components/GtfsTableEditor.js b/src/main/client/editor/components/GtfsTableEditor.js deleted file mode 100644 index 7d4963bcf..000000000 --- a/src/main/client/editor/components/GtfsTableEditor.js +++ /dev/null @@ -1,158 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Grid, Row, Col, Button, Glyphicon, PageHeader } from 'react-bootstrap' -import JSZip from 'jszip' -import { browserHistory, Link } from 'react-router' - -import ManagerPage from '../../common/components/ManagerPage' -import GtfsTable from './GtfsTable' -import { getConfigProperty } from '../../common/util/config' - -export default class GtfsTableEditor extends Component { - - constructor (props) { - super(props) - - this.state = { - activeTableId: this.props.currentTable - } - } - - componentWillMount () { - this.props.onComponentMount(this.props) - } - - componentWillReceiveProps (nextProps) { - } - - save () { - const zip = new JSZip() - - for(const table of getConfigProperty('modules.editor.spec')) { - if (!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue - - let fileContent = '' - // white the header line - const fieldNameArr = table.fields.map(field => field['name']) - fileContent += fieldNameArr.join(',') + '\n' - - // write the data rows - var dataRows = this.props.tableData[table.id].map(rowData => { - const rowText = fieldNameArr.map(fieldName => { - return rowData[fieldName] || '' - }).join(',') - fileContent += rowText + '\n' - }) - - // add to the zip archive - zip.file(table.name, fileContent) - } - - zip.generateAsync({type:"blob"}).then((content) => { - this.props.feedSaved(content) - }) - } - - render () { - if (!this.props.feedSource) return null - const editingIsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.feedSource.projectId, this.props.feedSource.id, 'edit-gtfs') - const buttonStyle = { - display: 'block', - width: '100%' - } - - const activeTable = getConfigProperty('modules.editor.spec') - .find(t => t.id === this.state.activeTableId) - - return ( - - - - -
      -
    • Explore
    • -
    • Projects
    • -
    • {this.props.project.name}
    • -
    • {this.props.feedSource.name}
    • -
    • Edit GTFS
    • -
    - -
    - - - Editing GTFS for {this.props.feedSource.name} - - - - - - - - {getConfigProperty('modules.editor.spec').map(table => { - return (

    - -

    ) - })} - - - { - this.props.gtfsEntitySelected(type, entity) - }} - getGtfsEntity={(type, id) => { - return this.props.gtfsEntityLookup[`${type}_${id}`] - }} - showHelpClicked={(tableId, fieldName) => { - const helpContent = fieldName - ? getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).fields - .find(f => f.name === fieldName).helpContent - : getConfigProperty('modules.editor.spec') - .find(t => t.id === tableId).helpContent - this.refs.page.showInfoModal({ - title: `Help for ${tableId}.txt` + (fieldName ? `: ${fieldName}` : ''), - body: helpContent || '(No help content found for this field)' - }) - }} - newRowsDisplayed={(rows) => { - this.props.newRowsDisplayed(activeTable.id, rows, this.props.feedSource) - }} - /> - -
    -
    -
    - ) - } -} diff --git a/src/main/client/editor/components/GtfsVersionSummary.js b/src/main/client/editor/components/GtfsVersionSummary.js index 55afdf283..1b1ba0f26 100644 --- a/src/main/client/editor/components/GtfsVersionSummary.js +++ b/src/main/client/editor/components/GtfsVersionSummary.js @@ -15,14 +15,14 @@ export default class GtfsVersionSummary extends Component { } isTableIncluded (tableId) { - if (!this.props.editor.tableData) return null - return tableId in this.props.editor.tableData ? 'Yes' : 'No' + if (!this.props.editor.data.tables) return null + return tableId in this.props.editor.data.tables ? 'Yes' : 'No' } tableRecordCount (tableId) { - if (!this.props.editor.tableData) return null - if (!(tableId in this.props.editor.tableData)) return 'N/A' - return this.props.editor.tableData[tableId].length.toLocaleString() + if (!this.props.editor.data.tables) return null + if (!(tableId in this.props.editor.data.tables)) return 'N/A' + return this.props.editor.data.tables[tableId].length.toLocaleString() } validationIssueCount (tableId) { diff --git a/src/main/client/editor/components/RouteEditor.js b/src/main/client/editor/components/RouteEditor.js deleted file mode 100644 index bf12a34fc..000000000 --- a/src/main/client/editor/components/RouteEditor.js +++ /dev/null @@ -1,85 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { Table, Button, ButtonToolbar } from 'react-bootstrap' -import {Icon} from '@conveyal/woonerf' -import { browserHistory, Link } from 'react-router' -import { LinkContainer } from 'react-router-bootstrap' - -import EntityDetails from './EntityDetails' - -export default class RouteEditor extends Component { - - constructor (props) { - super(props) - } - - render () { - const routes = ['test', 'Route 123', 'Route 456', 'Route 1', 'Route 10'] - let panelStyle = { - width: '300px', - height: '100%', - position: 'absolute', - left: '0px', - zIndex: 99, - backgroundColor: 'white', - paddingRight: '5px', - paddingLeft: '5px' - } - const feedSource = this.props.feedSource - const rowStyle = { - cursor: 'pointer' - } - const routeTable = ( - - - - {routes.map(r => { - return ( - console.log(e)} - style={rowStyle} - > - - - ) - })} - -
    {r}
    - ) - return ( -
    -

    - - - - - - Route editor -

    - {routeTable} - {this.props.entity - ? - : null - } -
    - ) - } -} diff --git a/src/main/client/editor/components/StopCircleMarker.js b/src/main/client/editor/components/StopCircleMarker.js deleted file mode 100644 index 09ea02be5..000000000 --- a/src/main/client/editor/components/StopCircleMarker.js +++ /dev/null @@ -1,65 +0,0 @@ -const circleMarker = ( - { - this.setState({editStop: null, editFinished: null, editStopLatLng: null}) - - // reset latlng - this.refs[stop.id].leafletElement.setLatLng(stopLatLng) - - // set active entity - this.props.setActiveEntity(this.props.feedSource.id, 'stop', stop) - }} - onClick={(e) => { - document.removeEventListener('keydown', escapeListener, false) - // if editing of this stop just finished, open popup - if (this.state.editFinished === stop.id) { - console.log('begin editing again?') - // set current location - this.refs[stop.id].leafletElement.setLatLng(e.latlng) - this.setState({editStop: stop.id, editFinished: null, editStopLatLng: e.latlng}) - this.refs.map.leafletElement - .on('mousemove', (e) => { - this.refs[stop.id].leafletElement.setLatLng(e.latlng) - }) - document.addEventListener('keydown', escapeListener, false) - } - // click while actively editing: stop editing and fire update action - else if (editingStop) { - console.log('stop editing') - this.setState({editStop: null, editFinished: stop.id, editStopLatLng: e.latlng}) - this.refs.map.leafletElement.removeEventListener('mousemove') - document.removeEventListener('keydown', escapeListener, false) - const precision = 100000000 // eight decimal places is accurate up to 1.1 meters - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {stop_lat: Math.round(e.latlng.lat * precision) / precision, stop_lon: Math.round(e.latlng.lng * precision) / precision}) - } - // if active stop, begin editing - else if (isActive) { - this.setState({editStop: stop.id}) - this.refs.map.leafletElement - .on('mousemove', (e) => { - this.refs[stop.id].leafletElement.setLatLng(e.latlng) - }) - document.addEventListener('keydown', escapeListener, false) - } - // else, set as active stop - else { - console.log('resetting active stop') - this.resetMap() - - // set active entity - this.props.setActiveEntity(this.props.feedSource.id, 'stop', stop) - } - }} - > - {this.state.editFinished === stop.id - ? null - : null - } - -) diff --git a/src/main/client/editor/components/StopMarkersLayer.js b/src/main/client/editor/components/StopMarkersLayer.js deleted file mode 100644 index c69b4b21d..000000000 --- a/src/main/client/editor/components/StopMarkersLayer.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { PropTypes } from 'react' -import { latLng } from 'leaflet' -import {FeatureGroup, Marker} from 'react-leaflet' - -export default class StopMarkersLayer extends FeatureGroup { - - static propTypes = { - stops: PropTypes.array, - activeEntity: PropTypes.object, - feedSource: PropTypes.object, - mapState: PropTypes.object, - setActiveEntity: PropTypes.func, - updateActiveEntity: PropTypes.func, - entityEdited: PropTypes.bool - } - constructor (props) { - super(props) - } - render () { - let { stops, activeEntity, feedSource, mapState, setActiveEntity, updateActiveEntity, entityEdited } = this.props - return ( - - {stops - ? stops.map(stop => { - const isActive = activeEntity && activeEntity.id === stop.id - if (isActive) return null - if (!isActive) { - if (!mapState.zoom || mapState.zoom < 14) { - return null - } - if (stop.stop_lat > mapState.bounds.getNorth() || stop.stop_lat < mapState.bounds.getSouth() || stop.stop_lon > mapState.bounds.getEast() || stop.stop_lon < mapState.bounds.getWest()) { - return null - } - } - if (isNaN(stop.stop_lat) || isNaN(stop.stop_lon)) { - return null - } - const stopLatLng = [stop.stop_lat, stop.stop_lon] - const escapeListener = (e) => { - console.log(e) - // [Esc] is pressed - if (e.keyCode === 27 && entityEdited) { - console.log('escape pressed') - this.setState({editStop: null, editFinished: null, editStopLatLng: null}) - - // reset latlng - this.refs[stop.id].leafletElement.setLatLng(stopLatLng) - - // set active entity - setActiveEntity(feedSource.id, 'stop', stop) - - // remove listeners - this.refs.map.leafletElement.removeEventListener('mousemove') - document.removeEventListener('keydown', escapeListener, false) - } - } - const busIcon = divIcon({ - html: ` - - - `, - className: '', - iconSize: [24, 24], - }) - - const marker = ( - { - console.log(e) - let latlng = e.target.getLatLng() - let stopLatLng = this.getStopLatLng(latlng) - updateActiveEntity(activeEntity, 'stop', stopLatLng) - this.refs[`${stop.id}`].leafletElement.setLatLng(latlng) - }} - onClick={(e) => { - // set active entity - if (!isActive) - setActiveEntity(feedSource.id, 'stop', stop) - }} - > - - ) - - return marker - }) - : null - } - - ) - } -} diff --git a/src/main/client/editor/components/TimetableHeader.js b/src/main/client/editor/components/TimetableHeader.js deleted file mode 100644 index b0ea0928f..000000000 --- a/src/main/client/editor/components/TimetableHeader.js +++ /dev/null @@ -1,318 +0,0 @@ -import React, {Component, PropTypes} from 'react' -import { InputGroup, Col, Row, Checkbox, Label, Button, Form, OverlayTrigger, Tooltip, ButtonGroup } from 'react-bootstrap' -import truncate from 'truncate' -import {Icon} from '@conveyal/woonerf' -import Select from 'react-select' - -import HourMinuteInput from './HourMinuteInput' -import { getEntityName } from '../util/gtfs' - -export default class TimetableHeader extends Component { - static propTypes = { - feedSource: PropTypes.object - } - constructor (props) { - super(props) - this.state = { - selected: this.props.selected, - edited: this.props.edited - } - } - render () { - const { feedSource, route, tableData, activeScheduleId, activePattern, setActiveEntity, fetchTripsForCalendar } = this.props - const calendars = tableData.calendar || [] - const activeCalendar = calendars.find(c => c.id === activeScheduleId) - const headerStyle = { - backgroundColor: 'white' - } - const tableType = activePattern && activePattern.useFrequency - ? 'Frequency editor' - : 'Timetable editor' - const patternName = activePattern && activePattern.name - const calendarName = activeCalendar && activeCalendar.service_id - return ( -
    - - -

    - Back to route}> - - - {tableType} -

    - - -

    - {this.props.data.length} trips for {truncate(patternName, 15)} on {truncate(calendarName, 13)} calendar -

    - - -
    - {activePattern && !activePattern.useFrequency - ? { - this.props.toggleDepartureTimes() - }} - > - Hide departures - - : null - } - {' '} - - { - this.props.setOffset(seconds) - }} - /> - - - - -
    - -
    - - - - - - - - - - - - - Add new trip}> - - - Duplicate trips}> - - - Delete trips}> - - - Undo changes}> - - - Save changes}> - - - - - -
    - ) - } -} - -class RouteSelect extends Component { - _render = (option) => { - return ( - - {truncate(option.label, 23)} - {' '} - - - ) - } - render () { - const { route, routes, feedSource, setActiveEntity } = this.props - return ( - Select pattern...} - options={patterns.map(pattern => ({value: pattern.id, label: `${getEntityName('pattern', pattern)}` || '[Unnamed]', pattern}))} - onChange={(value) => { - const pattern = value && value.pattern || {id: 'new'} - setActiveEntity(feedSource.id, 'route', route, 'trippattern', pattern, 'timetable', null) - }} - /> - ) - } -} - -class CalendarSelect extends Component { - _render = (option) => { - const patternTrips = this.props.activePattern && this.props.activePattern.tripCountByCalendar[option.value] || 0 - const routeCount = Object.keys(option.calendar.routes).length - return ( - - {option.label} - {' '} - - {' '} - - {' '} - - - ) - } - render () { - const { activePattern, route, feedSource, activeCalendar, calendars, setActiveEntity, trips } = this.props - const options = calendars && route - ? calendars.sort((a, b) => { - if (route.id in a.routes && !(route.id in b.routes)) return -1 - else if (route.id in b.routes && !(route.id in a.routes)) return 1 - else return b.numberOfTrips - a.numberOfTrips - }).map(calendar => { - return { - label: calendar.description, - value: calendar.id, - service_id: calendar.service_id, - calendar, - totalTrips: calendar.numberOfTrips, - routeTrips: calendar.routes[route.id] || 0, - calendarTrips: trips.length - } - }) - : [] - return ( - Select calendar...} + valueRenderer={this._render} + optionRenderer={this._render} + disabled={!activePattern || activePattern.id === 'new'} + options={options} + onChange={(value) => { + const calendar = value && value.calendar + setActiveEntity(feedSource.id, 'route', route, 'trippattern', activePattern, 'timetable', calendar) + }} + filterOptions + /> + ) + } +} diff --git a/src/main/client/common/components/EditableCell.js b/src/main/client/editor/components/timetable/EditableCell.js similarity index 92% rename from src/main/client/common/components/EditableCell.js rename to src/main/client/editor/components/timetable/EditableCell.js index c82f14395..adfb7c2cc 100644 --- a/src/main/client/common/components/EditableCell.js +++ b/src/main/client/editor/components/timetable/EditableCell.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react' import ReactDOM from 'react-dom' import moment from 'moment' -import { TIMETABLE_FORMATS } from '../../editor/util' +import { TIMETABLE_FORMATS } from '../../util' export default class EditableCell extends Component { static propTypes = { @@ -11,19 +11,11 @@ export default class EditableCell extends Component { constructor (props) { super(props) this.state = { - isEditing: false, - isFocused: false, - data: '' - } - } - componentWillMount () { - this.setState({ isEditing: this.props.isEditing, edited: false, - isFocused: this.props.isFocused, data: this.props.data, originalData: this.props.data - }) + } } componentWillReceiveProps (nextProps) { if (this.props.data !== nextProps.data) { @@ -54,17 +46,8 @@ export default class EditableCell extends Component { this.props.onClick() } } - handleDoubleClick (evt) { - // this.setState({isEditing: true, isFocused: false}) - } handleKeyDown (evt) { let input = ReactDOM.findDOMNode(this.refs.cellInput) - // console.log(input.selectionStart) - // console.log(input.value.length) - // if (evt.keyCode >= 65 && evt.keyCode <= 90) { - // evt.preventDefault() - // return false - // } switch (evt.keyCode) { case 88: // x if (this.props.renderTime) { @@ -263,7 +246,9 @@ export default class EditableCell extends Component { style={cellStyle} onClick={(evt) => this.handleClick(evt)} > - {cellHtml} + { + cellHtml + }
    ) } diff --git a/src/main/client/editor/components/timetable/HeaderCell.js b/src/main/client/editor/components/timetable/HeaderCell.js new file mode 100644 index 000000000..f5d1c1a42 --- /dev/null +++ b/src/main/client/editor/components/timetable/HeaderCell.js @@ -0,0 +1,46 @@ +import React, {Component} from 'react' + +export default class HeaderCell extends Component { + constructor (props) { + super(props) + this.state = { + active: this.props.active + } + } + componentWillReceiveProps (nextProps) { + const { active } = nextProps + if (this.props.active !== active) { + this.setState({active}) + } + } + _handleClick () { + if (this.props.selectable) { + this.setState({active: !this.state.active}) + this.props.onChange(!this.state.active) + } + } + render () { + const edgeDiff = 0.5 + const style = { + backgroundColor: this.state.active ? '#A8D4BB' : '#eee', + border: '1px solid #ddd', + margin: `${-0.5 + edgeDiff}px`, + padding: `${-edgeDiff}px`, + UserSelect: 'none', + userSelect: 'none', + paddingTop: '6px', + cursor: this.props.selectable ? 'pointer' : 'default', + ...this.props.style + } + return ( +
    this._handleClick()} + > + {this.props.label} +
    + ) + } +} diff --git a/src/main/client/editor/components/timetable/PatternSelect.js b/src/main/client/editor/components/timetable/PatternSelect.js new file mode 100644 index 000000000..b9b55487d --- /dev/null +++ b/src/main/client/editor/components/timetable/PatternSelect.js @@ -0,0 +1,43 @@ +import React, {Component} from 'react' +import {Icon} from '@conveyal/woonerf' +import { Label } from 'react-bootstrap' +import Select from 'react-select' + +import { getEntityName } from '../../util/gtfs' + +export default class PatternSelect extends Component { + _render = (option) => { + const calendarCount = Object.keys(option.pattern.tripCountByCalendar).length + return ( + + {option.label} + {' '} + + {' '} + + + ) + } + render () { + const { activePattern, route, feedSource, setActiveEntity } = this.props + const patterns = route && route.tripPatterns ? route.tripPatterns : [] + return ( + Select route...} + options={routes && routes.map(route => ({value: route.id, label: `${getEntityName('route', route)}` || '[Unnamed]', route, routeTrips: route.numberOfTrips}))} + clearable={false} + entities={routes} + onChange={(value) => { + const patt = {id: 'new'} + setActiveEntity(feedSource.id, 'route', value.route, 'trippattern', patt, 'timetable', null) + }} + /> + ) + } +} diff --git a/src/main/client/editor/components/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js similarity index 87% rename from src/main/client/editor/components/Timetable.js rename to src/main/client/editor/components/timetable/Timetable.js index 73862fdba..c480c73f9 100644 --- a/src/main/client/editor/components/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -6,9 +6,10 @@ import update from 'react-addons-update' import objectPath from 'object-path' import scrollbarSize from 'dom-helpers/util/scrollbarSize' -import EditableCell from '../../common/components/EditableCell' -import Loading from '../../common/components/Loading' -import { isTimeFormat, TIMETABLE_FORMATS } from '../util' +import HeaderCell from './HeaderCell' +import EditableCell from './EditableCell' +import Loading from '../../../common/components/Loading' +import { isTimeFormat, TIMETABLE_FORMATS } from '../../util' export default class Timetable extends Component { static propTypes = { @@ -21,8 +22,6 @@ export default class Timetable extends Component { activeCell: null, // 'rowNum-colNum', e.g. 0-1 edited: [], offsetSeconds: 0, - data: this.props.data, - columns: this.props.columns, // scrollsync columnWidth: 30, @@ -100,7 +99,7 @@ export default class Timetable extends Component { : 90 return width } - handleCellClick (rowIndex, columnIndex) { + handleCellClick = (rowIndex, columnIndex) => { this.setState({scrollToColumn: columnIndex, scrollToRow: rowIndex}) } cellValueInvalid (col, value, previousValue) { @@ -150,8 +149,7 @@ export default class Timetable extends Component { // if (this.props.timetable.hideDepartureTimes && this.props.timetable.columns[colIndex + 1] && this.props.timetable.columns[colIndex + 1].type === 'DEPARTURE_TIME') { // this.updateCellValue(value, rowIndex, `${rowIndex}.${this.props.timetable.columns[columnIndex + 1].key}`) // } - }} - /> + }} /> ) } getHeaderColumns () { @@ -276,12 +274,7 @@ export default class Timetable extends Component {
    ) } - const { - columnWidth, - overscanColumnCount, - overscanRowCount, - rowHeight - } = this.state + const { columnWidth, overscanColumnCount, overscanRowCount, rowHeight } = this.state const columnHeaderCount = this.getHeaderColumns().length return ( @@ -295,8 +288,7 @@ export default class Timetable extends Component { disabled={this.state.activeCell !== null} scrollToColumn={this.state.scrollToColumn} scrollToRow={this.state.scrollToRow} - rowCount={this.props.data.length} - > + rowCount={this.props.data.length}> {({ onSectionRendered, scrollToColumn, scrollToRow }) => (
    this.handleKeyPress(evt, scrollToColumn, scrollToRow)} - > + onKeyDown={(evt) => this.handleKeyPress(evt, scrollToColumn, scrollToRow)}>
    + }}> {/* Top Left Cell */} + columnCount={1} />
    @@ -338,8 +325,7 @@ export default class Timetable extends Component { position: 'absolute', left: 0, top: rowHeight - }} - > + }}> {/* Left Side Column */} + width={width - scrollbarSize() - columnWidth} />
    + }}> {/* Primary timetable grid */} { this.grid = Grid }} - style={{ - outline: 'none' - }} + style={{outline: 'none'}} columnWidth={this._getColumnWidth} columnCount={this.props.columns.length} height={height} @@ -402,8 +384,7 @@ export default class Timetable extends Component { rowCount={this.props.data.length} scrollToColumn={scrollToColumn} scrollToRow={scrollToRow} - width={width - scrollbarSize() - columnWidth} - /> + width={width - scrollbarSize() - columnWidth} />
    ) }} @@ -419,48 +400,3 @@ export default class Timetable extends Component { ) } } - -class HeaderCell extends Component { - constructor (props) { - super(props) - this.state = { - active: this.props.active - } - } - componentWillReceiveProps (nextProps) { - const { active } = nextProps - if (this.props.active !== active) { - this.setState({active}) - } - } - _handleClick () { - if (this.props.selectable) { - this.setState({active: !this.state.active}) - this.props.onChange(!this.state.active) - } - } - render () { - const edgeDiff = 0.5 - const style = { - backgroundColor: this.state.active ? '#A8D4BB' : '#eee', - border: '1px solid #ddd', - margin: `${-0.5 + edgeDiff}px`, - padding: `${-edgeDiff}px`, - UserSelect: 'none', - userSelect: 'none', - paddingTop: '6px', - cursor: this.props.selectable ? 'pointer' : 'default', - ...this.props.style - } - return ( -
    this._handleClick()} - > - {this.props.label} -
    - ) - } -} diff --git a/src/main/client/editor/components/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js similarity index 85% rename from src/main/client/editor/components/TimetableEditor.js rename to src/main/client/editor/components/timetable/TimetableEditor.js index 21244b451..e037e15df 100644 --- a/src/main/client/editor/components/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -1,11 +1,9 @@ import React, {Component, PropTypes} from 'react' -import { InputGroup, Checkbox, Nav, NavItem, NavDropdown, MenuItem, Button, Form, FormControl } from 'react-bootstrap' -import {Icon} from '@conveyal/woonerf' import clone from 'clone' import update from 'react-addons-update' import objectPath from 'object-path' -import { isTimeFormat } from '../util' +import { isTimeFormat } from '../../util' import TimetableHeader from './TimetableHeader' import Timetable from './Timetable' @@ -121,31 +119,30 @@ export default class TimetableEditor extends Component { this.props.toggleAllRows(false) } componentWillReceiveProps (nextProps) { - console.log('receiving props') - const activePattern = nextProps.route && nextProps.route.tripPatterns ? nextProps.route.tripPatterns.find(p => p.id === nextProps.activePatternId) : null - const activeSchedule = nextProps.tableData.calendar ? nextProps.tableData.calendar.find(c => c.id === nextProps.activeScheduleId) : null - const trips = activePattern && activeSchedule ? activePattern[nextProps.activeScheduleId] : [] - // add unsaved trips to list of trips received - if (nextProps.timetable.edited.length > 0) { - console.log('changes found', nextProps.timetable.edited.length) - for (var i = 0; i < nextProps.timetable.edited.length; i++) { - let rowIndex = nextProps.timetable.edited[i] - let trip = nextProps.timetable.trips[rowIndex] - if (trip) { - trips.push(trip) - } - } - } - } - shouldComponentUpdate (nextProps) { - return true - } + // console.log('receiving props') + // const activePattern = nextProps.route && nextProps.route.tripPatterns ? nextProps.route.tripPatterns.find(p => p.id === nextProps.activePatternId) : null + // const activeSchedule = nextProps.tableData.calendar ? nextProps.tableData.calendar.find(c => c.id === nextProps.activeScheduleId) : null + // const trips = activePattern && activeSchedule ? activePattern[nextProps.activeScheduleId] : [] + // // add unsaved trips to list of trips received + // if (nextProps.timetable.edited.length > 0) { + // console.log('changes found', nextProps.timetable.edited.length) + // for (var i = 0; i < nextProps.timetable.edited.length; i++) { + // let rowIndex = nextProps.timetable.edited[i] + // let trip = nextProps.timetable.trips[rowIndex] + // if (trip) { + // trips.push(trip) + // } + // } + // } + } + // shouldComponentUpdate (nextProps) { + // return true + // } offsetRows (rowIndexes, offsetAmount) { let newRows = [...this.props.timetable.trips] let editedRows = [] console.log(`Offsetting ${rowIndexes.length} rows by ${offsetAmount} seconds`) for (var i = 0; i < rowIndexes.length; i++) { - let row = newRows[rowIndexes[i]] editedRows.push(rowIndexes[i]) for (var j = 0; j < this.props.timetable.columns.length; j++) { let col = this.props.timetable.columns[j] @@ -190,15 +187,11 @@ export default class TimetableEditor extends Component { } this.setState(update(this.state, stateUpdate)) }) - } - _activePattern () { - } isDataValid (col, value, previousValue) { if (isTimeFormat(col.type)) { return value && value >= 0 && value < previousValue - } - else { + } else { return true } } @@ -218,16 +211,11 @@ export default class TimetableEditor extends Component { > this.removeSelectedRows()} offsetRows={(rowIndexes, offsetAmount) => this.offsetRows(rowIndexes, offsetAmount)} - hideDepartureTimes={this.props.timetable.hideDepartureTimes} addNewRow={(blank, scroll) => this.addNewRow(blank, scroll)} - edited={this.props.timetable.edited} - data={this.props.timetable.trips} saveEditedTrips={(pattern, scheduleId) => this.saveEditedTrips(pattern, scheduleId)} - {...this.props} - /> + {...this.props} /> {activeSchedule ? + {...this.props} /> :

    {activePattern ? diff --git a/src/main/client/editor/components/timetable/TimetableHeader.js b/src/main/client/editor/components/timetable/TimetableHeader.js new file mode 100644 index 000000000..03f179e8a --- /dev/null +++ b/src/main/client/editor/components/timetable/TimetableHeader.js @@ -0,0 +1,184 @@ +import React, {Component, PropTypes} from 'react' +import { InputGroup, Col, Row, Checkbox, Button, Form, OverlayTrigger, Tooltip, ButtonGroup } from 'react-bootstrap' +import truncate from 'truncate' +import {Icon} from '@conveyal/woonerf' + +import HourMinuteInput from '../HourMinuteInput' +import CalendarSelect from './CalendarSelect' +import RouteSelect from './RouteSelect' +import PatternSelect from './PatternSelect' + +export default class TimetableHeader extends Component { + static propTypes = { + feedSource: PropTypes.object + } + render () { + const { feedSource, timetable, setOffset, offsetRows, toggleDepartureTimes, addNewRow, removeSelectedRows, saveEditedTrips, route, tableData, activeScheduleId, activePattern, setActiveEntity, fetchTripsForCalendar } = this.props + const { selected, trips, hideDepartureTimes, edited, offset } = timetable + const calendars = tableData.calendar || [] + const activeCalendar = calendars.find(c => c.id === activeScheduleId) + const headerStyle = { + backgroundColor: 'white' + } + const tableType = activePattern && activePattern.useFrequency + ? 'Frequency editor' + : 'Timetable editor' + const patternName = activePattern && activePattern.name + const calendarName = activeCalendar && activeCalendar.service_id + const numberOfTrips = trips ? trips.length : 0 + return ( +

    + + +

    + Back to route}> + + + {tableType} +

    + + +

    + {numberOfTrips} trips for {truncate(patternName, 15)} on {truncate(calendarName, 13)} calendar +

    + + +
    + {activePattern && !activePattern.useFrequency + ? { + toggleDepartureTimes() + }} + > + Hide departures + + : null + } + {' '} + + { + setOffset(seconds) + }} + /> + + + + +
    + +
    + + + + + + + + + + + + + Add new trip}> + + + Duplicate trips}> + + + Delete trips}> + + + Undo changes}> + + + Save changes}> + + + + + +
    + ) + } +} diff --git a/src/main/client/editor/containers/ActiveEntityList.js b/src/main/client/editor/containers/ActiveEntityList.js index 3ffd4c8c4..458275fa0 100644 --- a/src/main/client/editor/containers/ActiveEntityList.js +++ b/src/main/client/editor/containers/ActiveEntityList.js @@ -1,27 +1,20 @@ import { connect } from 'react-redux' -import { - setActiveGtfsEntity, - newGtfsEntity, - saveActiveGtfsEntity, - deleteGtfsEntity, - getGtfsTable -} from '../actions/editor' -import { getEntityName } from '../util/gtfs' +import { getEntityName } from '../util/gtfs' import EntityList from '../components/EntityList' const mapStateToProps = (state, ownProps) => { const entity = - state.editor.active && state.editor.active.entity && state.editor.active.entity.id === ownProps.activeEntityId - ? state.editor.active.entity - : state.editor.active && state.editor.active.entity && ownProps.activeComponent === 'feedinfo' - ? state.editor.active.entity + state.editor.data.active && state.editor.data.active.entity && state.editor.data.active.entity.id === ownProps.activeEntityId + ? state.editor.data.active.entity + : state.editor.data.active && state.editor.data.active.entity && ownProps.activeComponent === 'feedinfo' + ? state.editor.data.active.entity : null let activeEntity = entity ? { - name: getEntityName(ownProps.activeComponent, entity), - id: entity.id - } + name: getEntityName(ownProps.activeComponent, entity), + id: entity.id + } : null return { activeEntity diff --git a/src/main/client/editor/containers/ActiveFeedInfoPanel.js b/src/main/client/editor/containers/ActiveFeedInfoPanel.js index f00457116..abbe88dfe 100644 --- a/src/main/client/editor/containers/ActiveFeedInfoPanel.js +++ b/src/main/client/editor/containers/ActiveFeedInfoPanel.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux' import { createSnapshot, fetchSnapshots, restoreSnapshot } from '../actions/snapshots' -import { displayRoutesShapefile } from '../actions/editor' +import { displayRoutesShapefile } from '../actions/map' import FeedInfoPanel from '../components/FeedInfoPanel' const mapStateToProps = (state, ownProps) => { return { } } diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index 5185cd45d..fb2af74a6 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -13,32 +13,26 @@ import { import { fetchTripPatternsForRoute, fetchTripPatterns, - undoActiveTripPatternEdits, + undoActiveTripPatternEdits +} from '../actions/tripPattern' +import { updateControlPoint, addControlPoint, - removeControlPoint -} from '../actions/tripPattern' + removeControlPoint, + updateMapSetting, + handleControlPointDragEnd, + handlePatternEdit +} from '../actions/map' import { fetchTripsForCalendar } from '../actions/trip' +import { updateEditSetting, setActiveGtfsEntity, deleteGtfsEntity, updateActiveGtfsEntity, resetActiveGtfsEntity, clearGtfsContent } from '../actions/active' import { - setActiveGtfsEntity, newGtfsEntity, newGtfsEntities, cloneGtfsEntity, - updateEditSetting, - updateMapSetting, saveActiveGtfsEntity, - resetActiveGtfsEntity, - deleteGtfsEntity, - updateActiveGtfsEntity, - clearGtfsContent, - addGtfsRow, - updateGtfsField, - deleteGtfsRow, - saveGtfsRow, getGtfsTable, - loadGtfsEntities, receiveGtfsEntities, uploadBrandingAsset } from '../actions/editor' @@ -47,57 +41,46 @@ import { findProjectByFeedSource } from '../../manager/util' import { setTutorialHidden } from '../../manager/actions/ui' const mapStateToProps = (state, ownProps) => { - const feedSourceId = ownProps.routeParams.feedSourceId // location.pathname.split('/')[2] - const activeComponent = ownProps.routeParams.subpage // location.pathname.split('/')[4] - // const { activeComponent, subComponent, subSubComponent, activeEntityId, subEntityId, activeSubSubEntity } = ownProps.routeParams.subpage // location.pathname.split('/')[4] - const subComponent = ownProps.routeParams.subsubpage // location.pathname.split('/')[5] - const subSubComponent = ownProps.routeParams.subsubcomponent // location.pathname.split('/')[6] - const activeEntityId = ownProps.routeParams.entity // location.pathname.split('/')[7] - const subEntityId = ownProps.routeParams.subentity // location.pathname.split('/')[8] - const activeSubSubEntity = ownProps.routeParams.subsubentity // location.pathname.split('/')[9] + const { + feedSourceId, + activeComponent, + subComponent, + subSubComponent, + activeEntityId, + subEntityId, + activeSubSubEntity + } = ownProps.routeParams const activeEntity = - state.editor.active && state.editor.active.entity && state.editor.active.entity.id === activeEntityId - ? state.editor.active.entity - : state.editor.active && state.editor.active.entity && activeComponent === 'feedinfo' - ? state.editor.active.entity + state.editor.data.active.entity && state.editor.data.active.entity.id === activeEntityId + ? state.editor.data.active.entity + : state.editor.data.active.entity && activeComponent === 'feedinfo' + ? state.editor.data.active.entity : null - const activePattern = state.editor.active && state.editor.active.subEntity - // const subEntityId = state.editor.active && state.editor.active.subEntity && state.editor.active.subEntity.id === activeEntityId - // ? state.editor.active.subEntity - // : state.editor.active && state.editor.active.subEntity && activeComponent === 'feedinfo' - // ? state.editor.active.subEntity - // : null - // ownProps.routeParams.entity && state.editor.tableData[activeComponent] - // ? state.editor.tableData[activeComponent].find(e => ownProps.routeParams.entity === e.id) - // : null + const activePattern = state.editor.data.active.subEntity const entityEdited = subComponent === 'trippattern' - ? state.editor.active.patternEdited - : state.editor.active && state.editor.active.edited + ? state.editor.data.active.patternEdited + : state.editor.data.active.edited const controlPoints = state.editor.editSettings.controlPoints && state.editor.editSettings.controlPoints.length ? state.editor.editSettings.controlPoints[state.editor.editSettings.controlPoints.length - 1] : [] const editSettings = state.editor.editSettings const mapState = state.editor.mapState - const stopTree = state.editor.stopTree + const stopTree = state.editor.mapState.stopTree const tableView = ownProps.location.query && ownProps.location.query.table === 'true' - const entities = state.editor.tableData[activeComponent] + const entities = state.editor.data.tables[activeComponent] let user = state.user // find the containing project const project = findProjectByFeedSource(state, feedSourceId) const feedSource = project && project.feedSources.find(fs => fs.id === feedSourceId) - const feedInfo = state.editor.tableData.feedinfo + const feedInfo = state.editor.data.tables.feedinfo return { - tableData: state.editor.tableData, + tableData: state.editor.data.tables, hideTutorial: state.ui.hideTutorial, tripPatterns: state.editor.tripPatterns, - timetable: state.editor.timetable, - // gtfsEntityLookup: state.editor.gtfsEntityLookup, - // validation: state.editor.validation, - // currentTable: state.routing.locationBeforeTransitions.hash ? state.routing.locationBeforeTransitions.hash.split('#')[1] : 'agency', feedSource, entities, feedSourceId, @@ -113,7 +96,6 @@ const mapStateToProps = (state, ownProps) => { activeEntityId, subEntityId, activePattern, - // subEntityIdId, activeSubSubEntity, editSettings, mapState, @@ -124,14 +106,15 @@ const mapStateToProps = (state, ownProps) => { } const mapDispatchToProps = (dispatch, ownProps) => { - const feedSourceId = ownProps.routeParams.feedSourceId - const activeComponent = ownProps.routeParams.subpage - const subComponent = ownProps.routeParams.subsubpage - const subSubComponent = ownProps.routeParams.subsubcomponent - const activeEntityId = ownProps.routeParams.entity - const subEntityId = ownProps.routeParams.subentity - const activeSubSubEntity = ownProps.routeParams.subsubentity - + const { + feedSourceId, + activeComponent, + subComponent, + subSubComponent, + activeEntityId, + subEntityId, + activeSubSubEntity + } = ownProps.routeParams return { updateUserMetadata: (profile, props) => { dispatch(updateUserMetadata(profile, props)) @@ -203,26 +186,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { } }, - // OLD ROW FUNCTIONS - newRowClicked: (tableId) => { - dispatch(addGtfsRow(tableId)) - }, - deleteRowClicked: (tableId, rowIndex) => { - dispatch(deleteGtfsRow(tableId, rowIndex)) - }, - getGtfsTable: (tableId, feedId) => { - dispatch(getGtfsTable(tableId, feedId)) - }, - saveRowClicked: (tableId, rowIndex, feedId) => { - dispatch(saveGtfsRow(tableId, rowIndex, feedId)) - }, - fieldEdited: (tableId, rowIndex, fieldName, newValue) => { - dispatch(updateGtfsField(tableId, rowIndex, fieldName, newValue)) - }, - newRowsDisplayed: (tableId, rows, feedSource) => { - if (feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) - }, - // NEW GENERIC GTFS/EDITOR FUNCTIONS updateEditSetting: (setting, value) => { dispatch(updateEditSetting(setting, value)) @@ -271,13 +234,15 @@ const mapDispatchToProps = (dispatch, ownProps) => { fetchTripPatterns: (feedSourceId) => { dispatch(fetchTripPatterns(feedSourceId)) }, fetchStopsForTripPattern: (feedSourceId, tripPatternId) => { dispatch(fetchStopsForTripPattern(feedSourceId, tripPatternId)) }, fetchStops: (feedSourceId) => { dispatch(fetchStops(feedSourceId)) }, - fetchTripsForCalendar: (feedSourceId, pattern, calendarId) => { dispatch(fetchTripsForCalendar(feedSourceId, pattern, calendarId)) }, + fetchTripsForCalendar: (feedSourceId, pattern, calendarId) => dispatch(fetchTripsForCalendar(feedSourceId, pattern, calendarId)), // TRIP PATTERN EDIT FUNCTIONS undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, addControlPoint: (controlPoint, index) => { dispatch(addControlPoint(controlPoint, index)) }, - removeControlPoint: (index) => { dispatch(removeControlPoint(index)) }, + removeControlPoint: (index, begin, end, pattern, polyline) => { dispatch(removeControlPoint(index, begin, end, pattern, polyline)) }, updateControlPoint: (index, point, distance) => { dispatch(updateControlPoint(index, point, distance)) }, + handleControlPointDragEnd: (e, timer, controlPoint, controlPointIndex, polyline, activePattern) => dispatch(handleControlPointDragEnd(e, timer, controlPoint, controlPointIndex, polyline, activePattern)), + handlePatternEdit: (controlPointRef, begin, end, polyline, activePattern) => dispatch(handlePatternEdit(controlPointRef, begin, end, polyline, activePattern)), // EDITOR UI setTutorialHidden: (value) => { dispatch(setTutorialHidden(value)) } diff --git a/src/main/client/editor/containers/ActiveGtfsTableEditor.js b/src/main/client/editor/containers/ActiveGtfsTableEditor.js deleted file mode 100644 index bdb8755a8..000000000 --- a/src/main/client/editor/containers/ActiveGtfsTableEditor.js +++ /dev/null @@ -1,92 +0,0 @@ -import { connect } from 'react-redux' - -import GtfsTableEditor from '../components/GtfsTableEditor' -import { fetchFeedSourceAndProject } from '../../manager/actions/feeds' -import { - addGtfsRow, - updateGtfsField, - deleteGtfsRow, - saveGtfsRow, - getGtfsTable, - uploadGtfsFeed, - downloadGtfsFeed, - importGtfsFromGtfs, - loadGtfsEntities, - receiveGtfsEntities -} from '../actions/editor' - -const mapStateToProps = (state, ownProps) => { - - let feedSourceId = ownProps.routeParams.feedSourceId - let user = state.user - // find the containing project - let project = state.projects.all - ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) - : null - - let feedSource - if (project) { - feedSource = project.feedSources.find(fs => fs.id === feedSourceId) - } - - return { - tableData: state.editor.tableData, - gtfsEntityLookup: state.editor.gtfsEntityLookup, - validation: state.editor.validation, - currentTable: state.routing.locationBeforeTransitions.hash ? state.routing.locationBeforeTransitions.hash.split('#')[1] : 'agency', - feedSource, - project, - user - } -} - -const mapDispatchToProps = (dispatch, ownProps) => { - const feedSourceId = ownProps.routeParams.feedSourceId - const feedVersionId = ownProps.routeParams.feedVersionId - - return { - onComponentMount: (initialProps) => { - if (!initialProps.feedSource) dispatch(fetchFeedSourceAndProject(feedSourceId)) - if (!initialProps.tableData) dispatch(downloadGtfsFeed(feedVersionId)) - if (initialProps.currentTable) dispatch(getGtfsTable(initialProps.currentTable, feedSourceId)) - }, - newRowClicked: (tableId) => { - dispatch(addGtfsRow(tableId)) - }, - deleteRowClicked: (tableId, rowIndex) => { - dispatch(deleteGtfsRow(tableId, rowIndex)) - }, - getGtfsTable: (tableId, feedId) => { - dispatch(getGtfsTable(tableId, feedId)) - }, - saveRowClicked: (tableId, rowIndex, feedId) => { - dispatch(saveGtfsRow(tableId, rowIndex, feedId)) - }, - fieldEdited: (tableId, rowIndex, fieldName, newValue) => { - dispatch(updateGtfsField(tableId, rowIndex, fieldName, newValue)) - }, - feedSaved: (file) => { - dispatch(uploadGtfsFeed(feedVersionId, file)) - .then(() => { - console.log('re-downloading'); - dispatch(downloadGtfsFeed(feedVersionId)) - }) - }, - newRowsDisplayed: (tableId, rows, feedSource) => { - if (feedSource) dispatch(loadGtfsEntities(tableId, rows, feedSource)) - }, - gtfsEntitySelected: (type, entity) => { - dispatch(receiveGtfsEntities([entity])) - } - } -} - -const ActiveGtfsTableEditor = connect( - mapStateToProps, - mapDispatchToProps -)(GtfsTableEditor) - -export default ActiveGtfsTableEditor diff --git a/src/main/client/editor/containers/ActiveGtfsVersionSummary.js b/src/main/client/editor/containers/ActiveGtfsVersionSummary.js index 30af998c5..8bb2fdb24 100644 --- a/src/main/client/editor/containers/ActiveGtfsVersionSummary.js +++ b/src/main/client/editor/containers/ActiveGtfsVersionSummary.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux' -import GtfsVersionSummary from '../components/GtfsVersionSummary' +import GtfsVersionSummary from '../components/GtfsVersionSummary' import { downloadGtfsFeed, diff --git a/src/main/client/editor/containers/ActiveTimetableEditor.js b/src/main/client/editor/containers/ActiveTimetableEditor.js index f48d2fb43..78edb986b 100644 --- a/src/main/client/editor/containers/ActiveTimetableEditor.js +++ b/src/main/client/editor/containers/ActiveTimetableEditor.js @@ -10,14 +10,15 @@ import { setOffset } from '../actions/trip' -import TimetableEditor from '../components/TimetableEditor' +import TimetableEditor from '../components/timetable/TimetableEditor' const mapStateToProps = (state, ownProps) => { - const activePattern = ownProps.route && ownProps.route.tripPatterns ? ownProps.route.tripPatterns.find(p => p.id === ownProps.activePatternId) : null - const activeSchedule = ownProps.tableData.calendar ? ownProps.tableData.calendar.find(c => c.id === ownProps.activeScheduleId) : null + const activePattern = state.editor.data.active.subEntity + const activeSchedule = state.editor.data.tables.calendar ? state.editor.data.tables.calendar.find(c => c.id === ownProps.activeScheduleId) : null return { activePattern, - activeSchedule + activeSchedule, + timetable: state.editor.timetable } } diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js index 05af640cc..54388ca6f 100644 --- a/src/main/client/editor/containers/ActiveTripPatternList.js +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -1,13 +1,9 @@ import { connect } from 'react-redux' +import { updateEditSetting, setActiveGtfsEntity, deleteGtfsEntity, updateActiveGtfsEntity, resetActiveGtfsEntity } from '../actions/active' import { - setActiveGtfsEntity, newGtfsEntity, saveActiveGtfsEntity, - deleteGtfsEntity, - updateEditSetting, updateMapSetting, - updateActiveGtfsEntity, - resetActiveGtfsEntity, cloneGtfsEntity, undoActiveTripPatternEdits } from '../actions/editor' @@ -18,17 +14,16 @@ import TripPatternList from '../components/TripPatternList' const mapStateToProps = (state, ownProps) => { const mapState = state.editor.mapState const editSettings = state.editor.editSettings - const stops = state.editor.tableData.stop - const activePattern = state.editor.active.subEntity - - const feedSourceId = state.editor.active.feedSourceId + const stops = state.editor.data.tables.stop + const activePattern = state.editor.data.active.subEntity + const feedSourceId = state.editor.data.active.feedSourceId // find the containing project const project = findProjectByFeedSource(state, feedSourceId) const feedSource = project && project.feedSources.find(fs => fs.id === feedSourceId) - const activeEntity = state.editor.active.entity - // const subSubComponent = state.editor.active.subSubComponent - const activePatternId = state.editor.active.subEntityId + const activeEntity = state.editor.data.active.entity + // const subSubComponent = state.editor.data.active.subSubComponent + const activePatternId = state.editor.data.active.subEntityId return { mapState, @@ -37,7 +32,6 @@ const mapStateToProps = (state, ownProps) => { activePattern, feedSource, activeEntity, - // subSubComponent, activePatternId } } diff --git a/src/main/client/editor/props/index.js b/src/main/client/editor/props/index.js new file mode 100644 index 000000000..4a278f3d0 --- /dev/null +++ b/src/main/client/editor/props/index.js @@ -0,0 +1,35 @@ +import { PropTypes } from 'react' + +export const EditorMapProps = { + subEntityId: PropTypes.string, + activeEntityId: PropTypes.string, + activeComponent: PropTypes.string, + subComponent: PropTypes.string, + currentPattern: PropTypes.object, + stops: PropTypes.array, + stopTree: PropTypes.object, + editSettings: PropTypes.object, + mapState: PropTypes.object, + feedSource: PropTypes.object, + feedInfo: PropTypes.object, + zoomToTarget: PropTypes.string, + entities: PropTypes.array, + activeEntity: PropTypes.object, + entityEdited: PropTypes.bool, + offset: PropTypes.number, + tripPatterns: PropTypes.array, + updateActiveEntity: PropTypes.func, + saveActiveEntity: PropTypes.func, + setActiveEntity: PropTypes.func, + updateControlPoint: PropTypes.func, + newGtfsEntity: PropTypes.func, + fetchTripPatterns: PropTypes.func, + updateMapSetting: PropTypes.func, + addControlPoint: PropTypes.func, + removeControlPoint: PropTypes.func, + updateUserMetadata: PropTypes.func, + user: PropTypes.object, + sidebarExpanded: PropTypes.bool, + hidden: PropTypes.bool, + drawStops: PropTypes.bool // whether to draw stops or not (based on zoom level) +} diff --git a/src/main/client/editor/reducers/data.js b/src/main/client/editor/reducers/data.js new file mode 100644 index 000000000..a709bb575 --- /dev/null +++ b/src/main/client/editor/reducers/data.js @@ -0,0 +1,461 @@ +import update from 'react-addons-update' +import clone from 'clone' +import ll from 'lonlng' + +import { stopToGtfs, routeToGtfs, agencyToGtfs, calendarToGtfs, fareToGtfs, gtfsSort } from '../util/gtfs' +import { getStopsForPattern } from '../util' + +const defaultState = { + active: {}, + tables: {} +} +const data = (state = defaultState, action) => { + let stateUpdate, key, newTableData, activeEntity, activeSubEntity, newState, routeIndex, stopIndex, patternIndex, agencyIndex, fareIndex, calendarIndex, scheduleExceptionIndex, activePattern + switch (action.type) { + case 'REQUESTING_FEED_INFO': + if (state.feedSourceId && action.feedId !== state.feedSourceId) { + return defaultState + } + return state + case 'CREATE_GTFS_ENTITY': + if (action.component === 'trippattern') { + activeEntity = { + isCreating: true, + name: '', + id: 'new', + feedId: action.feedSourceId, + ...action.props + } + routeIndex = state.tables.route.findIndex(r => r.id === action.props.routeId) + return update(newState || state, { + tables: {route: {[routeIndex]: {tripPatterns: {$unshift: [activeEntity]}}}}, + active: { + entity: {tripPatterns: {$unshift: [activeEntity]}} + // edited: {$set: typeof action.props !== 'undefined'} + } + }) + } else { + activeEntity = { + isCreating: true, + name: '', + id: 'new', + feedId: action.feedSourceId, + ...action.props + } + // if tables's component array is undefined, add it + if (!state.tables[action.component]) { + newState = update(state, { + tables: {[action.component]: {$set: []}} + }) + } + return update(newState || state, { + tables: {[action.component]: {$unshift: [activeEntity]}} + // active: { + // entity: {$set: activeEntity}, + // edited: {$set: typeof action.props !== 'undefined'} + // } + }) + } + case 'SETTING_ACTIVE_GTFS_ENTITY': + activeEntity = action.component === 'feedinfo' + ? clone(state.tables[action.component]) + : state.tables[action.component] && action.entityId + ? clone(state.tables[action.component].find(e => e.id === action.entityId)) + : null + switch (action.subComponent) { + case 'trippattern': + activeSubEntity = activeEntity && activeEntity.tripPatterns + ? clone(activeEntity.tripPatterns.find(p => p.id === action.subEntityId)) + : null + if (activeSubEntity) { + activeSubEntity.stops = clone(getStopsForPattern(activeSubEntity, state.tables.stop)) + } + break + } + let active = { + feedSourceId: action.feedSourceId, + entity: activeEntity, + entityId: action.entityId, + subEntity: activeSubEntity, + subEntityId: action.subEntityId, + subSubEntityId: action.subSubEntityId, + component: action.component, + subComponent: action.subComponent, + subSubComponent: action.subSubComponent, + edited: activeEntity && activeEntity.id === 'new' + } + return update(state, { + active: {$set: active} + }) + case 'RESET_ACTIVE_GTFS_ENTITY': + switch (action.tcomponent) { + case 'trippattern': + patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) + activeEntity = Object.assign({}, state.active.entity.tripPatterns[patternIndex]) + return update(state, { + active: { + subEntity: {$set: activeEntity}, + patternEdited: {$set: false} + } + }) + case 'feedinfo': + activeEntity = Object.assign({}, state.tables[action.component]) + return update(state, { + active: { + entity: {$set: activeEntity}, + edited: {$set: false} + } + }) + default: + activeEntity = state.tables[action.component].find(e => e.id === action.entity.id) + return update(state, { + active: { + entity: {$set: activeEntity}, + edited: {$set: false} + } + }) + } + case 'SAVED_TRIP_PATTERN': + routeIndex = state.tables.route.findIndex(r => r.id === action.tripPattern.routeId) + patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.tripPattern.id) + stateUpdate = {active: {}} + + // if pattern is active + if (action.tripPattern.id === state.active.subEntityId) { + stateUpdate = { + active: { + subEntity: {$set: action.tripPattern}, + patternEdited: {$set: false} + } + } + } + // if not active, but present in active route + if (patternIndex !== -1) { + stateUpdate.tables = {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}}}} + stateUpdate.active.entity = {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}} + } else if (action.tripPattern.routeId === state.active.entity.id) { // if pattern is entirely new + const patterns = clone(state.active.entity.tripPatterns) + patterns.push(action.tripPattern) + return update(state, { + tables: {route: {[routeIndex]: {tripPatterns: {$push: [action.tripPattern]}}}}, + active: {entity: {tripPatterns: {$set: patterns}}} + }) + } + return update(state, stateUpdate) + case 'UPDATING_ACTIVE_GTFS_ENTITY': + switch (action.component) { + case 'trippattern': + patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) + activeEntity = Object.assign({}, state.active.subEntity) + for (key in action.props) { + activeEntity[key] = action.props[key] + } + stateUpdate = { + active: { + subEntity: {$set: activeEntity}, + patternEdited: {$set: true} + } + } + return update(state, stateUpdate) + default: + activeEntity = Object.assign({}, state.active.entity) + for (key in action.props) { + activeEntity[key] = action.props[key] + } + return update(state, { + active: { + entity: {$set: activeEntity}, + edited: {$set: true} + } + }) + } + case 'RECEIVE_AGENCIES': + const agencies = action.agencies.map(agencyToGtfs) + agencies.sort(gtfsSort) + agencyIndex = state.active.entity && agencies.findIndex(a => a.id === state.active.entity.id) + if (agencyIndex !== -1) { + return update(state, { + tables: {agency: {$set: agencies}}, + active: { + entity: {$set: agencies[agencyIndex]}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {agency: {$set: agencies}} + }) + } + case 'RECEIVE_FARES': + const fares = action.fares.map(fareToGtfs) + fares.sort(gtfsSort) + fareIndex = state.active.entity && fares.findIndex(f => f.id === state.active.entity.id) + if (fareIndex !== -1) { + return update(state, { + tables: {fare: {$set: fares}}, + active: { + entity: {$set: fares[fareIndex]}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {fare: {$set: fares}} + }) + } + case 'RECEIVE_FEED_INFO': + const feedInfo = action.feedInfo + ? { + // datatools props + id: action.feedInfo.id, + color: action.feedInfo.color, + defaultLat: action.feedInfo.defaultLat, + defaultLon: action.feedInfo.defaultLon, + defaultRouteType: action.feedInfo.defaultRouteType, + + // gtfs spec props + feed_end_date: action.feedInfo.feedEndDate, + feed_start_date: action.feedInfo.feedStartDate, + feed_lang: action.feedInfo.feedLang, + feed_publisher_name: action.feedInfo.feedPublisherName, + feed_publisher_url: action.feedInfo.feedPublisherUrl, + feed_version: action.feedInfo.feedVersion + } + : { + // datatools props + id: null, + color: null, + defaultLat: null, + defaultLon: null, + defaultRouteType: null, + + // gtfs spec props + feed_end_date: null, + feed_start_date: null, + feed_lang: null, + feed_publisher_name: null, + feed_publisher_url: null, + feed_version: null + } + if (state.active.component === 'feedinfo') { + return update(state, { + tables: {feedinfo: {$set: feedInfo}}, + active: { + entity: {$set: feedInfo}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {feedinfo: {$set: feedInfo}} + }) + } + case 'RECEIVE_CALENDARS': + const calendars = action.calendars ? action.calendars.map(calendarToGtfs) : null + calendars.sort(gtfsSort) + calendarIndex = state.active.entity && calendars.findIndex(c => c.id === state.active.entity.id) + if (calendarIndex !== -1) { + return update(state, { + tables: {calendar: {$set: calendars}}, + active: { + entity: {$set: calendars[calendarIndex]}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {calendar: {$set: calendars}} + }) + } + case 'RECEIVE_SCHEDULE_EXCEPTIONS': + const scheduleExceptions = action.scheduleExceptions || [] // no mapping required + scheduleExceptionIndex = state.active.entity && action.scheduleExceptions.findIndex(se => se.id === state.active.entity.id) + if (scheduleExceptionIndex !== -1) { + return update(state, { + tables: {scheduleexception: {$set: scheduleExceptions}}, + active: { + entity: {$set: scheduleExceptions[scheduleExceptionIndex]}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {scheduleexception: {$set: scheduleExceptions}} + }) + } + case 'RECEIVE_ROUTES': + const routes = action.routes ? action.routes.map(routeToGtfs) : [] + routes.sort(gtfsSort) + routeIndex = state.active.entity && routes.findIndex(r => r.id === state.active.entity.id) + if (routeIndex !== -1) { + let activeRoute = routes[routeIndex] + if (state.active.entity && state.active.entity.tripPatterns) { + activeRoute.tripPatterns = clone(state.active.entity.tripPatterns) + } + return update(state, { + tables: {route: {$set: routes}}, + active: { + entity: {$set: activeRoute}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {route: {$set: routes}} + }) + } + case 'RECEIVE_TRIP_PATTERNS': + return update(state, { + tripPatterns: {$set: + Object.keys(action.tripPatterns).map(key => { + return { + id: key, + latLngs: action.tripPatterns[key].shape ? (action.tripPatterns[key].shape.coordinates.map(c => ll.fromCoordinates(c))) : null + } + }) + } + }) + case 'RECEIVE_TRIP_PATTERNS_FOR_ROUTE': + routeIndex = state.tables.route.findIndex(r => r.id === action.routeId) + activePattern = state.active.subEntityId && action.tripPatterns.find(p => p.id === state.active.subEntityId) + if (activePattern) { + activePattern.stops = getStopsForPattern(activePattern, state.tables.stop) + } + if (routeIndex === -1) { + return state + } + if (state.active.entity.id === action.routeId) { + return update(state, { + tables: {route: {[routeIndex]: {$merge: {tripPatterns: action.tripPatterns}}}}, + active: { + entity: {$merge: {tripPatterns: action.tripPatterns}}, + subEntity: {$set: Object.assign({}, activePattern)} + } + }) + } else { + return update(state, { + tables: {route: {[routeIndex]: {$merge: {tripPatterns: action.tripPatterns}}}} + }) + } + // case 'RECEIVE_TRIPS_FOR_CALENDAR': + // routeIndex = state.tables.route.findIndex(r => r.id === action.pattern.routeId) + // patternIndex = state.tables.route[routeIndex].tripPatterns.findIndex(p => p.id === action.pattern.id) + // // if (state.active.entity.id === action.pattern.routeId) { + // return update(state, { + // tables: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, + // active: {entity: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}} + // }) + // // } else { + // // return update(state, { + // // tables: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}} + // // }) + // // } + // case 'DELETED_TRIPS_FOR_CALENDAR': + // routeIndex = state.tables.route.findIndex(r => r.id === action.pattern.routeId) + // patternIndex = state.tables.route[routeIndex].tripPatterns.findIndex(p => p.id === action.pattern.id) + // let tripIndex = state.tables.route[routeIndex].tripPatterns[patternIndex][action.calendarId].findIndex(t => t.) + // if (state.active.entity.id === action.pattern.routeId) { + // return update(state, { + // tables: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, + // active: {entity: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}} + // }) + // } + // else { + // return update(state, { + // tables: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, + // }) + // } + case 'RECEIVE_STOPS': + const stops = action.stops ? action.stops.map(stopToGtfs) : [] + stops.sort(gtfsSort) + stopIndex = state.active.entity && stops.findIndex(s => s.id === state.active.entity.id) + if (stopIndex !== -1) { + return update(state, { + tables: {stop: {$set: stops}}, + active: { + entity: {$set: stops[stopIndex]}, + edited: {$set: false} + } + }) + } else { + return update(state, { + tables: {stop: {$set: stops}} + }) + } + case 'DELETING_STOP': + stopIndex = state.tables.stop.findIndex(s => s.id === action.stop.id) + return update(state, { + tables: {stop: {$splice: [[stopIndex, 1]]}} + }) + case 'RECEIVE_STOP': + const stop = stopToGtfs(action.stop) + stopIndex = state.tables.stop.findIndex(s => s.id === stop.id) + + // TODO: handle adding to rbush tree + // TODO: updated sort with stops array + + // if stop is active, update active entity + if (stop.id === state.active.entityId && stopIndex !== -1) { + stateUpdate = { + tables: {stop: {[stopIndex]: {$set: stop}}}, + active: { + entity: {$set: stop}, + edited: {$set: false} + } + } + } else if (stopIndex === -1) { + stateUpdate = { + tables: {stop: {$push: [stop]}} + } + } else { + stateUpdate = { + tables: {stop: {[stopIndex]: {$set: stop}}} + } + } + return update(state, stateUpdate) + case 'CLEAR_GTFSEDITOR_CONTENT': + return defaultState + case 'RECEIVE_GTFSEDITOR_TABLE': + newTableData = {} + const getMappedEntities = (entities) => { + switch (action.tableId) { + case 'agency': + return action.entities.map(agencyToGtfs) + case 'route': + return action.entities.map(routeToGtfs) + case 'stop': + return action.entities.map(stopToGtfs) + case 'calendar': + return action.entities.map(calendarToGtfs) + case 'fare': + return action.entities.map(fareToGtfs) // no mapping exists for fares + default: + return action.entities + } + } + newTableData[action.tableId] = getMappedEntities(action.entities) + return update(state, { + tables: {$merge: newTableData} + }) + case 'RECEIVE_GTFS_ENTITIES': + const getType = function (entity) { + if (entity.hasOwnProperty('route_id')) return 'route' + if (entity.hasOwnProperty('stop_id')) return 'stop' + } + const newLookupEntries = {} + for (let entity of action.gtfsEntities) { + const type = getType(entity) + const key = type + '_' + entity[type + '_id'] + newLookupEntries[key] = entity + } + return update(state, + {gtfsEntityLookup: + {$merge: newLookupEntries} + } + ) + default: + return state + } +} + +export default data diff --git a/src/main/client/editor/reducers/editor.js b/src/main/client/editor/reducers/editor.js deleted file mode 100644 index e89f6fd15..000000000 --- a/src/main/client/editor/reducers/editor.js +++ /dev/null @@ -1,851 +0,0 @@ -import update from 'react-addons-update' -import rbush from 'rbush' -import clone from 'clone' -import objectPath from 'object-path' -import { getControlPoints, getEntityBounds, stopToGtfs, routeToGtfs, agencyToGtfs, calendarToGtfs, fareToGtfs, gtfsSort } from '../util/gtfs' -import { latLngBounds } from 'leaflet' -import ll from 'lonlng' -import { CLICK_OPTIONS, getTimetableColumns } from '../util' - -const defaultState = { - feedSourceId: null, - active: {}, - editSettings: { - editGeometry: false, - followStreets: true, - onMapClick: CLICK_OPTIONS[0], - stopInterval: 400, - distanceFromIntersection: 5, - afterIntersection: true, - intersectionStep: 2, - snapToStops: true, - addStops: false, - hideStops: false, - controlPoints: [], - coordinatesHistory: [], - actions: [] - }, - timetable: { - columns: [], - trips: [], - edited: [], - selected: [], - hideDepartureTimes: false, - offset: null - }, - mapState: { - zoom: null, - bounds: latLngBounds([[60, 60], [-60, -20]]), - target: null - }, - tableData: {}, - stopTree: null, - validation: null -} -const editor = (state = defaultState, action) => { - let stateUpdate, key, newTableData, fields, rowData, mappedEntities, activeEntity, activeSubEntity, newState, routeIndex, stopIndex, patternIndex, agencyIndex, fareIndex, calendarIndex, scheduleExceptionIndex, controlPoints, coordinates, mapState, activePattern, sortedTrips, columns, trips - switch (action.type) { - case 'REQUESTING_FEED_INFO': - if (state.feedSourceId && action.feedId !== state.feedSourceId) { - return defaultState - } - return state - case 'UPDATE_MAP_SETTING': - mapState = {...state.mapState} - for (key in action.props) { - mapState[key] = action.props[key] - } - if (!('target' in action.props)) { - mapState.target = null - } - return update(state, { - mapState: {$set: mapState} - }) - case 'UPDATE_EDIT_SETTING': - if (action.setting === 'editGeometry' && !state.editSettings.editGeometry) { - controlPoints = getControlPoints(state.active.subEntity, state.editSettings.snapToStops) - return update(state, { - editSettings: { - [action.setting]: {$set: action.value}, - controlPoints: {$set: [controlPoints]} - } - }) - } else { - return update(state, { - editSettings: { - [action.setting]: {$set: action.value} - } - }) - } - case 'UNDO_TRIP_PATTERN_EDITS': - patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === state.active.subEntityId) - let lastActionIndex = state.editSettings.actions.length - 1 - let lastActionType = state.editSettings.actions[lastActionIndex] - let lastCoordinatesIndex = state.editSettings.coordinatesHistory.length - 1 - let lastControlPointsIndex = state.editSettings.controlPoints.length - 1 - stateUpdate = { - editSettings: { - // coordinatesHistory: {$splice: [[lastEditIndex, 1]]}, - // controlPoints: {$splice: [[lastEditIndex, 1]]}, - actions: {$splice: [[lastActionIndex, 1]]} - } - } - switch (lastActionType) { - case 'ADD_CONTROL_POINT': - stateUpdate.editSettings.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} - break - case 'UPDATE_CONTROL_POINT': - stateUpdate.editSettings.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} - stateUpdate.editSettings.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} - coordinates = state.editSettings.coordinatesHistory[lastCoordinatesIndex] - if (coordinates) { - stateUpdate.active = { - subEntity: {shape: {coordinates: {$set: coordinates}}} - } - } - break - case 'REMOVE_CONTROL_POINT': - stateUpdate.editSettings.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} - stateUpdate.editSettings.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} - coordinates = state.editSettings.coordinatesHistory[lastCoordinatesIndex] - if (coordinates) { - stateUpdate.active = { - subEntity: {shape: {coordinates: {$set: coordinates}}} - } - } - break - } - return update(state, stateUpdate) - case 'ADD_CONTROL_POINT': - controlPoints = [...state.editSettings.controlPoints[state.editSettings.controlPoints.length - 1]] - controlPoints.splice(action.index, 0, action.controlPoint) - return update(state, { - editSettings: { - controlPoints: {$push: [controlPoints]}, - actions: {$push: [action.type]} - } - }) - case 'REMOVE_CONTROL_POINT': - controlPoints = [...state.editSettings.controlPoints[state.editSettings.controlPoints.length - 1]] - controlPoints.splice(action.index, 1) - return update(state, { - editSettings: { - controlPoints: {$push: [controlPoints]}, - actions: {$push: [action.type]} - } - }) - case 'UPDATE_CONTROL_POINT': - let newControlPoints = [] - controlPoints = state.editSettings.controlPoints[state.editSettings.controlPoints.length - 1] - for (var i = 0; i < controlPoints.length; i++) { - newControlPoints.push(Object.assign({}, controlPoints[i])) - } - let newest = update(newControlPoints, {[action.index]: {point: {$set: action.point}, distance: {$set: action.distance}}}) - return update(state, { - editSettings: { - controlPoints: {$push: [newest]}, - actions: {$push: [action.type]} - } - }) - case 'RECEIVED_ROUTES_SHAPEFILE': - return update(state, { - mapState: { - routesGeojson: {$set: action.geojson} - } - }) - case 'CREATE_GTFS_ENTITY': - if (action.component === 'trippattern') { - activeEntity = { - isCreating: true, - name: '', - id: 'new', - feedId: action.feedSourceId, - ...action.props - } - routeIndex = state.tableData.route.findIndex(r => r.id === action.props.routeId) - return update(newState || state, { - tableData: {route: {[routeIndex]: {tripPatterns: {$unshift: [activeEntity]}}}}, - active: { - entity: {tripPatterns: {$unshift: [activeEntity]}} - // edited: {$set: typeof action.props !== 'undefined'} - } - }) - } else { - activeEntity = { - isCreating: true, - name: '', - id: 'new', - feedId: action.feedSourceId, - ...action.props - } - // if tableData's component array is undefined, add it - if (!state.tableData[action.component]) { - newState = update(state, { - tableData: {[action.component]: {$set: []}} - }) - } - return update(newState || state, { - tableData: {[action.component]: {$unshift: [activeEntity]}} - // active: { - // entity: {$set: activeEntity}, - // edited: {$set: typeof action.props !== 'undefined'} - // } - }) - } - case 'SETTING_ACTIVE_GTFS_ENTITY': - activeEntity = action.component === 'feedinfo' - ? clone(state.tableData[action.component]) - : state.tableData[action.component] && action.entityId - ? clone(state.tableData[action.component].find(e => e.id === action.entityId)) - : null - switch (action.subComponent) { - case 'trippattern': - activeSubEntity = activeEntity && activeEntity.tripPatterns - ? clone(activeEntity.tripPatterns.find(p => p.id === action.subEntityId)) - : null - controlPoints = getControlPoints(activeSubEntity, state.editSettings.snapToStops) - coordinates = activeSubEntity && activeSubEntity.shape && activeSubEntity.shape.coordinates - - // set timetable trips (if in timetable editor) - if (action.subSubComponent === 'timetable' && activeSubEntity) { - routeIndex = state.tableData.route.findIndex(r => r.id === action.entityId) - patternIndex = state.tableData.route[routeIndex].tripPatterns.findIndex(p => p.id === action.subEntityId) - activePattern = clone(state.tableData.route[routeIndex].tripPatterns[patternIndex]) - trips = clone(activePattern[action.subSubEntityId]) - sortedTrips = trips - ? trips.filter(t => t.useFrequency === activePattern.useFrequency) // filter out based on useFrequency - .sort((a, b) => { - if (a.stopTimes[0].departureTime < b.stopTimes[0].departureTime) return -1 - if (a.stopTimes[0].departureTime > b.stopTimes[0].departureTime) return 1 - return 0 - }) - : [] - columns = getTimetableColumns(activePattern, state.tableData.stop, state.timetable.hideDepartureTimes) - return update(state, { - timetable: { - trips: {$set: sortedTrips}, - columns: {$set: columns}, - edited: {$set: []}, - selected: {$set: []} - } - }) - } - break - } - let active = { - feedSourceId: action.feedSourceId, - entity: activeEntity, - entityId: action.entityId, - subEntity: activeSubEntity, - subEntityId: action.subEntityId, - component: action.component, - subComponent: action.subComponent, - subSubComponent: action.subSubComponent, - edited: activeEntity && activeEntity.id === 'new' - } - stateUpdate = { - editSettings: { - controlPoints: {$set: controlPoints} - }, - active: {$set: active} - } - if (coordinates) { - stateUpdate.coordinatesHistory = {$set: [coordinates]} - } - return update(state, stateUpdate) - case 'RESET_ACTIVE_GTFS_ENTITY': - switch (action.component) { - case 'trippattern': - patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) - activeEntity = Object.assign({}, state.active.entity.tripPatterns[patternIndex]) - return update(state, { - active: { - subEntity: {$set: activeEntity}, - patternEdited: {$set: false} - } - }) - case 'feedinfo': - activeEntity = Object.assign({}, state.tableData[action.component]) - return update(state, { - active: { - entity: {$set: activeEntity}, - edited: {$set: false} - } - }) - default: - activeEntity = state.tableData[action.component].find(e => e.id === action.entity.id) - return update(state, { - active: { - entity: {$set: activeEntity}, - edited: {$set: false} - } - }) - } - case 'SAVED_TRIP_PATTERN': - routeIndex = state.tableData.route.findIndex(r => r.id === action.tripPattern.routeId) - patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.tripPattern.id) - stateUpdate = {active: {}} - - // if pattern is active - if (action.tripPattern.id === state.active.subEntityId) { - // set controlPoints initially and then whenever isSnappingToStops changes - controlPoints = getControlPoints(action.tripPattern, state.editSettings.snapToStops) - - stateUpdate = { - active: { - subEntity: {$set: action.tripPattern}, - patternEdited: {$set: false} - }, - editSettings: {controlPoints: {$set: [controlPoints]}} - } - } - // if not active, but present in active route - if (patternIndex !== -1) { - stateUpdate.tableData = {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}}}} - stateUpdate.active.entity = {tripPatterns: {[patternIndex]: {$set: action.tripPattern}}} - } else if (action.tripPattern.routeId === state.active.entity.id) { // if pattern is entirely new - const patterns = clone(state.active.entity.tripPatterns) - patterns.push(action.tripPattern) - return update(state, { - tableData: {route: {[routeIndex]: {tripPatterns: {$push: [action.tripPattern]}}}}, - // active: {entity: {tripPatterns: {$push: [action.tripPattern]}}} - active: {entity: {tripPatterns: {$set: patterns}}} - }) - } - return update(state, stateUpdate) - case 'UPDATE_ACTIVE_GTFS_ENTITY': - switch (action.component) { - case 'trippattern': - patternIndex = state.active.entity.tripPatterns.findIndex(p => p.id === action.entity.id) - activeEntity = Object.assign({}, state.active.subEntity) - for (key in action.props) { - activeEntity[key] = action.props[key] - } - stateUpdate = { - active: { - // entity: {tripPatterns: {[patternIndex]: {$set: activeEntity}}}, - subEntity: {$set: activeEntity}, - patternEdited: {$set: true} - } - } - if (action.props && 'shape' in action.props) { - // add previous coordinates to history - // stateUpdate.editSettings = {coordinatesHistory: {$push: [action.props.shape.coordinates]}} - coordinates = state.active.subEntity.shape && state.active.subEntity.shape.coordinates - if (coordinates) - stateUpdate.editSettings = {coordinatesHistory: {$push: [coordinates]}} - } - return update(state, stateUpdate) - // case 'feedinfo': - // activeEntity = Object.assign({}, state.active.entity) - // case 'timetable': - // activeEntity = Object.assign({}, state.active.entity) - // patternIndex = activeEntity.tripPatterns.findIndex(p => p.id === action.entity.id) - // for (key in action.props) { - // activeEntity.tripPatterns[patternIndex][key] = action.props[key] - // } - // return update(state, { - // active: {entity: {$set: activeEntity}}, - // edited: {$set: true} - // }) - default: - activeEntity = Object.assign({}, state.active.entity) - for (key in action.props) { - activeEntity[key] = action.props[key] - } - return update(state, { - active: { - entity: {$set: activeEntity}, - edited: {$set: true} - }, - }) - } - case 'RECEIVE_AGENCIES': - const agencies = action.agencies.map(agencyToGtfs) - agencies.sort(gtfsSort) - agencyIndex = state.active.entity && agencies.findIndex(a => a.id === state.active.entity.id) - if (agencyIndex !== -1) { - return update(state, { - tableData: {agency: {$set: agencies}}, - active: { - entity: {$set: agencies[agencyIndex]}, - edited: {$set: false} - } - }) - } else { - return update(state, { - tableData: {agency: {$set: agencies}} - }) - } - case 'RECEIVE_FARES': - const fares = action.fares.map(fareToGtfs) - fares.sort(gtfsSort) - fareIndex = state.active.entity && fares.findIndex(f => f.id === state.active.entity.id) - if (fareIndex !== -1) { - return update(state, { - tableData: {fare: {$set: fares}}, - active: { - entity: {$set: fares[fareIndex]}, - edited: {$set: false} - } - }) - } else { - return update(state, { - tableData: {fare: {$set: fares}} - }) - } - case 'RECEIVE_FEED_INFO': - const feedInfo = action.feedInfo - ? { - // datatools props - id: action.feedInfo.id, - color: action.feedInfo.color, - defaultLat: action.feedInfo.defaultLat, - defaultLon: action.feedInfo.defaultLon, - defaultRouteType: action.feedInfo.defaultRouteType, - - // gtfs spec props - feed_end_date: action.feedInfo.feedEndDate, - feed_start_date: action.feedInfo.feedStartDate, - feed_lang: action.feedInfo.feedLang, - feed_publisher_name: action.feedInfo.feedPublisherName, - feed_publisher_url: action.feedInfo.feedPublisherUrl, - feed_version: action.feedInfo.feedVersion - } - : { - // datatools props - id: null, - color: null, - defaultLat: null, - defaultLon: null, - defaultRouteType: null, - - // gtfs spec props - feed_end_date: null, - feed_start_date: null, - feed_lang: null, - feed_publisher_name: null, - feed_publisher_url: null, - feed_version: null - } - let mapState = {...state.mapState} - if (feedInfo && feedInfo.defaultLon && feedInfo.defaultLat) { - mapState.bounds = getEntityBounds([feedInfo.defaultLon, feedInfo.defaultLat], 0.5) - mapState.target = feedInfo.id - } - - if (state.active.component === 'feedinfo') { - return update(state, { - tableData: {feedinfo: {$set: feedInfo}}, - active: { - entity: {$set: feedInfo}, - edited: {$set: false} - }, - mapState: {$set: mapState} - }) - } else { - return update(state, { - tableData: {feedinfo: {$set: feedInfo}}, - mapState: {$set: mapState} - }) - } - case 'RECEIVE_CALENDARS': - const calendars = action.calendars ? action.calendars.map(calendarToGtfs) : null - calendars.sort(gtfsSort) - calendarIndex = state.active.entity && calendars.findIndex(c => c.id === state.active.entity.id) - if (calendarIndex !== -1) { - return update(state, { - tableData: {calendar: {$set: calendars}}, - active: { - entity: {$set: calendars[calendarIndex]}, - edited: {$set: false} - } - }) - } else { - return update(state, { - tableData: {calendar: {$set: calendars}} - }) - } - case 'RECEIVE_SCHEDULE_EXCEPTIONS': - const scheduleExceptions = action.scheduleExceptions ? action.scheduleExceptions.map(se => { - return { - // datatools props - id: se.id, - name: se.name, - feedId: se.feedId, - exemplar: se.exemplar, - dates: se.dates, - customSchedule: se.customSchedule, - addedService: se.addedService, - removedService: se.removedService - - // gtfs spec props - // gtfs_prop: se.gtfs_prop - } - }) : [] - scheduleExceptionIndex = state.active.entity && action.scheduleExceptions.findIndex(se => se.id === state.active.entity.id) - if (scheduleExceptionIndex !== -1) { - return update(state, { - tableData: {scheduleexception: {$set: scheduleExceptions}}, - active: { - entity: {$set: scheduleExceptions[scheduleExceptionIndex]}, - edited: {$set: false} - } - }) - } else { - return update(state, { - tableData: {scheduleexception: {$set: scheduleExceptions}} - }) - } - case 'RECEIVE_ROUTES': - const routes = action.routes ? action.routes.map(routeToGtfs) : [] - routes.sort(gtfsSort) - // feedTableData.route = routes - routeIndex = state.active.entity && routes.findIndex(r => r.id === state.active.entity.id) - if (routeIndex !== -1) { - let activeRoute = routes[routeIndex] - if (state.active.entity && state.active.entity.tripPatterns) { - activeRoute.tripPatterns = clone(state.active.entity.tripPatterns) - } - let followStreets = activeRoute ? activeRoute.route_type === 3 || activeRoute.route_type === 0 : true - return update(state, { - tableData: {route: {$set: routes}}, - active: { - entity: {$set: activeRoute}, - edited: {$set: false} - }, - editSettings: { - followStreets: {$set: followStreets} - } - }) - } else { - return update(state, { - tableData: {route: {$set: routes}} - }) - } - case 'RECEIVE_TRIP_PATTERNS': - return update(state, { - tripPatterns: {$set: - Object.keys(action.tripPatterns).map(key => { - return { - id: key, - latLngs: action.tripPatterns[key].shape ? (action.tripPatterns[key].shape.coordinates.map(c => ll.fromCoordinates(c))) : null - } - }) - } - }) - case 'RECEIVE_TRIP_PATTERNS_FOR_ROUTE': - routeIndex = state.tableData.route.findIndex(r => r.id === action.routeId) - activePattern = state.active.subEntityId && action.tripPatterns.find(p => p.id === state.active.subEntityId) - if (routeIndex === -1) { - return state - } - // set controlPoints initially and then whenever isSnappingToStops changes - if (activePattern) { - controlPoints = getControlPoints(activePattern, state.editSettings.snapToStops) - } else { - controlPoints = [] - } - if (state.active.entity.id === action.routeId) { - return update(state, { - tableData: {route: {[routeIndex]: {$merge: {tripPatterns: action.tripPatterns}}}}, - active: { - entity: {$merge: {tripPatterns: action.tripPatterns}}, - subEntity: {$set: Object.assign({}, activePattern)} - }, - editSettings: {controlPoints: {$set: [controlPoints]}} - }) - } else { - return update(state, { - tableData: {route: {[routeIndex]: {$merge: {tripPatterns: action.tripPatterns}}}} - }) - } - case 'SET_TIMETABLE_OFFSET': - return update(state, { - timetable: { - offset: {$set: action.seconds} - } - }) - case 'UPDATE_TIMETABLE_CELL_VALUE': - trips = clone(state.timetable.trips) - objectPath.set(trips, action.key, action.value) - return update(state, { - timetable: { - trips: {$set: trips}, - edited: {$push: [action.rowIndex]} - } - }) - case 'TOGGLE_ALL_TIMETABLE_ROW_SELECTION': - let selected = [] - if (action.select) { - for (let i = 0; i < state.timetable.trips.length; i++) { - selected.push(i) - } - } - return update(state, { - timetable: { - selected: {$set: selected} - } - }) - case 'TOGGLE_DEPARTURE_TIMES': - return update(state, { - timetable: { - hideDepartureTimes: {$set: !state.timetable.hideDepartureTimes} - } - }) - case 'ADD_NEW_TRIP': - return update(state, { - timetable: { - trips: {$push: [action.trip]}, - edited: {$push: [state.timetable.trips.length]} - } - }) - case 'TOGGLE_SINGLE_TIMETABLE_ROW_SELECTION': - let selectIndex = state.timetable.selected.indexOf(action.rowIndex) - if (selectIndex === -1) { - return update(state, { - timetable: { - selected: {$push: [action.rowIndex]} - } - }) - } - else { - return update(state, { - timetable: { - selected: {$splice: [[selectIndex, 1]]} - } - }) - } - case 'RECEIVE_TRIPS_FOR_CALENDAR': - routeIndex = state.tableData.route.findIndex(r => r.id === action.pattern.routeId) - patternIndex = state.tableData.route[routeIndex].tripPatterns.findIndex(p => p.id === action.pattern.id) - activePattern = clone(state.tableData.route[routeIndex].tripPatterns[patternIndex]) - trips = clone(action.trips) - sortedTrips = trips - ? action.trips.filter(t => t.useFrequency === activePattern.useFrequency) // filter out based on useFrequency - .sort((a, b) => { - // if (a.isCreating && !b.isCreating) return -1 - // if (!a.isCreating && b.isCreating) return 1 - if (a.stopTimes[0].departureTime < b.stopTimes[0].departureTime) return -1 - if (a.stopTimes[0].departureTime > b.stopTimes[0].departureTime) return 1 - return 0 - }) - : [] - columns = getTimetableColumns(activePattern, state.tableData.stop, state.timetable.hideDepartureTimes) - if (state.active.entity.id === action.pattern.routeId) { - return update(state, { - tableData: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, - active: {entity: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}, - timetable: { - trips: {$set: sortedTrips}, - patternId: {$set: activePattern.id}, - calendarId: {$set: action.calendarId}, - columns: {$set: columns}, - edited: {$set: []} - } - }) - } - else { - return update(state, { - tableData: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, - }) - } - // case 'DELETED_TRIPS_FOR_CALENDAR': - // routeIndex = state.tableData.route.findIndex(r => r.id === action.pattern.routeId) - // patternIndex = state.tableData.route[routeIndex].tripPatterns.findIndex(p => p.id === action.pattern.id) - // let tripIndex = state.tableData.route[routeIndex].tripPatterns[patternIndex][action.calendarId].findIndex(t => t.) - // if (state.active.entity.id === action.pattern.routeId) { - // return update(state, { - // tableData: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, - // active: {entity: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}} - // }) - // } - // else { - // return update(state, { - // tableData: {route: {[routeIndex]: {tripPatterns: {[patternIndex]: {$merge: {[action.calendarId]: action.trips}}}}}}, - // }) - // } - case 'RECEIVE_STOPS': - const stops = action.stops ? action.stops.map(stopToGtfs) : [] - stops.sort(gtfsSort) - const tree = rbush(9, ['[0]', '[1]', '[0]', '[1]']) - tree.load(stops.map(s => ([s.stop_lon, s.stop_lat, s]))) - stopIndex = state.active.entity && stops.findIndex(s => s.id === state.active.entity.id) - if (stopIndex !== -1) { - return update(state, { - tableData: {stop: {$set: stops}}, - stopTree: {$set: tree}, - active: { - entity: {$set: stops[stopIndex]}, - edited: {$set: false} - } - }) - } else { - return update(state, { - tableData: {stop: {$set: stops}}, - stopTree: {$set: tree} - }) - } - case 'DELETING_STOP': - stopIndex = state.tableData.stop.findIndex(s => s.id === action.stop.id) - return update(state, { - tableData: {stop: {$splice: [[stopIndex, 1]]}} - }) - case 'RECEIVE_STOP': - const stop = stopToGtfs(action.stop) - stopIndex = state.tableData.stop.findIndex(s => s.id === stop.id) - - // TODO: handle adding to rbush tree - // TODO: updated sort with stops array - - // if stop is active, update active entity - if (stop.id === state.active.entityId && stopIndex !== -1) { - stateUpdate = { - tableData: {stop: {[stopIndex]: {$set: stop}}}, - active: { - entity: {$set: stop}, - edited: {$set: false} - } - } - } else if (stopIndex === -1) { - stateUpdate = { - tableData: {stop: {$push: [stop]}} - } - } else { - stateUpdate = { - tableData: {stop: {[stopIndex]: {$set: stop}}} - } - } - return update(state, stateUpdate) - case 'CLEAR_GTFSEDITOR_CONTENT': - return defaultState - case 'RECEIVE_GTFSEDITOR_TABLE': - newTableData = {} - const getMappedEntities = (entities) => { - switch (action.tableId) { - case 'agency': - return action.entities.map(agencyToGtfs) - case 'route': - return action.entities.map(routeToGtfs) - case 'stop': - return action.entities.map(stopToGtfs) - case 'calendar': - return action.entities.map(calendarToGtfs) - case 'fare': - return action.entities.map(fareToGtfs) // no mapping exists for fares - default: - return action.entities - } - } - - newTableData[action.tableId] = getMappedEntities(action.entities) - - return update(state, { - tableData: {$merge: newTableData} - }) - case 'RECEIVE_GTFSEDITOR_CONTENT': - newTableData = {} - for(let i = 0; i < action.filenames.length; i++) { - const lines = action.fileContent[i].split('\n') - if (lines.length < 2) continue - fields = lines[0].split(',') - newTableData[action.filenames[i].split('.')[0]] = lines.slice(1) - .filter(line => line.split(',').length === fields.length) - .map((line, rowIndex) => { - const values = line.split(',') - rowData = { origRowIndex: rowIndex } - for(let f = 0; f < fields.length; f++) { - rowData[fields[f]] = values[f] - } - return rowData - }) - } - return update(state, { - feedSourceId: {$set: action.feedSourceId}, - tableData: {$set: newTableData} - }) - - case 'ADD_GTFSEDITOR_ROW': - // create this table if it doesn already exist - if (!(action.tableId in state.tableData)) { - return update(state, - {tableData: - {$merge: {[action.tableId]: [action.rowData]} } - } - ) - } - // otherwise, add it to the exising table - return update(state, - {tableData: - {[action.tableId]: - {$push: [action.rowData]} - } - } - ) - - case 'UPDATE_GTFSEDITOR_FIELD': - return update(state, - {tableData: - {[action.tableId]: - {[action.rowIndex]: - {[action.fieldName]: - {$set: action.newValue} - } - } - } - } - ) - - case 'DELETE_GTFSEDITOR_ROW': - const table = state.tableData[action.tableId] - const newTable = [ - ...table.slice(0, action.rowIndex), - ...table.slice(action.rowIndex + 1) - ] - return update(state, - {tableData: - {[action.tableId]: - {$set: newTable} - } - } - ) - - case 'RECEIVE_GTFS_ENTITIES': - const getType = function (entity) { - if (entity.hasOwnProperty('route_id')) return 'route' - if (entity.hasOwnProperty('stop_id')) return 'stop' - } - - const newLookupEntries = {} - for(const entity of action.gtfsEntities) { - const type = getType(entity) - const key = type + '_' + entity[type+'_id'] - newLookupEntries[key] = entity - } - - return update(state, - {gtfsEntityLookup: - {$merge: newLookupEntries} - } - ) - - case 'RECEIVE_GTFSEDITOR_VALIDATION': - const validationTable = {} - for (const issue of action.validationIssues) { - if (!(issue.tableId in validationTable)) { - validationTable[issue.tableId] = [] - } - validationTable[issue.tableId].push(issue) - } - return update(state, - {validation: - {$set: validationTable} - } - ) - - default: - return state - } -} - -export default editor diff --git a/src/main/client/editor/reducers/index.js b/src/main/client/editor/reducers/index.js index aa8561b51..858a69237 100644 --- a/src/main/client/editor/reducers/index.js +++ b/src/main/client/editor/reducers/index.js @@ -1,3 +1,13 @@ -module.exports = { - editor: require('./editor') -} +import { combineReducers } from 'redux' + +import data from './data' +import editSettings from './settings' +import mapState from './mapState' +import timetable from './timetable' + +export default combineReducers({ + data, + editSettings, + mapState, + timetable +}) diff --git a/src/main/client/editor/reducers/mapState.js b/src/main/client/editor/reducers/mapState.js new file mode 100644 index 000000000..3b6a3f509 --- /dev/null +++ b/src/main/client/editor/reducers/mapState.js @@ -0,0 +1,49 @@ +import update from 'react-addons-update' +import { latLngBounds } from 'leaflet' +import rbush from 'rbush' + +import { getEntityBounds } from '../util/gtfs' + +const defaultState = { + zoom: null, + bounds: latLngBounds([[60, 60], [-60, -20]]), + target: null, + stopTree: null +} + +const mapState = (state = defaultState, action) => { + let updatedState + switch (action.type) { + case 'RECEIVE_FEED_INFO': + if (action.feedInfo && action.feedInfo.defaultLon && action.feedInfo.defaultLat) { + return update(state, { + bounds: {$set: getEntityBounds([action.feedInfo.defaultLon, action.feedInfo.defaultLat], 0.5)}, + target: {$set: action.feedInfo.id} + }) + } + break + case 'RECEIVE_STOPS': + const tree = rbush(9, ['[0]', '[1]', '[0]', '[1]']) + tree.load(action.stops.map(s => ([s.stop_lon, s.stop_lat, s]))) + return update(state, { + stopTree: {$set: tree} + }) + case 'RECEIVED_ROUTES_SHAPEFILE': + return update(state, { + routesGeojson: {$set: action.geojson} + }) + case 'UPDATE_MAP_SETTING': + updatedState = {} + for (let key in action.props) { + updatedState[key] = {$set: action.props[key]} + } + if (!('target' in action.props)) { + updatedState.target = {$set: null} + } + return update(state, updatedState) + default: + return state + } +} + +export default mapState diff --git a/src/main/client/editor/reducers/settings.js b/src/main/client/editor/reducers/settings.js new file mode 100644 index 000000000..a5ff78862 --- /dev/null +++ b/src/main/client/editor/reducers/settings.js @@ -0,0 +1,148 @@ +import update from 'react-addons-update' + +import { getControlPoints } from '../util/gtfs' +import { CLICK_OPTIONS } from '../util' + +const defaultState = { + editGeometry: false, + followStreets: true, + onMapClick: CLICK_OPTIONS[0], + stopInterval: 400, + distanceFromIntersection: 5, + afterIntersection: true, + intersectionStep: 2, + snapToStops: true, + addStops: false, + hideStops: false, + controlPoints: [], + coordinatesHistory: [], + actions: [] +} + +const editSettings = (state = defaultState, action) => { + let stateUpdate, controlPoints, coordinates + switch (action.type) { + case 'SETTING_ACTIVE_GTFS_ENTITY': + switch (action.subComponent) { + case 'trippattern': + controlPoints = getControlPoints(action.activeSubEntity, state.snapToStops) + coordinates = action.activeSubEntity && action.activeSubEntity.shape && action.activeSubEntity.shape.coordinates + stateUpdate = { + controlPoints: {$set: controlPoints} + } + if (coordinates) { + stateUpdate.coordinatesHistory = {$set: [coordinates]} + } + return update(state, stateUpdate) + default: + return state + } + case 'UPDATING_ACTIVE_GTFS_ENTITY': + if (action.props && 'shape' in action.props) { + // add previous coordinates to history + coordinates = action.active.subEntity.shape && action.active.subEntity.shape.coordinates + if (coordinates) { + return update(state, { + coordinatesHistory: {$push: [coordinates]} + }) + } + } + break + case 'RECEIVE_TRIP_PATTERNS_FOR_ROUTE': + // set controlPoints initially and then whenever isSnappingToStops changes + if (action.activePattern) { + controlPoints = getControlPoints(action.activePattern, state.snapToStops) + } else { + controlPoints = [] + } + return update(state, { + controlPoints: {$set: [controlPoints]} + }) + case 'UPDATE_EDIT_SETTING': + if (action.setting === 'editGeometry' && !state.editGeometry) { + controlPoints = getControlPoints(action.activePattern, state.snapToStops) + return update(state, { + [action.setting]: {$set: action.value}, + controlPoints: {$set: [controlPoints]} + }) + } else { + return update(state, { + [action.setting]: {$set: action.value} + }) + } + case 'UNDO_TRIP_PATTERN_EDITS': + let lastActionIndex = state.actions.length - 1 + let lastActionType = state.actions[lastActionIndex] + let lastCoordinatesIndex = state.coordinatesHistory.length - 1 + let lastControlPointsIndex = state.controlPoints.length - 1 + stateUpdate = { + // coordinatesHistory: {$splice: [[lastEditIndex, 1]]}, + // controlPoints: {$splice: [[lastEditIndex, 1]]}, + actions: {$splice: [[lastActionIndex, 1]]} + } + switch (lastActionType) { + case 'ADD_CONTROL_POINT': + stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} + break + case 'UPDATE_CONTROL_POINT': + stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} + stateUpdate.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} + coordinates = state.coordinatesHistory[lastCoordinatesIndex] + if (coordinates) { + stateUpdate.active = { + subEntity: {shape: {coordinates: {$set: coordinates}}} + } + } + break + case 'REMOVE_CONTROL_POINT': + stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} + stateUpdate.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} + coordinates = state.coordinatesHistory[lastCoordinatesIndex] + if (coordinates) { + stateUpdate.active = { + subEntity: {shape: {coordinates: {$set: coordinates}}} + } + } + break + } + return update(state, stateUpdate) + case 'ADD_CONTROL_POINT': + controlPoints = [...state.controlPoints[state.controlPoints.length - 1]] + controlPoints.splice(action.index, 0, action.controlPoint) + return update(state, { + controlPoints: {$push: [controlPoints]}, + actions: {$push: [action.type]} + }) + case 'REMOVE_CONTROL_POINT': + controlPoints = [...state.controlPoints[state.controlPoints.length - 1]] + controlPoints.splice(action.index, 1) + return update(state, { + controlPoints: {$push: [controlPoints]}, + actions: {$push: [action.type]} + }) + case 'UPDATE_CONTROL_POINT': + let newControlPoints = [] + controlPoints = state.controlPoints[state.controlPoints.length - 1] + for (var i = 0; i < controlPoints.length; i++) { + newControlPoints.push(Object.assign({}, controlPoints[i])) + } + let newest = update(newControlPoints, {[action.index]: {point: {$set: action.point}, distance: {$set: action.distance}}}) + return update(state, { + controlPoints: {$push: [newest]}, + actions: {$push: [action.type]} + }) + case 'SAVED_TRIP_PATTERN': + // NOTE: pattern should always be active, so conditional is unnecessary... + // if (action.tripPattern.id === state.data.active.subEntityId) { + // set controlPoints initially and then whenever isSnappingToStops changes + controlPoints = getControlPoints(action.tripPattern, state.snapToStops) + return update(state, { + controlPoints: {$set: [controlPoints]} + }) + // } + default: + return state + } +} + +export default editSettings diff --git a/src/main/client/editor/reducers/timetable.js b/src/main/client/editor/reducers/timetable.js new file mode 100644 index 000000000..e455dbe0b --- /dev/null +++ b/src/main/client/editor/reducers/timetable.js @@ -0,0 +1,88 @@ +import update from 'react-addons-update' +import objectPath from 'object-path' +import clone from 'clone' + +import { sortAndFilterTrips } from '../util' + +const defaultState = { + columns: [], + trips: [], + edited: [], + selected: [], + hideDepartureTimes: false, + offset: null +} + +const timetable = (state = defaultState, action) => { + let trips + switch (action.type) { + case 'RECIEVE_TRIP_PATTERNS_FOR_ROUTE': + return update(state, { + columns: {$set: action.activeColumns} + }) + case 'SETTING_ACTIVE_GTFS_ENTITY': + switch (action.subComponent) { + case 'trippattern': + if (action.subSubComponent === 'timetable') { + return update(state, { + columns: {$set: action.activeColumns}, + patternId: {$set: action.subEntityId}, + calendarId: {$set: action.subSubEntityId} + }) + } + break + } + return state + case 'RECEIVE_TRIPS_FOR_CALENDAR': + trips = clone(sortAndFilterTrips(action.trips, action.pattern.useFrequency)) + return update(state, { + trips: {$set: trips}, + edited: {$set: []} + }) + case 'SET_TIMETABLE_OFFSET': + return update(state, { + offset: {$set: action.seconds} + }) + case 'UPDATE_TIMETABLE_CELL_VALUE': + trips = clone(state.trips) + objectPath.set(trips, action.key, action.value) + return update(state, { + trips: {$set: trips}, + edited: {$push: [action.rowIndex]} + }) + case 'TOGGLE_ALL_TIMETABLE_ROW_SELECTION': + let selected = [] + if (action.select) { + for (let i = 0; i < state.trips.length; i++) { + selected.push(i) + } + } + return update(state, { + selected: {$set: selected} + }) + case 'TOGGLE_DEPARTURE_TIMES': + return update(state, { + hideDepartureTimes: {$set: !state.hideDepartureTimes} + }) + case 'ADD_NEW_TRIP': + return update(state, { + trips: {$push: [action.trip]}, + edited: {$push: [state.trips.length]} + }) + case 'TOGGLE_SINGLE_TIMETABLE_ROW_SELECTION': + let selectIndex = state.selected.indexOf(action.rowIndex) + if (selectIndex === -1) { + return update(state, { + selected: {$push: [action.rowIndex]} + }) + } else { + return update(state, { + selected: {$splice: [[selectIndex, 1]]} + }) + } + default: + return state + } +} + +export default timetable diff --git a/src/main/client/editor/util/gtfs.js b/src/main/client/editor/util/gtfs.js index fb6667680..4a69230ab 100644 --- a/src/main/client/editor/util/gtfs.js +++ b/src/main/client/editor/util/gtfs.js @@ -88,58 +88,6 @@ export function getAbbreviatedStopName (stop, maxCharactersPerWord = 10) { : stop.stop_name } -export const gtfsIcons = [ - { - id: 'feedinfo', - icon: 'info', - addable: false, - title: 'Edit feed info', - label: 'Feed Info' - }, - { - id: 'agency', - icon: 'building', - addable: true, - title: 'Edit agencies', - label: 'Agencies' - }, - { - id: 'route', - icon: 'bus', - addable: true, - title: 'Edit routes', - label: 'Routes' - }, - { - id: 'stop', - icon: 'map-marker', - addable: true, - title: 'Edit stops', - label: 'Stops' - }, - { - id: 'calendar', - icon: 'calendar', - addable: true, - title: 'Edit calendars', - label: 'Calendars' - }, - { - id: 'scheduleexception', - icon: 'ban', - addable: true, - hideSidebar: true, - title: 'Edit schedule exceptions' - }, - { - id: 'fare', - icon: 'ticket', - addable: true, - title: 'Edit fares', - label: 'Fares' - } -] - export function getControlPoints (pattern, snapToStops) { if (!pattern) { return [] diff --git a/src/main/client/editor/util/index.js b/src/main/client/editor/util/index.js index 2c7e3be79..b8cfd2cd1 100644 --- a/src/main/client/editor/util/index.js +++ b/src/main/client/editor/util/index.js @@ -8,7 +8,7 @@ export function isTimeFormat (type) { return /TIME/.test(type) } -export function getTimetableColumns (pattern, stops, hideDepartureTimes) { +export function getTimetableColumns (pattern, stops) { const columns = [ { name: 'Block ID', @@ -45,21 +45,20 @@ export function getTimetableColumns (pattern, stops, hideDepartureTimes) { width: TIME_WIDTH, key: `stopTimes.${index}.arrivalTime`, colSpan: '2', - hidden: false, + // hidden: false, type: 'ARRIVAL_TIME', placeholder: 'HH:MM:SS' }) columns.push({ key: `stopTimes.${index}.departureTime`, width: TIME_WIDTH, - hidden: hideDepartureTimes, + // hidden: hideDepartureTimes, type: 'DEPARTURE_TIME', placeholder: 'HH:MM:SS' }) }) - } - // columns added if using freqency schedule type - else { + } else { + // columns added if using freqency schedule type columns.push({ name: 'Start time', width: 100, @@ -85,3 +84,20 @@ export function getTimetableColumns (pattern, stops, hideDepartureTimes) { } return columns } + +export function getStopsForPattern (pattern, stops) { + return pattern && pattern.patternStops && stops + ? pattern.patternStops.map(ps => stops.find(s => s.id === ps.stopId)) + : [] +} + +export function sortAndFilterTrips (trips, useFrequency) { + return trips + ? trips.filter(t => t.useFrequency === useFrequency) // filter out based on useFrequency + .sort((a, b) => { + if (a.stopTimes[0].departureTime < b.stopTimes[0].departureTime) return -1 + if (a.stopTimes[0].departureTime > b.stopTimes[0].departureTime) return 1 + return 0 + }) + : [] +} diff --git a/src/main/client/editor/util/map.js b/src/main/client/editor/util/map.js new file mode 100644 index 000000000..1ea682f98 --- /dev/null +++ b/src/main/client/editor/util/map.js @@ -0,0 +1,61 @@ + +import { generateUID } from '../../common/util/util' +import { getConfigProperty } from '../../common/util/config' +import { reverseEsri as reverse } from '../../scenario-editor/utils/reverse' + +export const MAP_LAYERS = [ + { + name: 'Streets', + id: getConfigProperty('mapbox.map_id') + }, + { + name: 'Light', + id: 'mapbox.light' + }, + { + name: 'Dark', + id: 'mapbox.dark' + }, + { + name: 'Satellite', + id: 'mapbox.streets-satellite' + } +] + +export function stopToStopTime (stop) { + return {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} +} + +export function clickToLatLng (latlng) { + const precision = 100000000 // eight decimal places is accurate up to 1.1 meters + return {stop_lat: Math.round(latlng.lat * precision) / precision, stop_lon: Math.round(latlng.lng % 180 * precision) / precision} +} + +// TODO: not used currently, remove? +export function zoomToEntity (entity, map) { + if (entity && entity.id) { + map.leafletElement.panTo([entity.stop_lat, entity.stop_lon]) + } +} + +export async function constructStop (latlng, feedSourceId) { + let stopLatLng = clickToLatLng(latlng) + let result = await reverse(latlng) + let stopId = generateUID() + let stopName = `New Stop (${stopId})` + if (result && result.address) { + stopName = result.address.Address + } + return { + stop_id: stopId, + stop_name: stopName, + feedId: feedSourceId, + ...stopLatLng + } +} + +export function getFeedBounds (feedSource, pad) { + return feedSource && feedSource.latestValidation && feedSource.latestValidation.bounds + ? [[feedSource.latestValidation.bounds.north + pad, feedSource.latestValidation.bounds.west - pad], [feedSource.latestValidation.bounds.south - pad, feedSource.latestValidation.bounds.east + pad]] + : [[60, 60], [-60, -20]] +} diff --git a/src/main/client/editor/util/ui.js b/src/main/client/editor/util/ui.js new file mode 100644 index 000000000..5ddd0925c --- /dev/null +++ b/src/main/client/editor/util/ui.js @@ -0,0 +1,51 @@ +export const gtfsIcons = [ + { + id: 'feedinfo', + icon: 'info', + addable: false, + title: 'Edit feed info', + label: 'Feed Info' + }, + { + id: 'agency', + icon: 'building', + addable: true, + title: 'Edit agencies', + label: 'Agencies' + }, + { + id: 'route', + icon: 'bus', + addable: true, + title: 'Edit routes', + label: 'Routes' + }, + { + id: 'stop', + icon: 'map-marker', + addable: true, + title: 'Edit stops', + label: 'Stops' + }, + { + id: 'calendar', + icon: 'calendar', + addable: true, + title: 'Edit calendars', + label: 'Calendars' + }, + { + id: 'scheduleexception', + icon: 'ban', + addable: true, + hideSidebar: true, + title: 'Edit schedule exceptions' + }, + { + id: 'fare', + icon: 'ticket', + addable: true, + title: 'Edit fares', + label: 'Fares' + } +] diff --git a/src/main/client/gtfs/actions/feed.js b/src/main/client/gtfs/actions/feed.js index 2f054a3fc..5bd35efe2 100644 --- a/src/main/client/gtfs/actions/feed.js +++ b/src/main/client/gtfs/actions/feed.js @@ -7,7 +7,9 @@ function fetchingFeed (feedId, date, from, to) { return { type: 'FETCH_GRAPHQL_FEED', feedId, - date, from, to + date, + from, + to } } @@ -15,7 +17,9 @@ function errorFetchingFeed (feedId, date, from, to) { return { type: 'FETCH_GRAPHQL_FEED_REJECTED', feedId, - date, from, to + date, + from, + to } } diff --git a/src/main/client/gtfs/actions/filter.js b/src/main/client/gtfs/actions/filter.js index 83872f77c..5c758d81a 100644 --- a/src/main/client/gtfs/actions/filter.js +++ b/src/main/client/gtfs/actions/filter.js @@ -1,6 +1,5 @@ import { secureFetch } from '../../common/util/util' import { getFeed } from '../../common/util/modules' -import { fetchProjectFeeds } from '../../manager/actions/feeds' export const updatingGtfsFilter = (activeProject, user) => { return { type: 'UPDATE_GTFS_FILTER', @@ -40,8 +39,8 @@ export function updateGtfsFilter (activeProject, user) { // }) // } // else { - let feeds = feedIds.map(id => getFeed(activeFeeds, id)).filter(n => n) - dispatch(updateLoadedFeeds(feeds)) + let feeds = feedIds.map(id => getFeed(activeFeeds, id)).filter(n => n) + dispatch(updateLoadedFeeds(feeds)) // } }) } diff --git a/src/main/client/gtfs/actions/general.js b/src/main/client/gtfs/actions/general.js index b314dbeeb..2d4c9288e 100644 --- a/src/main/client/gtfs/actions/general.js +++ b/src/main/client/gtfs/actions/general.js @@ -1,23 +1,8 @@ import fetch from 'isomorphic-fetch' -import { - fetchingStops, - errorFetchingStops, - receiveStops, - clearStops -} from './stops' -import { - fetchingPatterns, - errorFetchingPatterns, - receivePatterns, - clearPatterns -} from './patterns' -import { - fetchingRoutes, - errorFetchingRoutes, - receiveRoutes, - clearRoutes -} from './routes' -import { stopsAndRoutes, compose, patternsAndStopsForBoundingBox } from '../util/graphql' +import { clearStops } from './stops' +import { clearPatterns } from './patterns' +import { clearRoutes } from './routes' +import { stopsAndRoutes, compose, patternsAndStopsForBoundingBox } from '../util/graphql' import { getFeedId } from '../../common/util/modules' export function clearGtfsElements () { @@ -27,7 +12,6 @@ export function clearGtfsElements () { dispatch(clearPatterns()) } } - function requestGtfsElements (feedIds, entities) { return { type: 'REQUESTING_GTFS_ELEMENTS', @@ -35,7 +19,6 @@ function requestGtfsElements (feedIds, entities) { entities } } -// function receivedGtfsElements (feedIds, stops, patterns) { return { type: 'RECEIVED_GTFS_ELEMENTS', @@ -44,21 +27,22 @@ function receivedGtfsElements (feedIds, stops, patterns) { patterns } } - export const requestStopsAndRoutes = (feedIds, routeids, stopIds, module) => { return { type: 'REQUEST_GTFS_STOPS_AND_ROUTES', - feedIds, routeids, stopIds, module + feedIds, + routeids, + stopIds, + module } } - export const receivedStopsAndRoutes = (results, module) => { return { type: 'RECEIVED_GTFS_STOPS_AND_ROUTES', - results, module + results, + module } } - export function fetchStopsAndRoutes (entities, module) { return function (dispatch, getState) { let activeProject = getState().projects.active @@ -91,16 +75,22 @@ export function fetchStopsAndRoutes (entities, module) { }) } } - export function refreshGtfsElements (feedId, entities) { return function (dispatch, getState) { dispatch(requestGtfsElements(feedId, entities)) const bounds = getState().gtfs.filter.map.bounds - const max_lat = bounds.getNorth() - const max_lon = bounds.getEast() - const min_lat = bounds.getSouth() - const min_lon = bounds.getWest() - return fetch(compose(patternsAndStopsForBoundingBox(feedId, entities, max_lat, max_lon, min_lat, min_lon), {feedId, max_lat, max_lon, min_lat, min_lon})) + const maxLat = bounds.getNorth() + const maxLon = bounds.getEast() + const minLat = bounds.getSouth() + const minLon = bounds.getWest() + const vars = { + feedId, + max_lat: maxLat, + max_lon: maxLon, + min_lat: minLat, + min_lon: minLon + } + return fetch(compose(patternsAndStopsForBoundingBox(feedId, entities, maxLat, maxLon, minLat, minLon), vars)) .then((response) => { return response.json() }) diff --git a/src/main/client/gtfs/actions/patterns.js b/src/main/client/gtfs/actions/patterns.js index 4c49cf0b3..2af1281da 100644 --- a/src/main/client/gtfs/actions/patterns.js +++ b/src/main/client/gtfs/actions/patterns.js @@ -12,7 +12,7 @@ export function fetchingPatterns (feedId, routeId) { export function clearPatterns () { return { - type: 'CLEAR_GRAPHQL_PATTERNS', + type: 'CLEAR_GRAPHQL_PATTERNS' } } diff --git a/src/main/client/gtfs/actions/routes.js b/src/main/client/gtfs/actions/routes.js index dc5fec6aa..9f1d404c4 100644 --- a/src/main/client/gtfs/actions/routes.js +++ b/src/main/client/gtfs/actions/routes.js @@ -10,7 +10,7 @@ export function fetchingRoutes (feedId) { export function clearRoutes () { return { - type: 'CLEAR_GRAPHQL_ROUTES', + type: 'CLEAR_GRAPHQL_ROUTES' } } diff --git a/src/main/client/gtfs/actions/stops.js b/src/main/client/gtfs/actions/stops.js index 767e02980..2c81c8af2 100644 --- a/src/main/client/gtfs/actions/stops.js +++ b/src/main/client/gtfs/actions/stops.js @@ -8,13 +8,16 @@ export function fetchingStops (feedId, routeId, patternId, date, from, to) { type: 'FETCH_GRAPHQL_STOPS', feedId, routeId, - patternId, date, from, to + patternId, + date, + from, + to } } export function clearStops () { return { - type: 'CLEAR_GRAPHQL_STOPS', + type: 'CLEAR_GRAPHQL_STOPS' } } @@ -24,7 +27,9 @@ export function errorFetchingStops (feedId, routeId, patternId, date, from, to) feedId, routeId, patternId, - date, from, to + date, + from, + to } } diff --git a/src/main/client/gtfs/components/GtfsFilter.js b/src/main/client/gtfs/components/GtfsFilter.js index 4baebd2f9..db484c431 100644 --- a/src/main/client/gtfs/components/GtfsFilter.js +++ b/src/main/client/gtfs/components/GtfsFilter.js @@ -1,25 +1,16 @@ import React from 'react' - -import { Panel, Button, DropdownButton, MenuItem, Badge, Glyphicon, Label, ButtonToolbar } from 'react-bootstrap' +import { Button, DropdownButton, MenuItem, Label, ButtonToolbar } from 'react-bootstrap' export default class GtfsFilter extends React.Component { - - constructor (props) { - super(props) - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { - var buttonMinimalStyle = { + let buttonMinimalStyle = { marginTop: '10px', - marginBottom: '5px', - // textAlign: 'right' + marginBottom: '5px' } - - var compare = function (a, b) { + const compare = function (a, b) { var aName = a.shortName || a.name var bName = b.shortName || b.name if (aName < bName) return -1 @@ -29,62 +20,64 @@ export default class GtfsFilter extends React.Component { var activeFeeds = this.props.activeFeeds.sort(compare) var activeAndLoadedFeeds = this.props.activeFeeds.filter(f => f && this.props.loadedFeeds.findIndex(feed => feed.id === f.id) !== -1) - var nonActiveFeeds = this.props.allFeeds.filter((feed) => { - return (activeFeeds.indexOf(feed) === -1) - }).sort(compare) + // var nonActiveFeeds = this.props.allFeeds.filter((feed) => { + // return (activeFeeds.indexOf(feed) === -1) + // }).sort(compare) var feedLookup = {} - for(var f of this.props.allFeeds) feedLookup[f.id] = f + for (let f of this.props.allFeeds) feedLookup[f.id] = f return ( - - feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name).join(' and ')}` - : `Searching ${activeAndLoadedFeeds.length} feeds`} - alt={activeAndLoadedFeeds.join(', ')} - onSelect={eventKey => { - let feed = feedLookup[eventKey] - activeFeeds.indexOf(feed) === -1 ? this.props.onAddFeed(feed) : this.props.onRemoveFeed(feed) - }} - > - {this.props.allFeeds.map((feed) => { - let disabled = this.props.loadedFeeds.findIndex(f => f.id === feed.id) === -1 - return ( - - - - {feed.shortName || feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name} - - - ) - })} - - - + + feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name).join(' and ')}` + : `Searching ${activeAndLoadedFeeds.length} feeds`} + alt={activeAndLoadedFeeds.join(', ')} + onSelect={eventKey => { + let feed = feedLookup[eventKey] + activeFeeds.indexOf(feed) === -1 ? this.props.onAddFeed(feed) : this.props.onRemoveFeed(feed) + }} + > + {this.props.allFeeds.map((feed) => { + let disabled = this.props.loadedFeeds.findIndex(f => f.id === feed.id) === -1 + return ( + + + + {feed.shortName || feed.name.length > 11 ? feed.name.substr(0, 11) + '...' : feed.name} + + + ) + })} + + + ) } } diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index 7ad5c1547..83452f21d 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -1,50 +1,40 @@ import React, { Component, PropTypes } from 'react' -import fetch from 'isomorphic-fetch' -import moment from 'moment' -import { Button, FormControl, ControlLabel } from 'react-bootstrap' import { shallowEqual } from 'react-pure-render' -import { divIcon, Browser } from 'leaflet' -import { Map, Marker, Popup, TileLayer, GeoJson, FeatureGroup, Rectangle } from 'react-leaflet' -import {Icon} from '@conveyal/woonerf' +import { Browser } from 'leaflet' +import { Map, Marker, TileLayer, GeoJson, FeatureGroup, Rectangle } from 'react-leaflet' -import { getFeed, getFeedId } from '../../common/util/modules' +import { getFeedId } from '../../common/util/modules' +import PatternGeoJson from './PatternGeoJson' +import StopMarker from './StopMarker' import { getFeedsBounds } from '../../common/util/geo' -import { getRouteName } from '../../editor/util/gtfs' import { getConfigProperty } from '../../common/util/config' -const colors = ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a'] - export default class GtfsMap extends Component { static propTypes = { searchFocus: PropTypes.string, bounds: PropTypes.array, feeds: PropTypes.array, version: PropTypes.object, - onStopClick: PropTypes.func, onRouteClick: PropTypes.func, onZoomChange: PropTypes.func, popupAction: PropTypes.string, newEntityId: PropTypes.number, - entities: PropTypes.array, position: PropTypes.array, stops: PropTypes.array, patterns: PropTypes.array, routes: PropTypes.array, - width: PropTypes.string, // % or px height: PropTypes.number // only px } constructor (props) { super(props) - this.state = { bounds: this.props.bounds || [[70, 130], [-70, -130]], map: {} } } - componentDidMount () { this.resetMap(true) } @@ -71,27 +61,22 @@ export default class GtfsMap extends Component { if (nextProps.feeds.length !== this.props.feeds.length && this.refs.map) { this.refreshGtfsElements(nextProps.feeds) } - // if (nextProps.entities !== this.props.entities && this.refs.map) { this.refreshGtfsElements(nextProps.feeds, nextProps.entities) } } - if (nextProps.searchFocus && nextProps.searchFocus !== this.props.searchFocus) { this.setState({searchFocus: nextProps.searchFocus}) } - // handle stop: panning on stop select // pattern panTo is handled with layerAddHandler and searchFocus if (nextProps.stop && !shallowEqual(nextProps.stop, this.props.stop)) { this.refs.map.leafletElement.panTo([nextProps.stop.stop_lat, nextProps.stop.stop_lon]) } - // if height or width changes, reset map if (nextProps.height !== this.props.height || nextProps.width !== this.props.width) { this.resetMap() } - // recalculate isochrone if date/time changes if (!shallowEqual(nextProps.dateTime, this.props.dateTime)) { this.state.lastClicked && this.props.showIsochrones && this.fetchIsochrones(this.state.lastClicked) @@ -101,8 +86,7 @@ export default class GtfsMap extends Component { let bounds if (this.props.feeds) { bounds = getFeedsBounds(this.props.feeds) - } - else if (this.props.version) { + } else if (this.props.version) { bounds = this.props.version.validationSummary.bounds } bounds = bounds && bounds.north ? [[bounds.north, bounds.east], [bounds.south, bounds.west]] : this.state.bounds @@ -116,136 +100,15 @@ export default class GtfsMap extends Component { getIsochroneColor (time) { return time ? 'blue' : 'red' } - renderTransferPerformance (stop) { - return stop.transferPerformance && stop.transferPerformance.length - ?
    - Transfer performance - { - let state = {} - state[stop.stop_id] = +evt.target.value - this.setState(state)} - } - > - {stop.transferPerformance - // .sort((a, b) => { - // - // }) - .map((summary, index) => { - const fromRoute = this.props.routes.find(r => r.route_id === summary.fromRoute) - const toRoute = this.props.routes.find(r => r.route_id === summary.toRoute) - return - }) - } - - {this.renderTransferPerformanceResult(stop.transferPerformance[this.state[stop.stop_id] || 0])} -
    - :

    No transfers found

    - } - renderTransferPerformanceResult (transferPerformance) { - if (!transferPerformance) - return

    No transfers found

    - return ( -
      -
    • Typical case: {moment.duration(transferPerformance.typicalCase, 'seconds').humanize()}
    • -
    • Best case: {moment.duration(transferPerformance.bestCase, 'seconds').humanize()}
    • -
    • Worst case: {moment.duration(transferPerformance.worstCase, 'seconds').humanize()}
    • -
    ) - } - renderStop (stop, index) { - if (!stop) { - return null - } - const feedId = stop.feed_id || stop.feed && stop.feed.feed_id - const feed = getFeed(this.props.feeds, feedId) - const busIcon = divIcon({ - html: ` - - - `, - className: '', - iconSize: [24, 24], - }) - return ( - { - // e.target.openPopup() - // }} - position={[stop.stop_lat, stop.stop_lon]} - key={`marker-${stop.stop_id}`} - > - -
    -

    {stop.stop_name} ({stop.stop_id})

    - {this.props.renderTransferPerformance && this.renderTransferPerformance(stop)} - {this.props.onStopClick - ? - : null - } -
    -
    -
    - ) - } - renderPattern (pattern, index = 0) { - if (!pattern) { - return null + refreshGtfsElements (feeds, entities) { + const zoomLevel = this.refs.map.leafletElement.getZoom() + const feedIds = (feeds || this.props.feeds).map(getFeedId) + const ents = (entities || this.props.entities || ['routes', 'stops']) + if (feedIds.length === 0 || zoomLevel <= 13) { + // this.props.clearGtfsElements() + } else { + this.props.refreshGtfsElements(feedIds, ents) } - const route = pattern.route - const feedId = route ? route.feed_id || route.feed.feed_id : null - const feed = getFeed(this.props.feeds, feedId) - const routeName = route ? getRouteName(route) : pattern.route_name - const routeId = route ? route.route_id : pattern.route_id - const popup = ( - -
    -

    {routeName}

    -

    {getRouteName(route)}

    -
      -
    • ID: {routeId}
    • -
    • Agency:{' '} - {// TODO: change this back to feedName - // route.feed_id - feed && feed.name - } -
    • -
    - {this.props.onRouteClick - ? - :

    [Must add stops first]

    - } -
    -
    - ) - return ( - { - layer.feature.properties.patternId = pattern.pattern_id - layer._leaflet_id = pattern.pattern_id - }} - > - {popup} - - ) } renderIsochrones () { let comps = [] @@ -261,7 +124,7 @@ export default class GtfsMap extends Component { opacity={0} style={(feature) => { return { - color: this.getIsochroneColor(iso.properties.time), + color: this.getIsochroneColor(iso.properties.time) } }} /> @@ -303,64 +166,73 @@ export default class GtfsMap extends Component { } let bounds = this.getBounds() return ( -
    - this.mapClicked(e)} - onMoveEnd={(e) => this.mapMoved(e)} - onLayerAdd={(e) => this.layerAddHandler(e)} - className='Gtfs-Map' - > - - {/* feed bounds */} - {this.props.showBounds - ? + this.mapClicked(e)} + onMoveEnd={(e) => this.mapMoved(e)} + onLayerAdd={(e) => this.layerAddHandler(e)} + className='Gtfs-Map' + > + + {/* feed bounds */} + {this.props.showBounds && + - : null - } - - {/* Stops from map bounds search */} - {this.props.stops - ? this.props.stops.map((stop, index) => this.renderStop(stop, index)) - : null } - {/* Stop from GtfsSearch */} - {this.renderStop(this.props.stop)} - - - {/* Patterns from map bounds search */} - {this.props.patterns - ? this.props.patterns.map((pattern, index) => this.renderPattern(pattern, index)) - : null - } - {/* Pattern from GtfsSearch */} - {this.renderPattern(this.props.pattern)} - - - {/* Isochrones from map click */} - {this.props.showIsochrones && this.renderIsochrones()} - - -
    + + {/* Stops from map bounds search */} + {this.props.stops && this.props.stops.length + ? this.props.stops.map((stop, index) => { + if (!stop) return null + return ( + + ) + }) + : null + } + {/* Stop from GtfsSearch */} + {this.props.stop && } + + + {/* Patterns from map bounds search */} + {this.props.patterns + ? this.props.patterns.map((pattern, index) => ( + + )) + : null + } + {/* Pattern from GtfsSearch */} + {this.props.pattern && } + + + {/* Isochrones from map click */} + {this.props.showIsochrones && this.renderIsochrones()} + + +
    ) } - - refreshGtfsElements (feeds, entities) { - const zoomLevel = this.refs.map.leafletElement.getZoom() - const feedIds = (feeds || this.props.feeds).map(getFeedId) - const ents = (entities || this.props.entities || ['routes', 'stops']) - if (feedIds.length === 0 || zoomLevel <= 13) { - // this.props.clearGtfsElements() - } else { - this.props.refreshGtfsElements(feedIds, ents) - } - } } diff --git a/src/main/client/gtfs/components/PatternGeoJson.js b/src/main/client/gtfs/components/PatternGeoJson.js new file mode 100644 index 000000000..4323ebb69 --- /dev/null +++ b/src/main/client/gtfs/components/PatternGeoJson.js @@ -0,0 +1,71 @@ +import React, {PropTypes} from 'react' +import { GeoJson, Popup } from 'react-leaflet' +import {Icon} from '@conveyal/woonerf' +import { Button } from 'react-bootstrap' + +import { getRouteName } from '../../editor/util/gtfs' +import { getFeed } from '../../common/util/modules' + +const COLORS = ['#a6cee3', '#1f78b4', '#b2df8a', '#33a02c', '#fb9a99', '#e31a1c', '#fdbf6f', '#ff7f00', '#cab2d6', '#6a3d9a'] + +export default class PatternGeoJson extends GeoJson { + static propTypes = { + pattern: PropTypes.object, + index: PropTypes.number, + feeds: PropTypes.array, + onRouteClick: PropTypes.func, + popupAction: PropTypes.string, + newEntityId: PropTypes.number + } + render () { + const { pattern, index = 0, feeds, onRouteClick, newEntityId, popupAction } = this.props + if (!pattern) { + return null + } + const route = pattern.route + const feedId = route ? route.feed_id || route.feed.feed_id : null + const feed = getFeed(feeds, feedId) + const routeName = route ? getRouteName(route) : pattern.route_name + const routeId = route ? route.route_id : pattern.route_id + const popup = ( + +
    +

    {routeName}

    +

    {getRouteName(route)}

    +
      +
    • ID: {routeId}
    • +
    • Agency:{' '} + {// TODO: change this back to feedName + // route.feed_id + feed && feed.name + } +
    • +
    + {onRouteClick + ? + :

    [Must add stops first]

    + } +
    +
    + ) + return ( + { + layer.feature.properties.patternId = pattern.pattern_id + layer._leaflet_id = pattern.pattern_id + }} + > + {popup} + + ) + } +} diff --git a/src/main/client/gtfs/components/StopMarker.js b/src/main/client/gtfs/components/StopMarker.js new file mode 100644 index 000000000..9975dff22 --- /dev/null +++ b/src/main/client/gtfs/components/StopMarker.js @@ -0,0 +1,53 @@ +import React, { Component, PropTypes } from 'react' +import { Marker, Popup } from 'react-leaflet' +import { Button } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' +import { getFeed } from '../../common/util/modules' +import { divIcon } from 'leaflet' + +import TransferPerformance from './TransferPerformance' + +export default class StopMarker extends Component { + static propTypes = { + stop: PropTypes.object + } + render () { + const { stop, feeds, renderTransferPerformance, onStopClick, newEntityId, popupAction } = this.props + if (!stop) { + return null + } + const feedId = stop.feed_id || stop.feed && stop.feed.feed_id + const feed = getFeed(feeds, feedId) + const busIcon = divIcon({ + html: ` + + + `, + className: '', + iconSize: [24, 24] + }) + return ( + + +
    +

    {stop.stop_name} ({stop.stop_id})

    + {renderTransferPerformance && } + {onStopClick + ? + : null + } +
    +
    +
    + ) + } +} diff --git a/src/main/client/gtfs/components/TransferPerformance.js b/src/main/client/gtfs/components/TransferPerformance.js new file mode 100644 index 000000000..6efc32a9f --- /dev/null +++ b/src/main/client/gtfs/components/TransferPerformance.js @@ -0,0 +1,47 @@ +import React, {Component} from 'react' +import { ControlLabel, FormControl } from 'react-bootstrap' +import moment from 'moment' + +export default class TransferPerformance extends Component { + renderTransferPerformanceResult (transferPerformance) { + if (!transferPerformance) { + return

    No transfers found

    + } + return ( +
      +
    • Typical case: {moment.duration(transferPerformance.typicalCase, 'seconds').humanize()}
    • +
    • Best case: {moment.duration(transferPerformance.bestCase, 'seconds').humanize()}
    • +
    • Worst case: {moment.duration(transferPerformance.worstCase, 'seconds').humanize()}
    • +
    + ) + } + render () { + const { stop } = this.props + return stop.transferPerformance && stop.transferPerformance.length + ?
    + Transfer performance + { + let state = {} + state[stop.stop_id] = +evt.target.value + this.setState(state) + }} + > + {stop.transferPerformance + // .sort((a, b) => { + // + // }) + .map((summary, index) => { + const fromRoute = this.props.routes.find(r => r.route_id === summary.fromRoute) + const toRoute = this.props.routes.find(r => r.route_id === summary.toRoute) + return + }) + } + + {this.renderTransferPerformanceResult(stop.transferPerformance[this.state[stop.stop_id] || 0])} +
    + :

    No transfers found

    + } +} diff --git a/src/main/client/gtfs/components/gtfsmapsearch.js b/src/main/client/gtfs/components/gtfsmapsearch.js index 81a897658..18ad9bd8e 100644 --- a/src/main/client/gtfs/components/gtfsmapsearch.js +++ b/src/main/client/gtfs/components/gtfsmapsearch.js @@ -1,15 +1,15 @@ import React, { Component, PropTypes } from 'react' import fetch from 'isomorphic-fetch' import { Button } from 'react-bootstrap' -import { PureComponent, shallowEqual } from 'react-pure-render' import ActiveGtfsMap from '../containers/ActiveGtfsMap' -import GtfsMap from './GtfsMap' import GtfsSearch from './gtfssearch' export default class GtfsMapSearch extends Component { - - constructor(props) { + static propTypes = { + placeholder: PropTypes.string + } + constructor (props) { super(props) this.state = { stop: null, @@ -34,13 +34,11 @@ export default class GtfsMapSearch extends Component { handleSelection (input) { if (!input) { this.setState({stop: null, pattern: null, searchFocus: null}) - } - else if (input && input.stop) { + } else if (input && input.stop) { const pattern = null const stop = input.stop this.setState({ stop, pattern, searchFocus: stop.stop_id }) - } - else if (input && input.route) { + } else if (input && input.route) { // TODO: replace with GraphQL return Promise.all([this.getPatterns(input)]).then((results) => { const pattern = results[0] @@ -49,64 +47,59 @@ export default class GtfsMapSearch extends Component { }) } } - render() { + render () { let zoomMessage = 'Zoom in to view ' + this.state.searching.join(' and ') if (this.refs.map && this.refs.map.refs.map) { let mapZoom = this.refs.map.refs.map.leafletElement.getZoom() zoomMessage = mapZoom <= 13 ? zoomMessage : '' } - const onZoomChange = (e) => { - let mapZoom = e.target._zoom - zoomMessage = mapZoom <= 13 ? zoomMessage : '' - } - const {attribution, centerCoordinates, geojson, markers, transitive, url, zoom} = this.props const searchProps = { stop: this.state.stop, pattern: this.state.pattern, searchFocus: this.state.searchFocus, - entities: this.state.searching, + entities: this.state.searching } return ( -
    - this.handleSelection(input)} - entities={this.state.searching} - /> -
      -
    • - -
    • -
    • {zoomMessage}
    • -
    - -
    +
    + this.handleSelection(input)} + entities={this.state.searching} + /> +
      +
    • + +
    • +
    • {zoomMessage}
    • +
    + +
    ) } } diff --git a/src/main/client/gtfs/components/gtfssearch.js b/src/main/client/gtfs/components/gtfssearch.js index b26ff6b1e..341627ab4 100644 --- a/src/main/client/gtfs/components/gtfssearch.js +++ b/src/main/client/gtfs/components/gtfssearch.js @@ -1,19 +1,19 @@ -import React, { PropTypes } from 'react' +import React, { Component, PropTypes } from 'react' import fetch from 'isomorphic-fetch' -import { Panel, Grid, Row, Col, Button, Glyphicon, Label } from 'react-bootstrap' -import { PureComponent, shallowEqual } from 'react-pure-render' -import { Map, Marker, Popup, TileLayer, Polyline, MapControl } from 'react-leaflet' +import { Glyphicon, Label } from 'react-bootstrap' import Select from 'react-select' import { getFeed, getFeedId } from '../../common/util/modules' -export default class GtfsSearch extends React.Component { - - constructor(props) { +export default class GtfsSearch extends Component { + static propTypes = { + value: PropTypes.string + } + constructor (props) { super(props) this.state = { value: this.props.value - }; + } } cacheOptions (options) { @@ -29,17 +29,28 @@ export default class GtfsSearch extends React.Component { } renderOption (option) { - return {option.stop ? : } {option.label} {option.link} + return ( + + {option.stop + ? + : + } {option.label} {option.link} + + ) } onChange (value) { this.props.onChange && this.props.onChange(value) this.setState({value}) } - render() { + render () { const getRouteName = (route) => { - let routeName = route.route_short_name && route.route_long_name ? `${route.route_short_name} - ${route.route_long_name}` : - route.route_long_name ? route.route_long_name : - route.route_short_name ? route.route_short_name : null + let routeName = route.route_short_name && route.route_long_name + ? `${route.route_short_name} - ${route.route_long_name}` + : route.route_long_name + ? route.route_long_name + : route.route_short_name + ? route.route_short_name + : null return routeName } const getStops = (input) => { @@ -104,19 +115,18 @@ export default class GtfsSearch extends React.Component { }) } const getOptions = (input) => { - const entities = typeof this.props.entities !== 'undefined' ? this.props.entities : ['routes', 'stops'] let entitySearches = [] - if (entities.indexOf('stops') > -1){ + if (entities.indexOf('stops') > -1) { entitySearches.push(getStops(input)) } - if (entities.indexOf('routes') > -1){ + if (entities.indexOf('routes') > -1) { entitySearches.push(getRoutes(input)) } return Promise.all(entitySearches).then((results) => { const stops = results[0] const routes = typeof results[1] !== 'undefined' ? results[1] : [] - const options = { options: [...stops,...routes] } + const options = { options: [...stops, ...routes] } // console.log('search options', options) return options }) diff --git a/src/main/client/gtfs/containers/ActiveGtfsMap.js b/src/main/client/gtfs/containers/ActiveGtfsMap.js index 53f40c6a1..3b7bcd697 100644 --- a/src/main/client/gtfs/containers/ActiveGtfsMap.js +++ b/src/main/client/gtfs/containers/ActiveGtfsMap.js @@ -1,15 +1,11 @@ -import React from 'react' import { connect } from 'react-redux' import GtfsMap from '../components/GtfsMap' -import { fetchPatterns } from '../actions/patterns' import { clearGtfsElements, refreshGtfsElements } from '../actions/general' -import { fetchStops, stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../actions/stops' -import { fetchRoutes } from '../actions/routes' +import { stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../actions/stops' import { updateMapState } from '../actions/filter' import { fetchFeedVersionIsochrones } from '../../manager/actions/feeds' - const mapStateToProps = (state, ownProps) => { return { stops: state.gtfs.stops.data, @@ -51,11 +47,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, fetchIsochrones: (feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) => { dispatch(fetchFeedVersionIsochrones(feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime)) - }, - // viewStops: (row) => { - // dispatch(stopPatternFilterChange(feedId, row)) - // dispatch(ownProps.selectTab('stops')) - // } + } } } diff --git a/src/main/client/gtfs/containers/GlobalGtfsFilter.js b/src/main/client/gtfs/containers/GlobalGtfsFilter.js index 22d893ba5..25f5e7080 100644 --- a/src/main/client/gtfs/containers/GlobalGtfsFilter.js +++ b/src/main/client/gtfs/containers/GlobalGtfsFilter.js @@ -1,10 +1,8 @@ -import React from 'react' import { connect } from 'react-redux' import GtfsFilter from '../components/GtfsFilter' - import { addActiveFeed, removeActiveFeed, addAllActiveFeeds, - removeAllActiveFeeds, updatePermissionFilter, updateGtfsFilter } from '../actions/filter' + removeAllActiveFeeds, updateGtfsFilter } from '../actions/filter' const mapStateToProps = (state, ownProps) => { return { @@ -19,10 +17,11 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch, ownProps) => { return { onComponentMount: (initialProps) => { - let filter = initialProps.permissionFilter || 'view-feed' + // let filter = initialProps.permissionFilter || 'view-feed' // dispatch(updatePermissionFilter(filter)) - if (initialProps.project && initialProps.user) + if (initialProps.project && initialProps.user) { dispatch(updateGtfsFilter(initialProps.project, initialProps.user)) + } }, onAddFeed: (feed) => dispatch(addActiveFeed(feed)), onRemoveFeed: (feed) => dispatch(removeActiveFeed(feed)), diff --git a/src/main/client/gtfs/reducers/feed.js b/src/main/client/gtfs/reducers/feed.js index 6f70e029b..b0c550d2c 100644 --- a/src/main/client/gtfs/reducers/feed.js +++ b/src/main/client/gtfs/reducers/feed.js @@ -1,6 +1,5 @@ import update from 'react-addons-update' - const defaultState = { fetchStatus: { fetched: false, @@ -17,11 +16,10 @@ const feedStatKeyDescription = { feed_lang: 'Language Code', feed_version: 'Feed Version', route_count: 'Number of Routes in Feed', - stop_count: "Number of Stops in Feed" + stop_count: 'Number of Stops in Feed' } -export default function reducer(state=defaultState, action) { - +export default function reducer (state = defaultState, action) { switch (action.type) { case 'SET_ACTIVE_FEEDVERSION': return defaultState @@ -34,7 +32,6 @@ export default function reducer(state=defaultState, action) { }, data: [] } - break case 'FETCH_GRAPHQL_FEED_REJECTED': return update(state, { fetchStatus: { @@ -46,10 +43,9 @@ export default function reducer(state=defaultState, action) { }, data: [] }) - break case 'FETCH_GRAPHQL_FEED_FULFILLED': - let feedData = action.data.feeds[0], - feedStats = [] + let feedData = action.data.feeds[0] + let feedStats = [] const feedKeys = Object.keys(feedData) for (let i = 0; i < feedKeys.length; i++) { feedStats.push({ @@ -65,9 +61,7 @@ export default function reducer(state=defaultState, action) { }, data: feedStats } - break default: return state } - } diff --git a/src/main/client/gtfs/reducers/filter.js b/src/main/client/gtfs/reducers/filter.js index bb1a41809..a2d2a4832 100644 --- a/src/main/client/gtfs/reducers/filter.js +++ b/src/main/client/gtfs/reducers/filter.js @@ -8,7 +8,7 @@ const gtfsFilter = (state = { typeFilter: ['stops', 'routes'], map: { bounds: [], - zoom: null, + zoom: null }, permissionFilter: 'view-feed', version: null, diff --git a/src/main/client/gtfs/reducers/routes.js b/src/main/client/gtfs/reducers/routes.js index 74e64b3ef..ba51f6028 100644 --- a/src/main/client/gtfs/reducers/routes.js +++ b/src/main/client/gtfs/reducers/routes.js @@ -2,7 +2,6 @@ import update from 'react-addons-update' import { getRouteName } from '../../editor/util/gtfs' - const defaultState = { fetchStatus: { fetched: false, @@ -25,7 +24,6 @@ export default function reducer (state = defaultState, action) { }, data: [] } - break case 'FETCH_GRAPHQL_ROUTES_REJECTED': return update(state, { fetchStatus: { @@ -36,7 +34,6 @@ export default function reducer (state = defaultState, action) { } } }) - break case 'FETCH_GRAPHQL_ROUTES_FULFILLED': let newRoutes = [] for (let i = 0; i < action.data.routes.length; i++) { @@ -52,9 +49,7 @@ export default function reducer (state = defaultState, action) { }, data: newRoutes } - break default: return state } - } diff --git a/src/main/client/gtfs/reducers/stops.js b/src/main/client/gtfs/reducers/stops.js index 003cb18ff..e46f4141e 100644 --- a/src/main/client/gtfs/reducers/stops.js +++ b/src/main/client/gtfs/reducers/stops.js @@ -93,5 +93,4 @@ export default function reducer (state = defaultState, action) { default: return state } - } diff --git a/src/main/client/gtfs/util/graphql.js b/src/main/client/gtfs/util/graphql.js index 756d54161..1f1ec450b 100644 --- a/src/main/client/gtfs/util/graphql.js +++ b/src/main/client/gtfs/util/graphql.js @@ -93,10 +93,10 @@ query allStopsQuery($feedId: [String]) { } ` -export const patternsAndStopsForBoundingBox = (feedId, entities, max_lat, max_lon, min_lat, min_lon) => ` +export const patternsAndStopsForBoundingBox = (feedId, entities, maxLat, maxLon, minLat, minLon) => ` query patternsAndStopsGeo($feedId: [String], $max_lat: Float, $max_lon: Float, $min_lat: Float, $min_lon: Float){ ${entities.indexOf('routes') !== -1 - ? `patterns(feed_id: $feedId, max_lat: $max_lat, max_lon: $max_lon, min_lat: $min_lat, min_lon: $min_lon){ + ? `patterns(feed_id: $feedId, max_lat: $max_lat, max_lon: $max_lon, min_lat: $min_lat, min_lon: $min_lon){ pattern_id, geometry, name, diff --git a/src/main/client/gtfsplus/actions/gtfsplus.js b/src/main/client/gtfsplus/actions/gtfsplus.js index 3fe5ac96a..d470e2b86 100644 --- a/src/main/client/gtfsplus/actions/gtfsplus.js +++ b/src/main/client/gtfsplus/actions/gtfsplus.js @@ -1,4 +1,5 @@ import JSZip from 'jszip' +import fetch from 'isomorphic-fetch' import { secureFetch } from '../../common/util/util' import { getConfigProperty } from '../../common/util/config' @@ -11,7 +12,7 @@ export function addGtfsPlusRow (tableId) { const table = getConfigProperty('modules.gtfsplus.spec').find(t => t.id === tableId) let rowData = {} - for(const field of table.fields) { + for (const field of table.fields) { rowData[field.name] = null } @@ -40,18 +41,17 @@ export function deleteGtfsPlusRow (tableId, rowIndex) { } } - // DOWNLOAD/RECEIVE DATA ACTIONS export function requestingGtfsPlusContent () { return { - type: 'REQUESTING_GTFSPLUS_CONTENT', + type: 'REQUESTING_GTFSPLUS_CONTENT' } } export function clearGtfsPlusContent () { return { - type: 'CLEAR_GTFSPLUS_CONTENT', + type: 'CLEAR_GTFSPLUS_CONTENT' } } @@ -69,7 +69,7 @@ export function downloadGtfsPlusFeed (feedVersionId) { return function (dispatch, getState) { dispatch(requestingGtfsPlusContent()) - const fetchFeed = fetch('/api/manager/secure/gtfsplus/'+ feedVersionId, { + const fetchFeed = fetch('/api/manager/secure/gtfsplus/' + feedVersionId, { method: 'get', cache: 'default', headers: { 'Authorization': 'Bearer ' + getState().user.token } @@ -88,7 +88,7 @@ export function downloadGtfsPlusFeed (feedVersionId) { JSZip.loadAsync(feed).then((zip) => { let filenames = [] let filePromises = [] - zip.forEach((path,file) => { + zip.forEach((path, file) => { filenames.push(path) filePromises.push(file.async('string')) }) @@ -105,7 +105,7 @@ export function downloadGtfsPlusFeed (feedVersionId) { export function validatingGtfsPlusFeed () { return { - type: 'VALIDATING_GTFSPLUS_FEED', + type: 'VALIDATING_GTFSPLUS_FEED' } } @@ -123,7 +123,6 @@ export function validateGtfsPlusFeed (feedVersionId) { return secureFetch(url, getState()) .then(res => res.json()) .then(validationIssues => { - //console.log('got GTFS+ val result', validationResult) dispatch(receiveGtfsPlusValidation(validationIssues)) }) } @@ -133,13 +132,13 @@ export function validateGtfsPlusFeed (feedVersionId) { export function uploadingGtfsPlusFeed () { return { - type: 'UPLOADING_GTFSPLUS_FEED', + type: 'UPLOADING_GTFSPLUS_FEED' } } export function uploadedGtfsPlusFeed () { return { - type: 'UPLOADED_GTFSPLUS_FEED', + type: 'UPLOADED_GTFSPLUS_FEED' } } @@ -147,7 +146,7 @@ export function uploadGtfsPlusFeed (feedVersionId, file) { return function (dispatch, getState) { dispatch(uploadingGtfsPlusFeed()) const url = `/api/manager/secure/gtfsplus/${feedVersionId}` - var data = new FormData() + var data = new window.FormData() data.append('file', file) return fetch(url, { @@ -170,12 +169,10 @@ export function receiveGtfsEntities (gtfsEntities) { } export function loadGtfsEntities (tableId, rows, feedSource) { - return function (dispatch, getState) { - // lookup table for mapping tableId:fieldName keys to inputType values const typeLookup = {} - const getDataType = function(tableId, fieldName) { + const getDataType = function (tableId, fieldName) { const lookupKey = tableId + ':' + fieldName if (lookupKey in typeLookup) return typeLookup[lookupKey] const fieldInfo = getConfigProperty('modules.gtfsplus.spec') @@ -191,17 +188,17 @@ export function loadGtfsEntities (tableId, rows, feedSource) { const currentLookup = getState().gtfsplus.gtfsEntityLookup - for(const rowData of rows) { - for(const fieldName in rowData) { - switch(getDataType(tableId, fieldName)) { + for (const rowData of rows) { + for (const fieldName in rowData) { + switch (getDataType(tableId, fieldName)) { case 'GTFS_ROUTE': const routeId = rowData[fieldName] if (routeId && !(`route_${routeId}` in currentLookup)) routesToLoad.push(routeId) - break; + break case 'GTFS_STOP': const stopId = rowData[fieldName] if (stopId && !(`stop_${stopId}` in currentLookup)) stopsToLoad.push(stopId) - break; + break } } } @@ -236,7 +233,7 @@ export function loadGtfsEntities (tableId, rows, feedSource) { export function publishingGtfsPlusFeed () { return { - type: 'PUBLISHING_GTFSPLUS_FEED', + type: 'PUBLISHING_GTFSPLUS_FEED' } } @@ -246,7 +243,7 @@ export function publishGtfsPlusFeed (feedVersion) { const url = `/api/manager/secure/gtfsplus/${feedVersion.id}/publish` return secureFetch(url, getState(), 'post') .then((res) => { - console.log('published done'); + console.log('published done') return dispatch(fetchFeedVersions(feedVersion.feedSource)) }) } diff --git a/src/main/client/gtfsplus/components/GtfsPlusEditor.js b/src/main/client/gtfsplus/components/GtfsPlusEditor.js index 715cf2d08..9fcd78cea 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusEditor.js +++ b/src/main/client/gtfsplus/components/GtfsPlusEditor.js @@ -8,7 +8,9 @@ import GtfsPlusTable from './GtfsPlusTable' import { getConfigProperty } from '../../common/util/config' export default class GtfsPlusEditor extends Component { - + static propTypes = { + onComponentMount: PropTypes.function + } constructor (props) { super(props) @@ -27,7 +29,7 @@ export default class GtfsPlusEditor extends Component { save () { const zip = new JSZip() - for(const table of getConfigProperty('modules.gtfsplus.spec')) { + for (const table of getConfigProperty('modules.gtfsplus.spec')) { if (!(table.id in this.props.tableData) || this.props.tableData[table.id].length === 0) continue let fileContent = '' @@ -36,7 +38,7 @@ export default class GtfsPlusEditor extends Component { fileContent += fieldNameArr.join(',') + '\n' // write the data rows - var dataRows = this.props.tableData[table.id].map(rowData => { + this.props.tableData[table.id].map(rowData => { const rowText = fieldNameArr.map(fieldName => { return rowData[fieldName] || '' }).join(',') @@ -47,7 +49,7 @@ export default class GtfsPlusEditor extends Component { zip.file(table.name, fileContent) } - zip.generateAsync({type:"blob"}).then((content) => { + zip.generateAsync({type: 'blob'}).then((content) => { this.props.feedSaved(content) }) } @@ -86,7 +88,7 @@ export default class GtfsPlusEditor extends Component { disabled={editingIsDisabled} className='pull-right' onClick={() => { - console.log('save'); + console.log('save') this.save() }} > Save & Revalidate @@ -117,7 +119,7 @@ export default class GtfsPlusEditor extends Component { { - switch(field.inputType) { + switch (field.inputType) { case 'TEXT': case 'GTFS_TRIP': case 'GTFS_FARE': @@ -106,7 +107,9 @@ export default class GtfsPlusTable extends Component { ) case 'GTFS_STOP': const stopEntity = this.props.getGtfsEntity('stop', currentValue) - const stopValue = stopEntity ? {'value': stopEntity.stop_id, 'label': stopEntity.stop_name } : '' + const stopValue = stopEntity + ? {'value': stopEntity.stop_id, 'label': stopEntity.stop_name} + : '' return ( {(pageCount > 1) ? - - - - Page {this.state.currentPage} of {pageCount} - + - + + Page {this.state.currentPage} of {pageCount} + - - Go to { - if (e.keyCode == 13) { - const newPage = parseInt(e.target.value) - if (newPage > 0 && newPage <= pageCount) { - e.target.value = '' - this.setState({ currentPage: newPage }) - } + + + + Go to { + if (e.keyCode === 13) { + const newPage = parseInt(e.target.value) + if (newPage > 0 && newPage <= pageCount) { + e.target.value = '' + this.setState({ currentPage: newPage }) } - }} - onFocus={(e) => e.target.select()} - /> - + } + }} + onFocus={(e) => e.target.select()} + /> + : null } Show  - { - console.log('evt', evt.target.value); + console.log('evt', evt.target.value) this.setState({ visibility: evt.target.value, currentPage: 1 @@ -245,14 +248,15 @@ export default class GtfsPlusTable extends Component { /> ) })} - + {rowData && rowData.length > 0 ? rowData.map((data, rowIndex) => { - const tableRowIndex = (this.state.currentPage - 1) * recordsPerPage + rowIndex - return ( + const tableRowIndex = (this.state.currentPage - 1) * recordsPerPage + rowIndex + return ( + {table.fields.map(field => { const validationIssue = this.props.validation ? this.props.validation.find(v => @@ -263,22 +267,24 @@ export default class GtfsPlusTable extends Component { {validationIssue.description} ) : null - return ( - {validationIssue - ?
    + return ( + + {validationIssue + ?
    - : null - } -
    - {getInput(tableRowIndex, field, data[field.name])} -
    - ) + : null + } +
    + {getInput(tableRowIndex, field, data[field.name])} +
    + + ) })} - - ) - }) + + ) + }) : null } @@ -296,8 +303,8 @@ export default class GtfsPlusTable extends Component { {!rowData || rowData.length === 0 ? - No entries exist for this table. - + No entries exist for this table. + : null } diff --git a/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js b/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js index 4ac1b07ae..1fb798dcd 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js +++ b/src/main/client/gtfsplus/components/GtfsPlusVersionSummary.js @@ -1,12 +1,14 @@ import React, {Component, PropTypes} from 'react' -import { Panel, Row, Col, Table, Input, Button, Glyphicon, Well, Alert } from 'react-bootstrap' -import { Link, browserHistory } from 'react-router' +import { Panel, Row, Col, Table, Button, Glyphicon, Alert } from 'react-bootstrap' +import { browserHistory } from 'react-router' import moment from 'moment' import { getConfigProperty } from '../../common/util/config' export default class GtfsPlusVersionSummary extends Component { - + static propTypes = { + gtfsplus: PropTypes.object + } constructor (props) { super(props) this.state = { expanded: false } @@ -71,16 +73,16 @@ export default class GtfsPlusVersionSummary extends Component { {this.gtfsPlusEdited() ? + disabled={publishingIsDisabled} + bsStyle='primary' + style={{ marginLeft: '6px' }} + onClick={() => { + this.props.publishClicked(this.props.version) + this.setState({ expanded: false }) + }} + > + Publish as New Version + : null } @@ -95,7 +97,7 @@ export default class GtfsPlusVersionSummary extends Component { Included? Records Validation Issues - + @@ -105,7 +107,7 @@ export default class GtfsPlusVersionSummary extends Component { {this.isTableIncluded(table.id)} {this.tableRecordCount(table.id)} {this.validationIssueCount(table.id)} - + ) })} diff --git a/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js b/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js index bdedfa9e6..560feb576 100644 --- a/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js +++ b/src/main/client/gtfsplus/containers/ActiveGtfsPlusEditor.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux' -import GtfsPlusEditor from '../components/GtfsPlusEditor' +import GtfsPlusEditor from '../components/GtfsPlusEditor' import { fetchFeedSourceAndProject } from '../../manager/actions/feeds' import { addGtfsPlusRow, @@ -8,21 +8,19 @@ import { deleteGtfsPlusRow, uploadGtfsPlusFeed, downloadGtfsPlusFeed, - importGtfsPlusFromGtfs, loadGtfsEntities, receiveGtfsEntities } from '../actions/gtfsplus' const mapStateToProps = (state, ownProps) => { - let feedSourceId = ownProps.routeParams.feedSourceId let user = state.user // find the containing project let project = state.projects.all ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) + if (!p.feedSources) return false + return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) + }) : null let feedSource @@ -61,7 +59,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { feedSaved: (file) => { dispatch(uploadGtfsPlusFeed(feedVersionId, file)) .then(() => { - console.log('re-downloading'); + console.log('re-downloading') dispatch(downloadGtfsPlusFeed(feedVersionId)) }) }, diff --git a/src/main/client/gtfsplus/containers/ActiveGtfsPlusVersionSummary.js b/src/main/client/gtfsplus/containers/ActiveGtfsPlusVersionSummary.js index 4e6e956dc..24a1eefbe 100644 --- a/src/main/client/gtfsplus/containers/ActiveGtfsPlusVersionSummary.js +++ b/src/main/client/gtfsplus/containers/ActiveGtfsPlusVersionSummary.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux' -import GtfsPlusVersionSummary from '../components/GtfsPlusVersionSummary' +import GtfsPlusVersionSummary from '../components/GtfsPlusVersionSummary' import { downloadGtfsPlusFeed, diff --git a/src/main/client/gtfsplus/reducers/gtfsplus.js b/src/main/client/gtfsplus/reducers/gtfsplus.js index ee1b0d2d6..bb6b4e813 100644 --- a/src/main/client/gtfsplus/reducers/gtfsplus.js +++ b/src/main/client/gtfsplus/reducers/gtfsplus.js @@ -1,6 +1,6 @@ import update from 'react-addons-update' -/*const emptyTableData = { +/* const emptyTableData = { 'realtime_routes': [], 'realtime_stops': [], 'directions': [], @@ -11,9 +11,7 @@ import update from 'react-addons-update' 'fare_rider_categories': [], 'calendar_attributes': [], 'farezone_attributes': [] -}*/ - -const emptyTableData = { } +} */ const gtfsplus = (state = { feedVersionId: null, @@ -34,7 +32,7 @@ const gtfsplus = (state = { case 'RECEIVE_GTFSPLUS_CONTENT': let newTableData = {} - for(let i = 0; i < action.filenames.length; i++) { + for (let i = 0; i < action.filenames.length; i++) { const lines = action.fileContent[i].split(/\r\n|\r|\n/g) if (lines.length < 2) continue console.log(lines[0]) @@ -47,8 +45,8 @@ const gtfsplus = (state = { .map((line, rowIndex) => { const values = line.split(',') let rowData = { origRowIndex: rowIndex } - for(let f = 0; f < fields.length; f++) { - rowData[fields[f]] = values[f] + for (let f = 0; f < fields.length; f++) { + rowData[fields[f]] = values[f] } return rowData }) @@ -58,13 +56,12 @@ const gtfsplus = (state = { timestamp: {$set: action.timestamp}, tableData: {$set: newTableData} }) - case 'ADD_GTFSPLUS_ROW': // create this table if it doesn't already exist if (!(action.tableId in state.tableData)) { return update(state, {tableData: - {$merge: {[action.tableId]: [action.rowData]} } + {$merge: {[action.tableId]: [action.rowData]}} } ) } @@ -76,7 +73,6 @@ const gtfsplus = (state = { } } ) - case 'UPDATE_GTFSPLUS_FIELD': return update(state, {tableData: @@ -89,7 +85,6 @@ const gtfsplus = (state = { } } ) - case 'DELETE_GTFSPLUS_ROW': const table = state.tableData[action.tableId] const newTable = [ @@ -103,17 +98,15 @@ const gtfsplus = (state = { } } ) - case 'RECEIVE_GTFS_PLUS_ENTITIES': const getType = function (entity) { if (entity.hasOwnProperty('route_id')) return 'route' if (entity.hasOwnProperty('stop_id')) return 'stop' } - const newLookupEntries = {} - for(const entity of action.gtfsEntities) { + for (let entity of action.gtfsEntities) { const type = getType(entity) - const key = type + '_' + entity[type+'_id'] + const key = type + '_' + entity[type + '_id'] newLookupEntries[key] = entity } @@ -122,10 +115,9 @@ const gtfsplus = (state = { {$merge: newLookupEntries} } ) - case 'RECEIVE_GTFSPLUS_VALIDATION': const validationTable = {} - for(const issue of action.validationIssues) { + for (let issue of action.validationIssues) { if (!(issue.tableId in validationTable)) { validationTable[issue.tableId] = [] } diff --git a/src/main/client/main.js b/src/main/client/main.js index 43f21859d..bf8493241 100644 --- a/src/main/client/main.js +++ b/src/main/client/main.js @@ -31,7 +31,7 @@ const languageId = window.localStorage.getItem('lang') config.messages.active = lang.find(l => l.id === languageId) || lang.find(l => l.id === 'en-US') -console.log('config', config) +// console.log('config', config) window.DT_CONFIG = config import * as managerReducers from './manager/reducers' @@ -40,7 +40,7 @@ import * as alertsReducers from './alerts/reducers' import * as signsReducers from './signs/reducers' import * as gtfsPlusReducers from './gtfsplus/reducers' -import * as editorReducers from './editor/reducers' +import editor from './editor/reducers' import gtfs from './gtfs/reducers' const logger = createLogger({duration: true, collapsed: true}) @@ -51,7 +51,7 @@ const store = createStore( ...alertsReducers, ...signsReducers, ...gtfsPlusReducers, - ...editorReducers, + editor, // ...reportReducers, routing: routerReducer, gtfs @@ -59,7 +59,7 @@ const store = createStore( applyMiddleware(thunkMiddleware, logger) ) -console.log('initial store', store.getState()) +// console.log('initial store', store.getState()) const appHistory = syncHistoryWithStore(browserHistory, store) diff --git a/src/main/client/manager/actions/deployments.js b/src/main/client/manager/actions/deployments.js index 4f2a25dc0..a77349496 100644 --- a/src/main/client/manager/actions/deployments.js +++ b/src/main/client/manager/actions/deployments.js @@ -1,7 +1,7 @@ import { browserHistory } from 'react-router' import { secureFetch } from '../../common/util/util' -import { fetchProject, receiveProject } from './projects' +import { receiveProject } from './projects' import { startJobMonitor } from './status' // Deployment Actions @@ -52,12 +52,11 @@ export function deployToTarget (deployment, target) { return function (dispatch, getState) { dispatch(deployingToTarget(deployment, target)) const url = `/api/manager/secure/deployments/${deployment.id}/deploy/${target}` - console.log('*** deploying...'); return secureFetch(url, getState(), 'post') .then(response => { - console.log(response); + console.log(response) if (response.status >= 300) { - alert('Deployment error: ' + response.statusText) + window.alert('Deployment error: ' + response.statusText) } else { dispatch(deployedToTarget(deployment, target)) dispatch(startJobMonitor()) @@ -66,7 +65,6 @@ export function deployToTarget (deployment, target) { } } - export function requestingDeployment () { return { type: 'REQUESTING_DEPLOYMENT' @@ -153,18 +151,6 @@ export function fetchDeploymentAndProject (id) { } } -export function fetchDeploymentTargets () { - return function (dispatch, getState) { - dispatch(requestingDeploymentTargets()) - const url = '/api/manager/secure/deployments/targets' - return secureFetch(url, getState()) - .then(response => response.json()) - .then(targets => { - return dispatch(receiveDeploymentTargets(targets)) - }) - } -} - export function createDeployment (projectId) { return { type: 'CREATE_DEPLOYMENT', diff --git a/src/main/client/manager/actions/feeds.js b/src/main/client/manager/actions/feeds.js index 8323bbcfd..d5b34d561 100644 --- a/src/main/client/manager/actions/feeds.js +++ b/src/main/client/manager/actions/feeds.js @@ -1,11 +1,8 @@ -import qs from 'qs' -import fetch from 'isomorphic-fetch' - import { secureFetch } from '../../common/util/util' import { fetchProject, fetchProjectWithFeeds } from './projects' import { setErrorMessage, startJobMonitor } from './status' +import { fetchFeedVersions, feedNotModified } from './versions' import { fetchSnapshots } from '../../editor/actions/snapshots' -// Feed Source Actions export function requestingFeedSources () { return { @@ -244,377 +241,3 @@ export function runFetchFeed (feedSource) { }) } } - -//* FEED VERSION ACTIONS *// - -// Get all FeedVersions for FeedSource - -export function requestingFeedVersions () { - return { - type: 'REQUESTING_FEEDVERSIONS' - } -} - -export function receiveFeedVersions (feedSource, feedVersions) { - return { - type: 'RECEIVE_FEEDVERSIONS', - feedSource, - feedVersions - } -} - -export function setActiveVersion (feedVersion) { - return { - type: 'SET_ACTIVE_FEEDVERSION', - feedVersion - } -} - -export function fetchFeedVersions (feedSource, unsecured) { - return function (dispatch, getState) { - dispatch(requestingFeedVersions()) - const apiRoot = unsecured ? 'public' : 'secure' - const url = `/api/manager/${apiRoot}/feedversion?feedSourceId=${feedSource.id}` - return secureFetch(url, getState()) - .then(response => response.json()) - .then(versions => { - dispatch(receiveFeedVersions(feedSource, versions)) - return versions - }) - } -} - -export function requestingFeedVersion () { - return { - type: 'REQUESTING_FEEDVERSION' - } -} - -export function receiveFeedVersion (feedVersion) { - return { - type: 'RECEIVE_FEEDVERSION', - feedVersion - } -} - -export function fetchFeedVersion (feedVersionId) { - return function (dispatch, getState) { - dispatch(requestingFeedVersion()) - const url = `/api/manager/secure/feedversion/${feedVersionId}` - return secureFetch(url, getState()) - .then(response => response.json()) - .then(version => { - return dispatch(receiveFeedVersion(version)) - }) - } -} - -export function publishingFeedVersion (feedVersion) { - return { - type: 'PUBLISHING_FEEDVERSION', - feedVersion - } -} - -export function publishedFeedVersion (feedVersion) { - return { - type: 'PUBLISHED_FEEDVERSION', - feedVersion - } -} - -export function publishFeedVersion (feedVersion) { - return function (dispatch, getState) { - dispatch(publishingFeedVersion(feedVersion)) - const url = `/api/manager/secure/feedversion/${feedVersion.id}/publish` - return secureFetch(url, getState(), 'post') - .then(response => response.json()) - .then(version => { - return dispatch(publishedFeedVersion(version)) - }) - } -} - -export function fetchPublicFeedVersions (feedSource) { - return function (dispatch, getState) { - dispatch(requestingFeedVersions()) - const url = `/api/manager/public/feedversion?feedSourceId=${feedSource.id}&public=true` - return secureFetch(url, getState()) - .then(response => response.json()) - .then(versions => { - dispatch(receiveFeedVersions(feedSource, versions)) - }) - } -} - -// Upload a GTFS File as a new FeedVersion - -export function uploadingFeed (feedSource, file) { - return { - type: 'UPLOADING_FEED', - feedSource, - file - } -} - -export function uploadedFeed (feedSource) { - return { - type: 'UPLOADED_FEED', - feedSource - } -} - -export function feedNotModified (feedSource, message) { - return { - type: 'FEED_NOT_MODIFIED', - feedSource, - message - } -} - -export function uploadFeed (feedSource, file) { - return function (dispatch, getState) { - dispatch(uploadingFeed(feedSource, file)) - const url = `/api/manager/secure/feedversion?feedSourceId=${feedSource.id}&lastModified=${file.lastModified}` - - var data = new window.FormData() - data.append('file', file) - - return fetch(url, { - method: 'post', - headers: { 'Authorization': 'Bearer ' + getState().user.token }, - body: data - }).then(res => { - if (res.status === 304) { - dispatch(feedNotModified(feedSource, 'Feed upload cancelled because it matches latest feed version.')) - } else if (res.status >= 400) { - dispatch(setErrorMessage('Error uploading feed source')) - } else { - dispatch(uploadedFeed(feedSource)) - dispatch(startJobMonitor()) - } - console.log('uploadFeed result', res) - - // fetch feed source with versions - return dispatch(fetchFeedSource(feedSource.id, true)) - }) - } -} - -// Delete an existing FeedVersion - -export function deletingFeedVersion (feedVersion) { - return { - type: 'DELETING_FEEDVERSION', - feedVersion - } -} - -export function deleteFeedVersion (feedVersion, changes) { - return function (dispatch, getState) { - dispatch(deletingFeedVersion(feedVersion)) - const url = '/api/manager/secure/feedversion/' + feedVersion.id - return secureFetch(url, getState(), 'delete') - .then((res) => { - // fetch feed source with versions - return dispatch(fetchFeedSource(feedVersion.feedSource.id, true)) - }) - } -} - -// Get GTFS validation results for a FeedVersion - -export function requestingValidationResult (feedVersion) { - return { - type: 'REQUESTING_VALIDATION_RESULT', - feedVersion - } -} - -export function receiveValidationResult (feedVersion, validationResult) { - return { - type: 'RECEIVE_VALIDATION_RESULT', - feedVersion, - validationResult - } -} - -export function fetchValidationResult (feedVersion, isPublic) { - return function (dispatch, getState) { - dispatch(requestingValidationResult(feedVersion)) - const route = isPublic ? 'public' : 'secure' - const url = `/api/manager/${route}/feedversion/${feedVersion.id}/validation` - return secureFetch(url, getState()) - .then(response => response.json()) - .then(result => { - dispatch(receiveValidationResult(feedVersion, result)) - }) - } -} - -// Request a FeedVersion isochrone - -export function requestingFeedVersionIsochrones () { - return { - type: 'REQUESTING_FEEDVERSION_ISOCHRONES' - } -} - -export function receiveFeedVersionIsochrones (feedSource, feedVersion, isochrones) { - return { - type: 'RECEIVE_FEEDVERSION_ISOCHRONES', - feedSource, - feedVersion, - isochrones - } -} - -export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) { - return function (dispatch, getState) { - if (typeof date === 'undefined' || typeof fromTime === 'undefined' || typeof toTime === 'undefined') { - date = getState().gtfs.filter.dateTimeFilter.date - fromTime = getState().gtfs.filter.dateTimeFilter.from - toTime = getState().gtfs.filter.dateTimeFilter.to - } - dispatch(requestingFeedVersionIsochrones()) - const params = {fromLat, fromLon, toLat, toLon, date, fromTime, toTime} - const url = `/api/manager/secure/feedversion/${feedVersion.id}/isochrones?${qs.stringify(params)}` - return secureFetch(url, getState()) - .then(res => { - console.log(res.status) - if (res.status === 202) { - // dispatch(setStatus) - console.log('building network') - return [] - } - return res.json() - }) - .then(isochrones => { - console.log('received isochrones ', isochrones) - dispatch(receiveFeedVersionIsochrones(feedVersion.feedSource, feedVersion, isochrones)) - return isochrones - }) - } -} - -// Download a GTFS file for a FeedVersion - -export function downloadFeedViaToken (feedVersion, isPublic) { - return function (dispatch, getState) { - const route = isPublic ? 'public' : 'secure' - const url = `/api/manager/${route}/feedversion/${feedVersion.id}/downloadtoken` - secureFetch(url, getState()) - .then(response => response.json()) - .then(result => { - window.location.assign(`/api/manager/downloadfeed/${result.id}`) - }) - } -} - -// Create a Feed Version from an editor snapshot - -export function creatingFeedVersionFromSnapshot () { - return { - type: 'CREATING_FEEDVERSION_FROM_SNAPSHOT' - } -} - -export function createFeedVersionFromSnapshot (feedSource, snapshotId) { - return function (dispatch, getState) { - dispatch(creatingFeedVersionFromSnapshot()) - const url = `/api/manager/secure/feedversion/fromsnapshot?feedSourceId=${feedSource.id}&snapshotId=${snapshotId}` - return secureFetch(url, getState(), 'post') - .then((res) => { - dispatch(startJobMonitor()) - }) - } -} - -// Create a Feed Version from an editor snapshot - -export function renamingFeedVersion () { - return { - type: 'RENAMING_FEEDVERSION' - } -} - -export function renameFeedVersion (feedVersion, name) { - return function (dispatch, getState) { - dispatch(renamingFeedVersion()) - const url = `/api/manager/secure/feedversion/${feedVersion.id}/rename?name=${name}` - return secureFetch(url, getState(), 'put') - .then((res) => { - dispatch(fetchFeedVersion(feedVersion.id)) - }) - } -} - -//* NOTES ACTIONS *// - -export function requestingNotes () { - return { - type: 'REQUESTING_NOTES' - } -} - -export function receiveNotesForFeedSource (feedSource, notes) { - return { - type: 'RECEIVE_NOTES_FOR_FEEDSOURCE', - feedSource, - notes - } -} - -export function fetchNotesForFeedSource (feedSource) { - return function (dispatch, getState) { - dispatch(requestingNotes()) - const url = `/api/manager/secure/note?type=FEED_SOURCE&objectId=${feedSource.id}` - secureFetch(url, getState()) - .then(response => response.json()) - .then(notes => { - dispatch(receiveNotesForFeedSource(feedSource, notes)) - }) - } -} - -export function postNoteForFeedSource (feedSource, note) { - return function (dispatch, getState) { - const url = `/api/manager/secure/note?type=FEED_SOURCE&objectId=${feedSource.id}` - secureFetch(url, getState(), 'post', note) - .then(response => response.json()) - .then(note => { - dispatch(fetchNotesForFeedSource(feedSource)) - }) - } -} - -export function receiveNotesForFeedVersion (feedVersion, notes) { - return { - type: 'RECEIVE_NOTES_FOR_FEEDVERSION', - feedVersion, - notes - } -} - -export function fetchNotesForFeedVersion (feedVersion) { - return function (dispatch, getState) { - dispatch(requestingNotes()) - const url = `/api/manager/secure/note?type=FEED_VERSION&objectId=${feedVersion.id}` - secureFetch(url, getState()) - .then(response => response.json()) - .then(notes => { - dispatch(receiveNotesForFeedVersion(feedVersion, notes)) - }) - } -} - -export function postNoteForFeedVersion (feedVersion, note) { - return function (dispatch, getState) { - const url = `/api/manager/secure/note?type=FEED_VERSION&objectId=${feedVersion.id}` - secureFetch(url, getState(), 'post', note) - .then(response => response.json()) - .then(note => { - dispatch(fetchNotesForFeedVersion(feedVersion)) - }) - } -} diff --git a/src/main/client/manager/actions/notes.js b/src/main/client/manager/actions/notes.js new file mode 100644 index 000000000..6252591a3 --- /dev/null +++ b/src/main/client/manager/actions/notes.js @@ -0,0 +1,69 @@ +import { secureFetch } from '../../common/util/util' + +export function requestingNotes () { + return { + type: 'REQUESTING_NOTES' + } +} + +export function receiveNotesForFeedSource (feedSource, notes) { + return { + type: 'RECEIVE_NOTES_FOR_FEEDSOURCE', + feedSource, + notes + } +} + +export function fetchNotesForFeedSource (feedSource) { + return function (dispatch, getState) { + dispatch(requestingNotes()) + const url = `/api/manager/secure/note?type=FEED_SOURCE&objectId=${feedSource.id}` + secureFetch(url, getState()) + .then(response => response.json()) + .then(notes => { + dispatch(receiveNotesForFeedSource(feedSource, notes)) + }) + } +} + +export function postNoteForFeedSource (feedSource, note) { + return function (dispatch, getState) { + const url = `/api/manager/secure/note?type=FEED_SOURCE&objectId=${feedSource.id}` + secureFetch(url, getState(), 'post', note) + .then(response => response.json()) + .then(note => { + dispatch(fetchNotesForFeedSource(feedSource)) + }) + } +} + +export function receiveNotesForFeedVersion (feedVersion, notes) { + return { + type: 'RECEIVE_NOTES_FOR_FEEDVERSION', + feedVersion, + notes + } +} + +export function fetchNotesForFeedVersion (feedVersion) { + return function (dispatch, getState) { + dispatch(requestingNotes()) + const url = `/api/manager/secure/note?type=FEED_VERSION&objectId=${feedVersion.id}` + secureFetch(url, getState()) + .then(response => response.json()) + .then(notes => { + dispatch(receiveNotesForFeedVersion(feedVersion, notes)) + }) + } +} + +export function postNoteForFeedVersion (feedVersion, note) { + return function (dispatch, getState) { + const url = `/api/manager/secure/note?type=FEED_VERSION&objectId=${feedVersion.id}` + secureFetch(url, getState(), 'post', note) + .then(response => response.json()) + .then(note => { + dispatch(fetchNotesForFeedVersion(feedVersion)) + }) + } +} diff --git a/src/main/client/manager/actions/projects.js b/src/main/client/manager/actions/projects.js index d0ab6c0b8..aaf1018f0 100644 --- a/src/main/client/manager/actions/projects.js +++ b/src/main/client/manager/actions/projects.js @@ -1,12 +1,12 @@ import { secureFetch } from '../../common/util/util' import { updateGtfsFilter } from '../../gtfs/actions/filter' import { setErrorMessage, startJobMonitor } from './status' -import { fetchProjectFeeds, updateFeedSource } from './feeds' +import { fetchProjectFeeds } from './feeds' // Bulk Project Actions function requestingProjects () { return { - type: 'REQUESTING_PROJECTS', + type: 'REQUESTING_PROJECTS' } } @@ -63,7 +63,7 @@ export function fetchProjectsWithPublicFeeds () { function requestingProject () { return { - type: 'REQUESTING_PROJECT', + type: 'REQUESTING_PROJECT' } } @@ -101,15 +101,16 @@ export function fetchProjectWithFeeds (projectId, unsecure) { // .catch(err => console.log(err)) .then(project => { dispatch(receiveProject(project)) - if (!unsecure) + if (!unsecure) { return dispatch(fetchProjectFeeds(project.id)) + } }) } } function deletingProject () { return { - type: 'DELETING_PROJECT', + type: 'DELETING_PROJECT' } } @@ -157,13 +158,13 @@ export function createProject () { export function requestingSync () { return { - type: 'REQUESTING_SYNC', + type: 'REQUESTING_SYNC' } } export function receiveSync () { return { - type: 'RECEIVE_SYNC', + type: 'RECEIVE_SYNC' } } @@ -183,7 +184,7 @@ export function thirdPartySync (projectId, type) { export function runningFetchFeedsForProject () { return { - type: 'RUNNING_FETCH_FEED_FOR_PROJECT', + type: 'RUNNING_FETCH_FEED_FOR_PROJECT' } } @@ -196,7 +197,6 @@ export function receiveFetchFeedsForProject (project) { export function fetchFeedsForProject (project) { return function (dispatch, getState) { - dispatch(runningFetchFeedsForProject()) const url = `/api/manager/secure/project/${project.id}/fetch` return secureFetch(url, getState(), 'post') @@ -204,11 +204,9 @@ export function fetchFeedsForProject (project) { if (res.status === 304) { // dispatch(feedNotModified(feedSource, 'Feed fetch cancelled because it matches latest feed version.')) console.log('fetch cancelled because matches latest') - } - else if (res.status >= 400) { + } else if (res.status >= 400) { dispatch(setErrorMessage('Error fetching project feeds')) - } - else { + } else { dispatch(receiveFetchFeedsForProject(project)) dispatch(startJobMonitor()) return res.json() @@ -224,7 +222,7 @@ export function fetchFeedsForProject (project) { function savingProject () { return { - type: 'SAVING_PROJECT', + type: 'SAVING_PROJECT' } } diff --git a/src/main/client/manager/actions/status.js b/src/main/client/manager/actions/status.js index 762cdfdc7..fea558c47 100644 --- a/src/main/client/manager/actions/status.js +++ b/src/main/client/manager/actions/status.js @@ -1,5 +1,5 @@ import { secureFetch } from '../../common/util/util' -import { fetchFeedVersion, fetchFeedSource } from './feeds' +import { fetchFeedSource } from './feeds' import { fetchSnapshots } from '../../editor/actions/snapshots' export function setErrorMessage (message) { @@ -15,7 +15,7 @@ export function clearStatusModal () { } } -/*function watchingStatus (job) { +/* function watchingStatus (job) { return { type: 'WATCHING_STATUS', job @@ -43,7 +43,7 @@ export function watchStatus (job) { } }, 1000); } -}*/ +} */ export function removeRetiredJob (job) { return { diff --git a/src/main/client/manager/actions/user.js b/src/main/client/manager/actions/user.js index 91135291c..137ee2353 100644 --- a/src/main/client/manager/actions/user.js +++ b/src/main/client/manager/actions/user.js @@ -1,6 +1,4 @@ import { secureFetch } from '../../common/util/util' -import { fetchProjects } from './projects' -import update from 'react-addons-update' import { getConfigProperty } from '../../common/util/config' import objectPath from 'object-path' @@ -24,7 +22,7 @@ export const userLoggedIn = (token, profile) => { } } -export function checkExistingLogin() { +export function checkExistingLogin () { return function (dispatch, getState) { dispatch(checkingExistingLogin()) var login = getState().user.auth0.checkExistingLogin() @@ -32,13 +30,11 @@ export function checkExistingLogin() { return login.then((userTokenAndProfile) => { if (userTokenAndProfile) { dispatch(userLoggedIn(userTokenAndProfile.token, userTokenAndProfile.profile)) - } - else { + } else { console.log('error checking token') } }) - } - else { + } else { console.log('no login found') dispatch(noExistingLogin()) // return empty promise @@ -65,13 +61,13 @@ export function updateUserMetadata (profile, props) { const CLIENT_ID = getConfigProperty('auth0.client_id') let userMetadata = profile.user_metadata || { - lang: 'en', - datatools: [ - { - client_id: CLIENT_ID - } - ] + lang: 'en', + datatools: [ + { + client_id: CLIENT_ID } + ] + } let clientIndex = userMetadata.datatools.findIndex(d => d.client_id === CLIENT_ID) for (var key in props) { objectPath.set(userMetadata, `datatools.${clientIndex}.${key}`, props[key]) @@ -116,8 +112,7 @@ export function updateTargetForSubscription (profile, target, subscriptionType) let index = sub.target.indexOf(target) if (index > -1) { sub.target.splice(index, 1) - } - else { + } else { sub.target.push(target) } } @@ -205,7 +200,7 @@ export function createPublicUser (credentials) { export function login (credentials, user, lockOptions) { return function (dispatch, getState) { - if (!credentials){ + if (!credentials) { return getState().user.auth0.loginViaLock(lockOptions) .then((userInfo) => { return dispatch(userLoggedIn(userInfo.token, userInfo.profile)) @@ -213,8 +208,7 @@ export function login (credentials, user, lockOptions) { // .then(() => { // dispatch(fetchProjects()) // }) - } - else { + } else { credentials.client_id = getConfigProperty('auth0.client_id') credentials.connection = 'Username-Password-Authentication' credentials.username = credentials.email @@ -225,9 +219,9 @@ export function login (credentials, user, lockOptions) { .then(response => response.json()) .then(token => { // save token to localStorage - localStorage.setItem('userToken', token.id_token) + window.localStorage.setItem('userToken', token.id_token) - return getState().user.auth0.loginFromToken (token.id_token) + return getState().user.auth0.loginFromToken(token.id_token) }).then((userInfo) => { console.log('got user info', userInfo) return dispatch(userLoggedIn(userInfo.token, userInfo.profile)) @@ -249,7 +243,7 @@ export function logout () { } } -export function resetPassword() { +export function resetPassword () { return function (dispatch, getState) { getState().user.auth0.resetPassword() } diff --git a/src/main/client/manager/actions/versions.js b/src/main/client/manager/actions/versions.js new file mode 100644 index 000000000..cd0f58ba0 --- /dev/null +++ b/src/main/client/manager/actions/versions.js @@ -0,0 +1,291 @@ +import qs from 'qs' +import fetch from 'isomorphic-fetch' + +import { secureFetch } from '../../common/util/util' +import { setErrorMessage, startJobMonitor } from './status' +import { fetchFeedSource } from './feeds' + +export function requestingFeedVersions () { + return { + type: 'REQUESTING_FEEDVERSIONS' + } +} + +export function receiveFeedVersions (feedSource, feedVersions) { + return { + type: 'RECEIVE_FEEDVERSIONS', + feedSource, + feedVersions + } +} + +export function setActiveVersion (feedVersion) { + return { + type: 'SET_ACTIVE_FEEDVERSION', + feedVersion + } +} + +export function fetchFeedVersions (feedSource, unsecured) { + return function (dispatch, getState) { + dispatch(requestingFeedVersions()) + const apiRoot = unsecured ? 'public' : 'secure' + const url = `/api/manager/${apiRoot}/feedversion?feedSourceId=${feedSource.id}` + return secureFetch(url, getState()) + .then(response => response.json()) + .then(versions => { + dispatch(receiveFeedVersions(feedSource, versions)) + return versions + }) + } +} + +export function requestingFeedVersion () { + return { + type: 'REQUESTING_FEEDVERSION' + } +} + +export function receiveFeedVersion (feedVersion) { + return { + type: 'RECEIVE_FEEDVERSION', + feedVersion + } +} + +export function fetchFeedVersion (feedVersionId) { + return function (dispatch, getState) { + dispatch(requestingFeedVersion()) + const url = `/api/manager/secure/feedversion/${feedVersionId}` + return secureFetch(url, getState()) + .then(response => response.json()) + .then(version => { + return dispatch(receiveFeedVersion(version)) + }) + } +} + +export function publishingFeedVersion (feedVersion) { + return { + type: 'PUBLISHING_FEEDVERSION', + feedVersion + } +} + +export function publishedFeedVersion (feedVersion) { + return { + type: 'PUBLISHED_FEEDVERSION', + feedVersion + } +} + +export function publishFeedVersion (feedVersion) { + return function (dispatch, getState) { + dispatch(publishingFeedVersion(feedVersion)) + const url = `/api/manager/secure/feedversion/${feedVersion.id}/publish` + return secureFetch(url, getState(), 'post') + .then(response => response.json()) + .then(version => { + return dispatch(publishedFeedVersion(version)) + }) + } +} + +export function fetchPublicFeedVersions (feedSource) { + return function (dispatch, getState) { + dispatch(requestingFeedVersions()) + const url = `/api/manager/public/feedversion?feedSourceId=${feedSource.id}&public=true` + return secureFetch(url, getState()) + .then(response => response.json()) + .then(versions => { + dispatch(receiveFeedVersions(feedSource, versions)) + }) + } +} + +// Upload a GTFS File as a new FeedVersion + +export function uploadingFeed (feedSource, file) { + return { + type: 'UPLOADING_FEED', + feedSource, + file + } +} + +export function uploadedFeed (feedSource) { + return { + type: 'UPLOADED_FEED', + feedSource + } +} + +export function feedNotModified (feedSource, message) { + return { + type: 'FEED_NOT_MODIFIED', + feedSource, + message + } +} + +export function uploadFeed (feedSource, file) { + return function (dispatch, getState) { + dispatch(uploadingFeed(feedSource, file)) + const url = `/api/manager/secure/feedversion?feedSourceId=${feedSource.id}&lastModified=${file.lastModified}` + + var data = new window.FormData() + data.append('file', file) + + return fetch(url, { + method: 'post', + headers: { 'Authorization': 'Bearer ' + getState().user.token }, + body: data + }).then(res => { + if (res.status === 304) { + dispatch(feedNotModified(feedSource, 'Feed upload cancelled because it matches latest feed version.')) + } else if (res.status >= 400) { + dispatch(setErrorMessage('Error uploading feed source')) + } else { + dispatch(uploadedFeed(feedSource)) + dispatch(startJobMonitor()) + } + console.log('uploadFeed result', res) + + // fetch feed source with versions + return dispatch(fetchFeedSource(feedSource.id, true)) + }) + } +} + +export function deletingFeedVersion (feedVersion) { + return { + type: 'DELETING_FEEDVERSION', + feedVersion + } +} + +export function deleteFeedVersion (feedVersion, changes) { + return function (dispatch, getState) { + dispatch(deletingFeedVersion(feedVersion)) + const url = '/api/manager/secure/feedversion/' + feedVersion.id + return secureFetch(url, getState(), 'delete') + .then((res) => { + // fetch feed source with versions + return dispatch(fetchFeedSource(feedVersion.feedSource.id, true)) + }) + } +} +export function requestingValidationResult (feedVersion) { + return { + type: 'REQUESTING_VALIDATION_RESULT', + feedVersion + } +} +export function receiveValidationResult (feedVersion, validationResult) { + return { + type: 'RECEIVE_VALIDATION_RESULT', + feedVersion, + validationResult + } +} +export function fetchValidationResult (feedVersion, isPublic) { + return function (dispatch, getState) { + dispatch(requestingValidationResult(feedVersion)) + const route = isPublic ? 'public' : 'secure' + const url = `/api/manager/${route}/feedversion/${feedVersion.id}/validation` + return secureFetch(url, getState()) + .then(response => response.json()) + .then(result => { + dispatch(receiveValidationResult(feedVersion, result)) + }) + } +} + +export function requestingFeedVersionIsochrones () { + return { + type: 'REQUESTING_FEEDVERSION_ISOCHRONES' + } +} + +export function receiveFeedVersionIsochrones (feedSource, feedVersion, isochrones) { + return { + type: 'RECEIVE_FEEDVERSION_ISOCHRONES', + feedSource, + feedVersion, + isochrones + } +} + +export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) { + return function (dispatch, getState) { + if (typeof date === 'undefined' || typeof fromTime === 'undefined' || typeof toTime === 'undefined') { + date = getState().gtfs.filter.dateTimeFilter.date + fromTime = getState().gtfs.filter.dateTimeFilter.from + toTime = getState().gtfs.filter.dateTimeFilter.to + } + dispatch(requestingFeedVersionIsochrones()) + const params = {fromLat, fromLon, toLat, toLon, date, fromTime, toTime} + const url = `/api/manager/secure/feedversion/${feedVersion.id}/isochrones?${qs.stringify(params)}` + return secureFetch(url, getState()) + .then(res => { + console.log(res.status) + if (res.status === 202) { + // dispatch(setStatus) + console.log('building network') + return [] + } + return res.json() + }) + .then(isochrones => { + console.log('received isochrones ', isochrones) + dispatch(receiveFeedVersionIsochrones(feedVersion.feedSource, feedVersion, isochrones)) + return isochrones + }) + } +} + +// Download a GTFS file for a FeedVersion +export function downloadFeedViaToken (feedVersion, isPublic) { + return function (dispatch, getState) { + const route = isPublic ? 'public' : 'secure' + const url = `/api/manager/${route}/feedversion/${feedVersion.id}/downloadtoken` + secureFetch(url, getState()) + .then(response => response.json()) + .then(result => { + window.location.assign(`/api/manager/downloadfeed/${result.id}`) + }) + } +} + +export function creatingFeedVersionFromSnapshot () { + return { + type: 'CREATING_FEEDVERSION_FROM_SNAPSHOT' + } +} +export function createFeedVersionFromSnapshot (feedSource, snapshotId) { + return function (dispatch, getState) { + dispatch(creatingFeedVersionFromSnapshot()) + const url = `/api/manager/secure/feedversion/fromsnapshot?feedSourceId=${feedSource.id}&snapshotId=${snapshotId}` + return secureFetch(url, getState(), 'post') + .then((res) => { + dispatch(startJobMonitor()) + }) + } +} + +export function renamingFeedVersion () { + return { + type: 'RENAMING_FEEDVERSION' + } +} + +export function renameFeedVersion (feedVersion, name) { + return function (dispatch, getState) { + dispatch(renamingFeedVersion()) + const url = `/api/manager/secure/feedversion/${feedVersion.id}/rename?name=${name}` + return secureFetch(url, getState(), 'put') + .then((res) => { + dispatch(fetchFeedVersion(feedVersion.id)) + }) + } +} diff --git a/src/main/client/manager/components/DeploymentSettings.js b/src/main/client/manager/components/DeploymentSettings.js new file mode 100644 index 000000000..5143398b5 --- /dev/null +++ b/src/main/client/manager/components/DeploymentSettings.js @@ -0,0 +1,315 @@ +import React, {Component} from 'react' +import { Row, Col, Button, Panel, Glyphicon, Form, Radio, Checkbox, FormGroup, ControlLabel, FormControl } from 'react-bootstrap' +import update from 'react-addons-update' +import { shallowEqual } from 'react-pure-render' + +import { getMessage, getComponentMessages } from '../../common/util/config' + +export default class DeploymentSettings extends Component { + constructor (props) { + super(props) + this.state = { + deployment: { + buildConfig: {}, + routerConfig: {}, + otpServers: this.props.project && this.props.project.otpServers ? this.props.project.otpServers : [] + } + } + } + componentWillReceiveProps (nextProps) { + this.setState({ + deployment: { + buildConfig: {}, + routerConfig: {}, + otpServers: nextProps.project && nextProps.project.otpServers ? nextProps.project.otpServers : [] + } + }) + } + shouldComponentUpdate (nextProps, nextState) { + return !shallowEqual(nextProps, this.props) || !shallowEqual(nextState, this.state) + } + render () { + console.log(this.state) + const messages = getComponentMessages('ProjectSettings') + const { project, editDisabled, updateProjectSettings } = this.props + const noEdits = Object.keys(this.state.deployment.buildConfig).length === 0 && + Object.keys(this.state.deployment.routerConfig).length === 0 && + shallowEqual(this.state.deployment.otpServers, project.otpServers) + return ( +
    + {getMessage(messages, 'deployment.buildConfig.title')}}> + + + + { + let stateUpdate = { deployment: { buildConfig: { fetchElevationUS: { $set: (evt.target.value === 'true') } } } } + this.setState(update(this.state, stateUpdate)) + }}> + + + + + + + + { + let stateUpdate = { deployment: { buildConfig: { stationTransfers: { $set: (evt.target.value === 'true') } } } } + this.setState(update(this.state, stateUpdate)) + }}> + + + + + + + + { + let stateUpdate = { deployment: { buildConfig: { subwayAccessTime: { $set: +evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + { + let stateUpdate = { deployment: { buildConfig: { fares: { $set: evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + Router Config}> + + + + { + let stateUpdate = { deployment: { routerConfig: { numItineraries: { $set: +evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + + + { + let stateUpdate = { deployment: { routerConfig: { walkSpeed: { $set: +evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + + + + + { + let stateUpdate = { deployment: { routerConfig: { stairsReluctance: { $set: +evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + + + { + let stateUpdate = { deployment: { routerConfig: { carDropoffTime: { $set: +evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + + + { + let stateUpdate = { deployment: { routerConfig: { brandingUrlRoot: { $set: evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + + + {getMessage(messages, 'deployment.servers.title')} + + }> +
    + {this.state.deployment.otpServers && this.state.deployment.otpServers.map((server, i) => { + let title = ( +
    + {server.name}{' '} + {server.publicUrl} +
    + ) + return ( + +
    + + + {getMessage(messages, 'deployment.servers.name')} + { + let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { name: evt.target.value } } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + {getMessage(messages, 'deployment.servers.public')} + { + let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { publicUrl: evt.target.value } } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + + {getMessage(messages, 'deployment.servers.internal')} + { + let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { internalUrl: evt.target.value.split(',') } } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + + { + let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { admin: evt.target.checked } } } } } + this.setState(update(this.state, stateUpdate)) + }}> + {getMessage(messages, 'deployment.servers.admin')} + +
    +
    + ) + })} +
    +
    + {getMessage(messages, 'deployment.osm.title')}}> + { + let stateUpdate = { deployment: { useCustomOsmBounds: { $set: (evt.target.value === 'true') } } } + this.setState(update(this.state, stateUpdate)) + }} + > + + {getMessage(messages, 'deployment.osm.gtfs')} + + + {getMessage(messages, 'deployment.osm.custom')} + + + {project.useCustomOsmBounds || this.state.deployment.useCustomOsmBounds + ? {getMessage(messages, 'deployment.osm.bounds')})} + ref='osmBounds' + onChange={(evt) => { + const bBox = evt.target.value.split(',') + if (bBox.length === 4) { + let stateUpdate = { deployment: { $merge: { osmWest: bBox[0], osmSouth: bBox[1], osmEast: bBox[2], osmNorth: bBox[3] } } } + this.setState(update(this.state, stateUpdate)) + } + }} /> + : null + } + + + + {/* Save button */} + + + +
    + ) + } +} diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index bc6761c2c..c6fc1f21f 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -24,7 +24,7 @@ export default class DeploymentViewer extends Component { } render () { - const { project, deployment, feedSources } = this.props + const { project } = this.props if (!this.props.deployment || !this.props.project || !this.props.feedSources) { return } @@ -59,13 +59,13 @@ export default class DeploymentViewer extends Component { onSelect={(evt) => { console.log(evt) this.props.deployToTargetClicked(this.props.deployment, evt) - //setTimeout(() => this.props.getDeploymentStatus(this.props.deployment, evt), 5000) + // setTimeout(() => this.props.getDeploymentStatus(this.props.deployment, evt), 5000) }} > {this.props.project.otpServers ? this.props.project.otpServers.map(server => ( - {server.name} - )) + {server.name} + )) : null } @@ -138,7 +138,7 @@ export default class DeploymentViewer extends Component { {getMessage(messages, 'table.stopTimesCount')} {getMessage(messages, 'table.validFrom')} {getMessage(messages, 'table.expires')} - + @@ -165,11 +165,6 @@ export default class DeploymentViewer extends Component { } class FeedVersionTableRow extends Component { - - constructor (props) { - super(props) - } - render () { const fs = this.props.feedSource const version = this.props.version diff --git a/src/main/client/manager/components/DeploymentsPanel.js b/src/main/client/manager/components/DeploymentsPanel.js new file mode 100644 index 000000000..2cd3e2dbc --- /dev/null +++ b/src/main/client/manager/components/DeploymentsPanel.js @@ -0,0 +1,164 @@ +import React, {Component, PropTypes} from 'react' +import moment from 'moment' +import { LinkContainer } from 'react-router-bootstrap' +import { Row, Label, Col, Button, Table, FormControl, Glyphicon, Panel } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' + +import ActiveDeploymentViewer from '../containers/ActiveDeploymentViewer' +import EditableTextField from '../../common/components/EditableTextField' +import { getComponentMessages, getMessage } from '../../common/util/config' + +export default class DeploymentsPanel extends Component { + static propTypes = { + deployments: PropTypes.array, + deleteDeploymentConfirmed: PropTypes.func, + deploymentsRequested: PropTypes.func, + onNewDeploymentClick: PropTypes.func, + newDeploymentNamed: PropTypes.func, + updateDeployment: PropTypes.func, + expanded: PropTypes.bool + } + componentWillMount () { + if (this.props.expanded) { + this.props.deploymentsRequested() + } + } + // deleteDeployment (deployment) { + // console.log(this.refs) + // this.refs['page'].showConfirmModal({ + // title: 'Delete Deployment?', + // body: `Are you sure you want to delete the deployment ${deployment.name}?`, + // onConfirm: () => { + // console.log('OK, deleting') + // this.props.deleteDeploymentConfirmed(deployment) + // } + // }) + // } + render () { + const deployment = this.props.deployments && this.props.deployments.find(d => d.id === this.props.activeSubComponent) + if (deployment) { + return ( + + ) + } + return ( + + + + + + Deploying feeds to OTP}> +

    A collection of feeds can be deployed to OpenTripPlanner (OTP) instances that have been defined in the organization settings.

    + + + +
    + +
    + ) + } +} + +class DeploymentsList extends Component { + render () { + const messages = getComponentMessages('DeploymentsPanel') + const na = (N/A) + return ( + + + this.props.searchTextChanged(evt.target.value)} + /> + + + + + + } + > + + + + + + + + + + + {this.props.deployments + ? this.props.deployments.map(dep => { + return ( + + + + + + + + ) + }) + : null + } + +
    {getMessage(messages, 'table.name')}{getMessage(messages, 'table.creationDate')}{getMessage(messages, 'table.deployedTo')}{getMessage(messages, 'table.feedCount')} +
    + { + if (dep.isCreating) this.props.newDeploymentNamed(value) + else this.props.updateDeployment(dep, {name: value}) + }} + link={`/project/${dep.project.id}/deployments/${dep.id}`} + /> + + {dep.dateCreated + ? ({moment(dep.dateCreated).format('MMM Do YYYY')} ({moment(dep.dateCreated).fromNow()})) + : na + } + + {dep.deployedTo + ? () + : na + } + + {dep.feedVersions + ? ({dep.feedVersions.length}) + : na + } + + +
    +
    + ) + } +} diff --git a/src/main/client/manager/components/ExternalPropertiesTable.js b/src/main/client/manager/components/ExternalPropertiesTable.js index f5fa270bc..6f0572b7c 100644 --- a/src/main/client/manager/components/ExternalPropertiesTable.js +++ b/src/main/client/manager/components/ExternalPropertiesTable.js @@ -7,14 +7,6 @@ export default class ExternalPropertiesTable extends Component { static propTypes = { resourceType: PropTypes.string } - constructor (props) { - super(props) - console.log('>> ExternalPropertiesTable props', this.props); - } - - componentWillMount () { - } - render () { return ( + this.props.deleteFeedSource(fs)} + onClose={() => this.props.setHold(false)} + /> + + this.confirmUpload(files)} + onClose={() => this.props.setHold(false)} + errorMessage='Uploaded file must be a valid zip file (.zip).' + /> + + this._selectItem(key)} + id={`feed-source-action-button`} + pullRight + > + + + + Fetch + Upload + + {/* show divider only if deployments and notifications are enabled (otherwise, we don't need it) */} + {isModuleEnabled('deployment') || getConfigProperty('application.notifications_enabled') + ? + : null + } + {isModuleEnabled('deployment') + ? + Deploy + + : null + } + {getConfigProperty('application.notifications_enabled') + ? + : null + } + View public page + + Delete + + +
    + } +} diff --git a/src/main/client/manager/components/FeedSourceTable.js b/src/main/client/manager/components/FeedSourceTable.js index 286e6c5c6..b04a4e438 100644 --- a/src/main/client/manager/components/FeedSourceTable.js +++ b/src/main/client/manager/components/FeedSourceTable.js @@ -1,17 +1,13 @@ import React, { Component, PropTypes } from 'react' import moment from 'moment' -import { Button, Checkbox, Glyphicon, Dropdown, MenuItem, ListGroupItem, ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap' -import { browserHistory } from 'react-router' +import { Button, ListGroupItem, ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' import { shallowEqual } from 'react-pure-render' import EditableTextField from '../../common/components/EditableTextField' -import ConfirmModal from '../../common/components/ConfirmModal' -import SelectFileModal from '../../common/components/SelectFileModal' -import WatchButton from '../../common/containers/WatchButton' +import FeedSourceDropdown from './FeedSourceDropdown' import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' -import { isValidZipFile } from '../../common/util/util' export default class FeedSourceTable extends Component { @@ -107,11 +103,11 @@ class FeedSourceTableRow extends Component { } render () { const fs = this.props.feedSource - const na = (N/A) + // const na = (N/A) const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') - const dateFormat = getConfigProperty('application.date_format') + // const dateFormat = getConfigProperty('application.date_format') const messages = getComponentMessages('ProjectViewer') - const feedItem = ( + return ( this.props.onHover(fs)} @@ -156,66 +152,6 @@ class FeedSourceTableRow extends Component {
    ) - const feedRow = ( - { - if (!this.state.hovered) { - this.setState({ hovered: true }) - this.props.onHover(fs) - } - }} - onMouseLeave={() => { - if (!this.props.active && this.state.hovered) this.setState({ hovered: false }) - }} - > - -
    - { - if (fs.isCreating) this.props.saveFeedSource(value) - else this.props.updateFeedSourceProperty(fs, 'name', value) - }} - link={`/feed/${fs.id}`} - /> -
    - - - { - this.props.updateFeedSourceProperty(fs, 'isPublic', e.target.checked) - }} - /> - - - { - this.props.updateFeedSourceProperty(fs, 'deployable', e.target.checked) - }} - /> - - {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : na} - {fs.latestValidation ? fs.latestValidation.errorCount : na} - {fs.latestValidation - ? ({moment(fs.latestValidation.startDate).format(dateFormat)} to {moment(fs.latestValidation.endDate).format(dateFormat)}) - : na - } - - {this.state.hovered - ? this.props.hoverComponent - : null - } - - - ) - - return feedItem } } @@ -289,137 +225,9 @@ class Attribute extends Component { ) return this.props.title - ? {this.props.title}}> + ? {this.props.title}}> {li} : li } } - -class FeedSourceDropdown extends Component { - - static propTypes = { - feedSource: PropTypes.object, - project: PropTypes.object, - user: PropTypes.object, - - createDeploymentFromFeedSource: PropTypes.func, - deleteFeedSource: PropTypes.func, - fetchFeed: PropTypes.func, - uploadFeed: PropTypes.func - } - _selectItem (key) { - switch (key) { - case 'delete': - return this.deleteFeed() - case 'fetch': - return this.props.fetchFeed(this.props.feedSource) - case 'upload': - return this.uploadFeed() - case 'deploy': - return this.props.createDeploymentFromFeedSource(this.props.feedSource) - case 'public': - return browserHistory.push(`/public/feed/${this.props.feedSource.id}`) - } - } - deleteFeed () { - this.props.setHold(this.props.feedSource) - this.refs['deleteModal'].open() - // this.setState({keepActive: true}) - } - uploadFeed () { - this.props.setHold(this.props.feedSource) - this.refs['uploadModal'].open() - } - confirmUpload (files) { - const file = files[0] - if (isValidZipFile(file)) { - this.props.uploadFeed(this.props.feedSource, file) - this.props.setHold(false) - return true - } else { - return false - } - } - render () { - const fs = this.props.feedSource - const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') - const isWatchingFeed = this.props.user.subscriptions.hasFeedSubscription(this.props.project.id, fs.id, 'feed-updated') - const editGtfsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'edit-gtfs') - - return
    - this.props.deleteFeedSource(fs)} - onClose={() => this.props.setHold(false)} - /> - - this.confirmUpload(files)} - onClose={() => this.props.setHold(false)} - errorMessage='Uploaded file must be a valid zip file (.zip).' - /> - - this._selectItem(key)} - id={`feed-source-action-button`} - pullRight - > - - - - Fetch - Upload - - {/* show divider only if deployments and notifications are enabled (otherwise, we don't need it) */} - {isModuleEnabled('deployment') || getConfigProperty('application.notifications_enabled') - ? - : null - } - {isModuleEnabled('deployment') - ? - Deploy - - : null - } - {getConfigProperty('application.notifications_enabled') - ? - : null - } - View public page - - Delete - - -
    - } -} diff --git a/src/main/client/manager/components/FeedSourceViewer.js b/src/main/client/manager/components/FeedSourceViewer.js index c94c745df..0b2ad9433 100644 --- a/src/main/client/manager/components/FeedSourceViewer.js +++ b/src/main/client/manager/components/FeedSourceViewer.js @@ -3,20 +3,17 @@ import {Icon} from '@conveyal/woonerf' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' import { LinkContainer } from 'react-router-bootstrap' -import { Grid, Row, Col, ListGroup, ListGroupItem, Button, Badge, Panel, Glyphicon, ButtonToolbar, Tabs, Tab, FormControl, InputGroup, ControlLabel, FormGroup, Checkbox } from 'react-bootstrap' +import { Grid, Row, Col, ListGroup, ListGroupItem, Button, Badge, Panel, Glyphicon, Tabs, Tab, FormControl, InputGroup, ControlLabel, FormGroup, Checkbox } from 'react-bootstrap' import { Link, browserHistory } from 'react-router' -import moment from 'moment' -import numeral from 'numeral' import ManagerPage from '../../common/components/ManagerPage' import Breadcrumbs from '../../common/components/Breadcrumbs' -import WatchButton from '../../common/containers/WatchButton' -import StarButton from '../../common/containers/StarButton' import ExternalPropertiesTable from './ExternalPropertiesTable' +import ManagerHeader from './ManagerHeader' import ActiveFeedVersionNavigator from '../containers/ActiveFeedVersionNavigator' import NotesViewer from './NotesViewer' import ActiveEditorFeedSourcePanel from '../../editor/containers/ActiveEditorFeedSourcePanel' -import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' +import { isModuleEnabled, getComponentMessages, getMessage } from '../../common/util/config' export default class FeedSourceViewer extends Component { @@ -69,18 +66,6 @@ export default class FeedSourceViewer extends Component { this.setState({didMount: false}) } } - - getAverageFileSize (feedVersions) { - let sum = 0 - let avg - if (feedVersions) { - for (var i = 0; i < feedVersions.length; i++) { - sum += feedVersions[i].fileSize - } - avg = sum / feedVersions.length - } - return numeral(avg || 0).format('0 b') - } confirmDeleteFeedSource (feedSource) { this.refs['page'].showConfirmModal({ title: 'Delete Feed Source?', @@ -130,9 +115,7 @@ export default class FeedSourceViewer extends Component { const messages = getComponentMessages('FeedSourceViewer') const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') - const isWatchingFeed = this.props.user.subscriptions.hasFeedSubscription(this.props.project.id, fs.id, 'feed-updated') - const editGtfsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'edit-gtfs') - const dateFormat = getConfigProperty('application.date_format') + // const editGtfsDisabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'edit-gtfs') const autoFetchFeed = fs.retrievalMethod === 'FETCHED_AUTOMATICALLY' const resourceType = this.props.activeComponent === 'settings' && this.props.activeSubComponent && this.props.activeSubComponent.toUpperCase() const activeTab = ['settings', 'comments', 'snapshots'].indexOf(this.props.activeComponent) === -1 || typeof this.props.routeParams.feedVersionIndex !== 'undefined' @@ -221,15 +204,15 @@ export default class FeedSourceViewer extends Component { : - { - this.props.externalPropertyChanged(fs, resourceType, name, value) - }} - /> - + { + this.props.externalPropertyChanged(fs, resourceType, name, value) + }} + /> + return ( } > - + - {/* Title + Shortcut Buttons Row */} - -

    - - {this.props.project.name} - {' '}/{' '} - {fs.name}{' '} - {fs.isPublic ? null : } - {' '} - {fs.editedSinceSnapshot - ? - : - } - - - - - - -

    -
      -
    • {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
    • -
    • {fs.url ? fs.url : '(none)'} -
    • - {
    • {this.getAverageFileSize(fs.feedVersions)}
    • } -
    - {/*
  • {fs.feedVersionCount}
  • {fs.url}*/} - -
    + {/* Feed Versions tab */} browserHistory.push(`/feed/${fs.id}/${eventKey}`))} > {getMessage(messages, 'gtfs')}}> - - {/* - -

    Feed Summary

    -
      -
    • Last Updated: {fs.lastUpdated ? moment(fs.lastUpdated).format(dateFormat) : 'n/a'}
    • -
    • Number of versions: {fs.feedVersionCount}
    • -
      -
    • URL: this.props.feedSourcePropertyChanged(fs, 'url', value)} - /> -
    • -
    -
    - */} - - - {/**/} - {/* - - - {isModuleEnabled('editor') - ? - : null - } - - - - -
    - Fetch URL:  -
    -
    - this.props.feedSourcePropertyChanged(fs, 'url', value)} - /> -
    - -
    - */} - - - - {/* - - - - - - - - -
    Create new version
    -
      -
    • -
    • -
      -
    • -
    - -
    */} - {/* - New version} - > - Upload - Fetch - - From snapshot - - - */} - -
    - {/* - 12 Versions - */} - {/* - Snapshots}> - - - Snapshot 1 - - - - */}
    {isModuleEnabled('editor') ? {getComponentMessages('EditorFeedSourcePanel').title} {fs.editorSnapshots ? fs.editorSnapshots.length : 0}} - > - - + title={{getComponentMessages('EditorFeedSourcePanel').title} {fs.editorSnapshots ? fs.editorSnapshots.length : 0}} + > + + : null } {/* Comments for feed source */} @@ -459,140 +279,20 @@ export default class FeedSourceViewer extends Component { {getMessage(messages, 'properties.title')}}> - - - General - {Object.keys(fs.externalProperties || {}).map(resourceType => { - const resourceLowerCase = resourceType.toLowerCase() - return ( - {toSentenceCase(resourceType)} properties - ) - })} - - - - - {/* - - - - - - - - - - - - - - - - - { - this.props.feedSourcePropertyChanged(fs, 'retrievalMethod', evt.target.value) - }} - > - {retrievalMethods.map(method => { - return - })} - - - - {this.props.feedSource.retrievalMethod === 'MANUALLY_UPLOADED' - ? - : - } - - - - - - {fs.retrievalMethod === 'FETCHED_AUTOMATICALLY' - ? - - - - : null - } - - - - - - - - - - -
    {getMessage(messages, 'properties.property')}{getMessage(messages, 'properties.value')}
    Name - this.props.feedSourcePropertyChanged(fs, 'name', value)} - /> -
    {getMessage(messages, 'properties.retrievalMethod.title')} - -
    Retrieval URL - this.props.feedSourcePropertyChanged(fs, 'url', value)} - /> -
    {getMessage(messages, 'properties.public')} - { - this.props.feedSourcePropertyChanged(fs, 'isPublic', e.target.checked) - }} - /> -
    {getMessage(messages, 'properties.deployable')} - { - this.props.feedSourcePropertyChanged(fs, 'deployable', e.target.checked) - }} - /> -
    - */} + + + General + {Object.keys(fs.externalProperties || {}).map(resourceType => { + const resourceLowerCase = resourceType.toLowerCase() + return ( + {toSentenceCase(resourceType)} properties + ) + })} + + + {activeSettings} - {/* - - {Object.keys(fs.externalProperties || {}).map(resourceType => { - return ( - { - this.props.externalPropertyChanged(fs, resourceType, name, value) - }} - /> - ) - })} - */} -
    diff --git a/src/main/client/manager/components/FeedVersionNavigator.js b/src/main/client/manager/components/FeedVersionNavigator.js index 634de6ece..7efb5c0c1 100644 --- a/src/main/client/manager/components/FeedVersionNavigator.js +++ b/src/main/client/manager/components/FeedVersionNavigator.js @@ -65,8 +65,6 @@ export default class FeedVersionNavigator extends Component { console.log(`Error version ${this.props.feedVersionIndex} does not exist`) } - const publicPrefix = this.props.isPublic ? '/public' : '' - return (
    {/* Version Navigation Widget and Name Editor */} - - - - - - {this.state.listView - ? null - : {/* Version Navigation/Selection Widget */} - {/* Previous Version Button */} - + + + + + + {this.state.listView + ? null + : {/* Version Navigation/Selection Widget */} + {/* Previous Version Button */} + - {/* Version Selector Dropdown */} - { - if (key !== this.props.feedVersionIndex) { - this.props.setVersionIndex(fs, key) - } - }} - > - {versions.map((version, k) => { - k = k + 1 - return {k}. {version.name} - })} - + {/* Version Selector Dropdown */} + { + if (key !== this.props.feedVersionIndex) { + this.props.setVersionIndex(fs, key) + } + }} + > + {versions.map((version, k) => { + k = k + 1 + return {k}. {version.name} + })} + - {/* Next Version Button */} - - - } - - {isModuleEnabled('deployment') - ? - : null - } - {isModuleEnabled('editor') - ? - : null + {/* Next Version Button */} + + + } + + {isModuleEnabled('deployment') + ? + : null + } + {isModuleEnabled('editor') + ? + : null + } + Create new version} id='bg-nested-dropdown' + onSelect={key => { + console.log(key) + switch (key) { + case 'delete': + return this.refs['deleteModal'].open() + case 'fetch': + return this.props.fetchFeed(fs) + case 'upload': + return this.refs['uploadModal'].open() + case 'deploy': + return this.props.createDeploymentFromFeedSource(fs) + case 'public': + return browserHistory.push(`/public/feed/${fs.id}`) } - Create new version} id='bg-nested-dropdown' - onSelect={key => { - console.log(key) - switch (key) { - case 'delete': - return this.refs['deleteModal'].open() - case 'fetch': - return this.props.fetchFeed(fs) - case 'upload': - return this.refs['uploadModal'].open() - case 'deploy': - return this.props.createDeploymentFromFeedSource(fs) - case 'public': - return browserHistory.push(`/public/feed/${fs.id}`) - } - }} - > - Fetch - Upload - - From snapshot - - - + }} + > + Fetch + Upload + + From snapshot + + + diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index 6f93e507f..4e7db41f1 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -65,6 +65,81 @@ export default class FeedVersionReport extends Component { return 'Click on map above to show travel shed for this feed.' } } + renderValidatorTabs () { + if (!isModuleEnabled('validator')) { + return null + } + const validatorTabs = [ + { + title: 'Validation', + key: 'validation', + component: { this.props.fetchValidationResult(this.props.version) }} + /> + }, + { + title: 'Accessibility', + key: 'accessibility', + component:
    + + + {/* isochrone message */} +

    + {this.renderIsochroneMessage(this.props.version)} +

    + +
    + + + Travel time + this.setState({isochroneBand: value * 60})} + step={5} + marks={{ + 15: '¼ hour', + 30: '½ hour', + 60: 1 hour, + 120: '2 hours' + }} + tipFormatter={(value) => { + return `${value} minutes` + }} + /> + + + +
    + }, + { + title: 'Timeline', + key: 'timeline', + component:
    + + +

    Number of trips per date of service.

    + +
    + { this.props.fetchValidationResult(this.props.version) }} + /> +
    + } + ] + return validatorTabs.map(t => ( + + {t.component} + + )) + } render () { const version = this.props.version const messages = getComponentMessages('FeedVersionReport') @@ -111,6 +186,7 @@ export default class FeedVersionReport extends Component { sizePerPageList: [10, 20, 50, 100] } } + const countFields = ['agencyCount', 'routeCount', 'tripCount', 'stopTimesCount'] return ( - -

    {numeral(version.validationSummary.agencyCount).format('0 a')}

    -

    {getMessage(messages, 'agencyCount')}

    - - -

    {numeral(version.validationSummary.routeCount).format('0 a')}

    -

    {getMessage(messages, 'routeCount')}

    - - -

    {numeral(version.validationSummary.tripCount).format('0 a')}

    -

    {getMessage(messages, 'tripCount')}

    - - -

    {numeral(version.validationSummary.stopTimesCount).format('0 a')}

    -

    {getMessage(messages, 'stopTimesCount')}

    - + {countFields.map(c => ( + +

    {numeral(version.validationSummary[c]).format('0 a')}

    +

    {getMessage(messages, c)}

    + + ))}
    @@ -223,66 +289,7 @@ export default class FeedVersionReport extends Component { tableOptions={tableOptions} /> - {isModuleEnabled('validator') - ? [ - - { this.props.fetchValidationResult(this.props.version) }} - /> - , - -
    - - - {/* isochrone message */} -

    - {this.renderIsochroneMessage(this.props.version)} -

    - -
    - - - Travel time - this.setState({isochroneBand: value * 60})} - step={5} - marks={{ - 15: '¼ hour', - 30: '½ hour', - 60: 1 hour, - 120: '2 hours' - }} - tipFormatter={(value) => { - return `${value} minutes` - }} - /> - - - -
    -
    , - - - -

    Number of trips per date of service.

    - -
    - { this.props.fetchValidationResult(version) }} - /> -
    - ] - : null - } + {this.renderValidatorTabs()} diff --git a/src/main/client/manager/components/FeedVersionViewer.js b/src/main/client/manager/components/FeedVersionViewer.js index 2864c15c2..90e67b659 100644 --- a/src/main/client/manager/components/FeedVersionViewer.js +++ b/src/main/client/manager/components/FeedVersionViewer.js @@ -5,12 +5,12 @@ import { LinkContainer } from 'react-router-bootstrap' import {Icon} from '@conveyal/woonerf' import GtfsValidationViewer from './validation/GtfsValidationViewer' -import GtfsValidationExplorer from './validation/GtfsValidationExplorer' +// import GtfsValidationExplorer from './validation/GtfsValidationExplorer' import FeedVersionReport from './FeedVersionReport' import NotesViewer from './NotesViewer' import ConfirmModal from '../../common/components/ConfirmModal' import ActiveGtfsPlusVersionSummary from '../../gtfsplus/containers/ActiveGtfsPlusVersionSummary' -import { isModuleEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' +import { isModuleEnabled, getComponentMessages, getMessage } from '../../common/util/config' export default class FeedVersionViewer extends Component { @@ -42,19 +42,21 @@ export default class FeedVersionViewer extends Component { return ( - + ) } switch (this.props.versionSection) { - case 'validation': - return + // case 'validation': + // return ( + // + // ) default: return ( @@ -68,33 +70,32 @@ export default class FeedVersionViewer extends Component { {!this.props.versionSection ? + isPublished={version.id === this.props.feedSource.publishedVersionId} + {...this.props} + /> : this.props.versionSection === 'issues' ? { this.props.fetchValidationResult(version) }} - /> + validationResult={version.validationResult} + version={version} + fetchValidationResult={() => { this.props.fetchValidationResult(version) }} + /> : this.props.versionSection === 'gtfsplus' && isModuleEnabled('gtfsplus') ? + version={version} + /> : this.props.versionSection === 'comments' ? { this.props.notesRequested() }} - newNotePosted={(note) => { this.props.newNotePosted(note) }} - /> + type='feed-version' + stacked + user={this.props.user} + version={this.props.version} + notes={version.notes} + noteCount={version.noteCount} + notesRequested={() => { this.props.notesRequested() }} + newNotePosted={(note) => { this.props.newNotePosted(note) }} + /> : null } - {/**/} ) @@ -121,21 +122,21 @@ export class VersionButtonToolbar extends Component { const messages = getComponentMessages('FeedVersionViewer') return (
    - - + + - {/* "Download Feed" Button */} - + {/* "Download Feed" Button */} + - {/* "Load for Editing" Button */} - {isModuleEnabled('editor') && !this.props.isPublic - ? - : null - } + : null + } - {/* "Delete Version" Button */} - {!this.props.isPublic - ? - : null - } - + : null + } +
    ) } @@ -204,12 +205,16 @@ class VersionSectionSelector extends Component { Version summary - Validation issues {this.renderIssuesLabel(version)} - + + Validation issues {this.renderIssuesLabel(version)} + + {isModuleEnabled('gtfsplus') ? - GTFS+ for this version - + + GTFS+ for this version + + : null } @@ -238,23 +243,23 @@ class VersionList extends Component { {this.props.versions ? this.props.versions.map(v => { - return ( - - {v.name} - {' '} - + return ( + + {v.name} + {' '} + {this.getVersionDateLabel(v)} - - - - ) - }) + + + + ) + }) : - No versions - + No versions + }
    diff --git a/src/main/client/manager/components/GeneralSettings.js b/src/main/client/manager/components/GeneralSettings.js new file mode 100644 index 000000000..090324488 --- /dev/null +++ b/src/main/client/manager/components/GeneralSettings.js @@ -0,0 +1,257 @@ +import React, {Component} from 'react' +import {Icon} from '@conveyal/woonerf' +import ReactDOM from 'react-dom' +import DateTimeField from 'react-bootstrap-datetimepicker' +import update from 'react-addons-update' +import { shallowEqual } from 'react-pure-render' +import moment from 'moment' +import { Row, Col, Button, Panel, Glyphicon, Checkbox, FormGroup, InputGroup, ControlLabel, FormControl, ListGroup, ListGroupItem } from 'react-bootstrap' + +import { getMessage, getComponentMessages } from '../../common/util/config' +import TimezoneSelect from '../../common/components/TimezoneSelect' +import LanguageSelect from '../../common/components/LanguageSelect' + +export default class GeneralSettings extends Component { + constructor (props) { + super(props) + this.state = { + general: {} + } + } + componentWillReceiveProps (nextProps) { + this.setState({ + general: {} + }) + } + shouldComponentUpdate (nextProps, nextState) { + return !shallowEqual(nextProps, this.props) || !shallowEqual(nextState, this.state) + } + render () { + const messages = getComponentMessages('ProjectSettings') + const { project, editDisabled, updateProjectSettings, deleteProject } = this.props + const noEdits = Object.keys(this.state.general).length === 0 && this.state.general.constructor === Object + const autoFetchChecked = typeof this.state.general.autoFetchFeeds !== 'undefined' ? this.state.general.autoFetchFeeds : project.autoFetchFeeds + const DEFAULT_FETCH_TIME = moment().startOf('day').add(2, 'hours') + return ( +
    + {getMessage(messages, 'title')}}> + + + + Organization name + + { + this.setState({name: evt.target.value}) + }} + /> + + + + + + + + + {getMessage(messages, 'general.updates.title')}}> + + + + { + let minutes = moment(DEFAULT_FETCH_TIME).minutes() + let hours = moment(DEFAULT_FETCH_TIME).hours() + let stateUpdate = { general: { $merge: { autoFetchFeeds: evt.target.checked, autoFetchMinute: minutes, autoFetchHour: hours } } } + this.setState(update(this.state, stateUpdate)) + }} + > + {getMessage(messages, 'general.updates.autoFetchFeeds')} + + {autoFetchChecked + ? { + let time = moment(+seconds) + let minutes = moment(time).minutes() + let hours = moment(time).hours() + let stateUpdate = { general: { $merge: { autoFetchMinute: minutes, autoFetchHour: hours } } } + this.setState(update(this.state, stateUpdate)) + }} + /> + : null + } + + + + + {getMessage(messages, 'general.location.title')}}> + + + + {getMessage(messages, 'general.location.defaultLocation')} + + { + const latLng = evt.target.value.split(',') + if (typeof latLng[0] !== 'undefined' && typeof latLng[1] !== 'undefined') { + let stateUpdate = { general: { $merge: {defaultLocationLat: latLng[0], defaultLocationLon: latLng[1]} } } + this.setState(update(this.state, stateUpdate)) + } else { + console.log('invalid value for latlng') + } + }} + /> + + + + + + + + + {getMessage(messages, 'general.location.boundingBox')} + + { + const bBox = evt.target.value.split(',') + if (bBox.length === 4) { + let stateUpdate = { general: { $merge: {west: bBox[0], south: bBox[1], east: bBox[2], north: bBox[3]} } } + this.setState(update(this.state, stateUpdate)) + } + }} + /> + { + + + + } + + + + + {getMessage(messages, 'general.location.defaultTimeZone')} + { + let stateUpdate = { general: { $merge: { defaultTimeZone: option.value } } } + this.setState(update(this.state, stateUpdate)) + }} + /> + + + {getMessage(messages, 'general.location.defaultLanguage')} + { + let stateUpdate = { general: { $merge: { defaultLanguage: option.value } } } + this.setState(update(this.state, stateUpdate)) + }} + /> + + + + Danger zone}> + + + +

    Delete this organization.

    +

    Once you delete an organization, the organization and all feed sources it contains cannot be recovered.

    +
    +
    +
    + + + {/* Save button */} + + + +
    + ) + } +} diff --git a/src/main/client/manager/components/ManagerHeader.js b/src/main/client/manager/components/ManagerHeader.js new file mode 100644 index 000000000..5a42be99c --- /dev/null +++ b/src/main/client/manager/components/ManagerHeader.js @@ -0,0 +1,86 @@ +import React, {Component, PropTypes} from 'react' +import {Icon} from '@conveyal/woonerf' +import { Row, Col, Button, ButtonToolbar } from 'react-bootstrap' +import { Link } from 'react-router' +import moment from 'moment' +import numeral from 'numeral' + +import WatchButton from '../../common/containers/WatchButton' +import StarButton from '../../common/containers/StarButton' +import { getConfigProperty } from '../../common/util/config' + +export default class ManagerHeader extends Component { + static propTypes = { + feedSource: PropTypes.object, + user: PropTypes.object + } + getAverageFileSize (feedVersions) { + let sum = 0 + let avg + if (feedVersions) { + for (var i = 0; i < feedVersions.length; i++) { + sum += feedVersions[i].fileSize + } + avg = sum / feedVersions.length + } + return numeral(avg || 0).format('0 b') + } + render () { + const { feedSource, project, user } = this.props + const isWatchingFeed = user.subscriptions.hasFeedSubscription(project.id, feedSource.id, 'feed-updated') + const dateFormat = getConfigProperty('application.date_format') + return ( + {/* Title + Shortcut Buttons Row */} + +

    + + {this.props.project.name} + {' '}/{' '} + {feedSource.name}{' '} + {feedSource.isPublic ? null : } + {' '} + {feedSource.editedSinceSnapshot + ? + : + } + + + + + + +

    +
      +
    • {feedSource.lastUpdated ? moment(feedSource.lastUpdated).format(dateFormat) : 'n/a'}
    • +
    • {feedSource.url ? feedSource.url : '(none)'} +
    • + {
    • {this.getAverageFileSize(feedSource.feedVersions)}
    • } +
    + {/*
  • {feedSource.feedVersionCount}
  • {feedSource.url} */} + +
    + ) + } +} diff --git a/src/main/client/manager/components/NotesViewer.js b/src/main/client/manager/components/NotesViewer.js index 3b6387691..e76466353 100644 --- a/src/main/client/manager/components/NotesViewer.js +++ b/src/main/client/manager/components/NotesViewer.js @@ -1,7 +1,6 @@ import React, {Component, PropTypes} from 'react' import moment from 'moment' import gravatar from 'gravatar' -import ReactDOM from 'react-dom' import { Panel, Row, Col, Glyphicon, FormControl, Button, ButtonToolbar, Media } from 'react-bootstrap' import WatchButton from '../../common/containers/WatchButton' @@ -61,26 +60,26 @@ export default class NotesViewer extends Component { {this.props.notes && this.props.notes.length > 0 ? this.props.notes.map(note => { - return ( - - - {note.userEmail} - - - - {this.props.user.profile.email} commented {moment(note.date).fromNow()} - - } - > + return ( + + + {note.userEmail} + + + + {this.props.user.profile.email} commented {moment(note.date).fromNow()} + + } + >

    {note.body || '(no content)'}

    -
    -
    -
    - ) - }) +
    +
    +
    + ) + }) :

    {getMessage(messages, 'none')}

    } @@ -88,7 +87,7 @@ export default class NotesViewer extends Component {

    {getMessage(messages, 'postComment')}

    - + diff --git a/src/main/client/manager/components/ProjectSettings.js b/src/main/client/manager/components/ProjectSettings.js index 84960772b..2e901ddd1 100644 --- a/src/main/client/manager/components/ProjectSettings.js +++ b/src/main/client/manager/components/ProjectSettings.js @@ -1,17 +1,9 @@ import React, {Component, PropTypes} from 'react' -import ReactDOM from 'react-dom' -import moment from 'moment' -// import moment_tz from 'moment-timezone' -import DateTimeField from 'react-bootstrap-datetimepicker' -import update from 'react-addons-update' -import { shallowEqual } from 'react-pure-render' import { LinkContainer } from 'react-router-bootstrap' -import {Icon} from '@conveyal/woonerf' -import { Row, Col, Button, Panel, Glyphicon, Form, Tabs, Tab, Radio, Checkbox, FormGroup, InputGroup, ControlLabel, FormControl, ListGroup, ListGroupItem } from 'react-bootstrap' -import TimezoneSelect from '../../common/components/TimezoneSelect' -import LanguageSelect from '../../common/components/LanguageSelect' -// import languages from '../../common/util/languages' +import { Row, Col, Panel, ListGroup, ListGroupItem } from 'react-bootstrap' +import GeneralSettings from './GeneralSettings' +import DeploymentSettings from './DeploymentSettings' import { isModuleEnabled, getComponentMessages, getMessage } from '../../common/util/config' import MapModal from '../../common/components/MapModal.js' @@ -22,578 +14,43 @@ export default class ProjectSettings extends Component { updateProjectSettings: PropTypes.func, deleteProject: PropTypes.func } - constructor (props) { - super(props) - this.state = { - general: {}, - deployment: { - buildConfig: {}, - routerConfig: {}, - otpServers: this.props.project && this.props.project.otpServers ? this.props.project.otpServers : [] - } - } - } - - componentWillReceiveProps (nextProps) { - this.setState({ - general: {}, - deployment: { - buildConfig: {}, - routerConfig: {}, - otpServers: nextProps.project && nextProps.project.otpServers ? nextProps.project.otpServers : [] - } - }) - } - shouldComponentUpdate (nextProps, nextState) { - return !shallowEqual(nextProps, this.props) || !shallowEqual(nextState, this.state) - } render () { + const { + project, + projectEditDisabled, + activeSettingsPanel, + updateProjectSettings, + deleteProject + } = this.props const messages = getComponentMessages('ProjectSettings') - const project = this.props.project - const generalSettingsUnedited = Object.keys(this.state.general).length === 0 && this.state.general.constructor === Object - const deploymentSettingsUnedited = - Object.keys(this.state.deployment.buildConfig).length === 0 && - Object.keys(this.state.deployment.buildConfig).length === 0 && - shallowEqual(this.state.deployment.otpServers, this.props.project.otpServers) - const autoFetchChecked = typeof this.state.general.autoFetchFeeds !== 'undefined' ? this.state.general.autoFetchFeeds : project.autoFetchFeeds - const projectEditDisabled = this.props.projectEditDisabled - const defaultFetchTime = moment().startOf('day').add(2, 'hours') - const activeSettingsPanel = !this.props.activeSettingsPanel - ?
    - {getMessage(messages, 'title')}}> - - - - Organization name - - { - this.setState({name: evt.target.value}) - }} - /> - - - - - - - - - {getMessage(messages, 'general.updates.title')}}> - - - - { - let minutes = moment(defaultFetchTime).minutes() - let hours = moment(defaultFetchTime).hours() - let stateUpdate = { general: { $merge: { autoFetchFeeds: evt.target.checked, autoFetchMinute: minutes, autoFetchHour: hours } } } - this.setState(update(this.state, stateUpdate)) - }} - > - {getMessage(messages, 'general.updates.autoFetchFeeds')} - - {autoFetchChecked - ? { - let time = moment(+seconds) - let minutes = moment(time).minutes() - let hours = moment(time).hours() - let stateUpdate = { general: { $merge: { autoFetchMinute: minutes, autoFetchHour: hours } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - : null - } - - - - - {getMessage(messages, 'general.location.title')}}> - - - - {getMessage(messages, 'general.location.defaultLocation')} - - { - const latLng = evt.target.value.split(',') - if (typeof latLng[0] !== 'undefined' && typeof latLng[1] !== 'undefined') { - let stateUpdate = { general: { $merge: {defaultLocationLat: latLng[0], defaultLocationLon: latLng[1]} } } - this.setState(update(this.state, stateUpdate)) - } else { - console.log('invalid value for latlng') - } - }} - /> - - - - - - - - - {getMessage(messages, 'general.location.boundingBox')} - - { - const bBox = evt.target.value.split(',') - if (bBox.length === 4) { - let stateUpdate = { general: { $merge: {west: bBox[0], south: bBox[1], east: bBox[2], north: bBox[3]} } } - this.setState(update(this.state, stateUpdate)) - } - }} - /> - { - - - - } - - - - - {getMessage(messages, 'general.location.defaultTimeZone')} - { - let stateUpdate = { general: { $merge: { defaultTimeZone: option.value } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - {getMessage(messages, 'general.location.defaultLanguage')} - { - let stateUpdate = { general: { $merge: { defaultLanguage: option.value } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - Danger zone}> + const activePanel = !activeSettingsPanel + ? + : + return ( + + + - - -

    Delete this organization.

    -

    Once you delete an organization, the organization and all feed sources it contains cannot be recovered.

    -
    + {getMessage(messages, 'general.title')} + {isModuleEnabled('deployment') + ? {getMessage(messages, 'deployment.title')} + : null + }
    - - - {/* Save button */} - - - -
    - :
    - {getMessage(messages, 'deployment.buildConfig.title')}}> - - - - { - let stateUpdate = { deployment: { buildConfig: { fetchElevationUS: { $set: (evt.target.value === 'true') } } } } - this.setState(update(this.state, stateUpdate)) - }} - > - - - - - - - - { - let stateUpdate = { deployment: { buildConfig: { stationTransfers: { $set: (evt.target.value === 'true') } } } } - this.setState(update(this.state, stateUpdate)) - }} - > - - - - - - - - { - let stateUpdate = { deployment: { buildConfig: { subwayAccessTime: { $set: +evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - { - let stateUpdate = { deployment: { buildConfig: { fares: { $set: evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - Router Config}> - - - - { - let stateUpdate = { deployment: { routerConfig: { numItineraries: { $set: +evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - - { - let stateUpdate = { deployment: { routerConfig: { walkSpeed: { $set: +evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - - - - { - let stateUpdate = { deployment: { routerConfig: { stairsReluctance: { $set: +evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - - { - let stateUpdate = { deployment: { routerConfig: { carDropoffTime: { $set: +evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - - { - let stateUpdate = { deployment: { routerConfig: { brandingUrlRoot: { $set: evt.target.value } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - - - {getMessage(messages, 'deployment.servers.title')} - - }> -
    - {this.state.deployment.otpServers && this.state.deployment.otpServers.map((server, i) => { - let title = ( -
    - {server.name}{' '} - {server.publicUrl} -
    - ) - return ( - -
    - - - {getMessage(messages, 'deployment.servers.name')} - { - let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { name: evt.target.value } } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - {getMessage(messages, 'deployment.servers.public')} - { - let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { publicUrl: evt.target.value } } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - - {getMessage(messages, 'deployment.servers.internal')} - { - let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { internalUrl: evt.target.value.split(',') } } } } } - this.setState(update(this.state, stateUpdate)) - }} - /> - - { - let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { admin: evt.target.checked } } } } } - this.setState(update(this.state, stateUpdate)) - }} - > - {getMessage(messages, 'deployment.servers.admin')} - -
    -
    - ) - }) - - } -
    -
    - {getMessage(messages, 'deployment.osm.title')}}> - { - let stateUpdate = { deployment: { useCustomOsmBounds: { $set: (evt.target.value === 'true') } } } - this.setState(update(this.state, stateUpdate)) - }} - > - - {getMessage(messages, 'deployment.osm.gtfs')} - - - {getMessage(messages, 'deployment.osm.custom')} - - - {project.useCustomOsmBounds || this.state.deployment.useCustomOsmBounds - ? {getMessage(messages, 'deployment.osm.bounds')})} - ref='osmBounds' - onChange={(evt) => { - const bBox = evt.target.value.split(',') - if (bBox.length === 4) { - let stateUpdate = { deployment: { $merge: { osmWest: bBox[0], osmSouth: bBox[1], osmEast: bBox[2], osmNorth: bBox[3] } } } - this.setState(update(this.state, stateUpdate)) - } - }} - /> - : null - } - - - - {/* Save button */} - - - -
    - - return ( - - - - - {getMessage(messages, 'general.title')} - {isModuleEnabled('deployment') - ? {getMessage(messages, 'deployment.title')} - : null - } - - - - - {activeSettingsPanel} - - + + + {activePanel} + + - ) } } diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index 1306edb1d..e395dbfb7 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -1,8 +1,6 @@ import React, {Component, PropTypes} from 'react' import Helmet from 'react-helmet' -import moment from 'moment' -import { LinkContainer } from 'react-router-bootstrap' -import { Tabs, Tab, Grid, Row, Label, Col, Button, InputGroup, ListGroup, ListGroupItem, Table, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' +import { Tabs, Tab, Grid, Row, Col, Button, InputGroup, ListGroup, ListGroupItem, FormControl, Glyphicon, ButtonToolbar, Panel, DropdownButton, MenuItem } from 'react-bootstrap' import { sentence as toSentenceCase } from 'change-case' import {Icon} from '@conveyal/woonerf' import { browserHistory, Link } from 'react-router' @@ -12,9 +10,9 @@ import ManagerPage from '../../common/components/ManagerPage' import Breadcrumbs from '../../common/components/Breadcrumbs' import WatchButton from '../../common/containers/WatchButton' import ProjectSettings from './ProjectSettings' -import ActiveDeploymentViewer from '../containers/ActiveDeploymentViewer' +import DeploymentsPanel from './DeploymentsPanel' import FeedSourceTable from './FeedSourceTable' -import EditableTextField from '../../common/components/EditableTextField' +import ThirdPartySyncButton from './ThirdPartySyncButton' import { defaultSorter } from '../../common/util/util' import { isModuleEnabled, isExtensionEnabled, getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' @@ -138,47 +136,10 @@ export default class ProjectViewer extends Component { {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') - ? Sync}> - {isExtensionEnabled('transitland') - ? { - this.props.thirdPartySync('TRANSITLAND') - }} - > - transit.land - - : null - } - {isExtensionEnabled('transitfeeds') - ? { - this.props.thirdPartySync('TRANSITFEEDS') - }} - > - transitfeeds.com - - : null - } - {isExtensionEnabled('mtc') - ? { - this.props.thirdPartySync('MTC') - }} - > - MTC - - : null - } - + ? : null } - - - -
    - ) - } -} - -class DeploymentsList extends Component { - render () { - const messages = getComponentMessages('DeploymentsPanel') - const na = (N/A) - return ( - - - this.props.searchTextChanged(evt.target.value)} - /> - - - - - - } - > - - - - - - - - - - - - {this.props.deployments - ? this.props.deployments.map(dep => { - return ( - - - - - - - - ) - }) - : null - } - -
    {getMessage(messages, 'table.name')}{getMessage(messages, 'table.creationDate')}{getMessage(messages, 'table.deployedTo')}{getMessage(messages, 'table.feedCount')}
    - { - if (dep.isCreating) this.props.newDeploymentNamed(value) - else this.props.updateDeployment(dep, {name: value}) - }} - link={`/project/${dep.project.id}/deployments/${dep.id}`} - /> - - {dep.dateCreated - ? ({moment(dep.dateCreated).format('MMM Do YYYY')} ({moment(dep.dateCreated).fromNow()})) - : na - } - - {dep.deployedTo - ? () - : na - } - - {dep.feedVersions - ? ({dep.feedVersions.length}) - : na - } - - -
    -
    - ) - } -} diff --git a/src/main/client/manager/components/ProjectsList.js b/src/main/client/manager/components/ProjectsList.js index 41f33c7c5..edbab0e2c 100644 --- a/src/main/client/manager/components/ProjectsList.js +++ b/src/main/client/manager/components/ProjectsList.js @@ -1,7 +1,6 @@ import React from 'react' import Helmet from 'react-helmet' import { Grid, Row, Col, Button, Table, FormControl, Panel, OverlayTrigger, Popover, Glyphicon } from 'react-bootstrap' -import { Link } from 'react-router' import ManagerPage from '../../common/components/ManagerPage' import EditableTextField from '../../common/components/EditableTextField' @@ -9,17 +8,10 @@ import defaultSorter from '../../common/util/util' import { getComponentMessages, getMessage } from '../../common/util/config' export default class ProjectsList extends React.Component { - - constructor (props) { - super(props) - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { - if (!this.props.projects) { return } @@ -37,78 +29,77 @@ export default class ProjectsList extends React.Component { /> Projects)}> - - - this.props.searchTextChanged(evt.target.value)} - /> - - - - - {getMessage(messages, 'help.content')}}> - - - - - - - - - - - - - - - {visibleProjects.length > 0 ? visibleProjects.map((project) => { - let disabled = !this.props.user.permissions.isProjectAdmin(project.id) - return ( - - - - - ) - }) - : - - } - -
    {getMessage(messages, 'table.name')}
    -
    - { - if (project.isCreating) this.props.newProjectNamed(value) - else this.props.projectNameChanged(project, value) - }} - link={`/project/${project.id}`} - /> -
    -
    -
    - {getMessage(messages, 'noProjects')} - {' '} - -
    - -
    + + + this.props.searchTextChanged(evt.target.value)} + /> + + + + {getMessage(messages, 'help.content')}}> + + + + + + + + + + + + + + {visibleProjects.length > 0 ? visibleProjects.map((project) => { + let disabled = !this.props.user.permissions.isProjectAdmin(project.id) + return ( + + + + ) + }) + : + + + } + +
    {getMessage(messages, 'table.name')} +
    +
    + { + if (project.isCreating) this.props.newProjectNamed(value) + else this.props.projectNameChanged(project, value) + }} + link={`/project/${project.id}`} + /> +
    +
    +
    + {getMessage(messages, 'noProjects')} + {' '} + +
    + +
    diff --git a/src/main/client/manager/components/ThirdPartySyncButton.js b/src/main/client/manager/components/ThirdPartySyncButton.js new file mode 100644 index 000000000..987240562 --- /dev/null +++ b/src/main/client/manager/components/ThirdPartySyncButton.js @@ -0,0 +1,54 @@ +import React, {Component} from 'react' +import { DropdownButton, MenuItem, Glyphicon } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' + +import { isExtensionEnabled } from '../../common/util/config' + +export default class ThirdPartySyncButton extends Component { + render () { + const { projectEditDisabled, thirdPartySync } = this.props + return ( + Sync}> + {isExtensionEnabled('transitland') + ? { + thirdPartySync('TRANSITLAND') + }} + > + transit.land + + : null + } + {isExtensionEnabled('transitfeeds') + ? { + thirdPartySync('TRANSITFEEDS') + }} + > + transitfeeds.com + + : null + } + {isExtensionEnabled('mtc') + ? { + thirdPartySync('MTC') + }} + > + MTC + + : null + } + + ) + } +} diff --git a/src/main/client/manager/components/UserHomePage.js b/src/main/client/manager/components/UserHomePage.js index 0874829af..59cec66f8 100644 --- a/src/main/client/manager/components/UserHomePage.js +++ b/src/main/client/manager/components/UserHomePage.js @@ -166,17 +166,14 @@ export default class UserHomePage extends Component { // this.props.setActiveProject(eventKey) // }} > - {activeProject - ? [ - - - {this.props.user.profile.nickname} - - , - - ] - : null - } + {activeProject && ( + + + {this.props.user.profile.nickname} + + + )} + {activeProject && } {visibleProjects.length > 0 ? visibleProjects.map((project, index) => { if (activeProject && project.id === activeProject.id) { diff --git a/src/main/client/manager/components/reporter/components/DateTimeFilter.js b/src/main/client/manager/components/reporter/components/DateTimeFilter.js index 1e8265fa8..8aade7524 100644 --- a/src/main/client/manager/components/reporter/components/DateTimeFilter.js +++ b/src/main/client/manager/components/reporter/components/DateTimeFilter.js @@ -86,12 +86,12 @@ export default class DateTimeFilter extends Component { {!validDate ? - - - Warning! Chosen date is outside of valid date range for feed version. - - - + + + Warning! Chosen date is outside of valid date range for feed version. + + + : null }
    diff --git a/src/main/client/manager/components/reporter/components/FeedLayout.js b/src/main/client/manager/components/reporter/components/FeedLayout.js index f3c2e6250..d9ea1c180 100644 --- a/src/main/client/manager/components/reporter/components/FeedLayout.js +++ b/src/main/client/manager/components/reporter/components/FeedLayout.js @@ -1,13 +1,14 @@ import React, { PropTypes, Component } from 'react' -import { Grid, Alert } from 'react-bootstrap' +import { Alert } from 'react-bootstrap' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' import Loading from '../../../../common/components/Loading' - export default class FeedLayout extends Component { - static propTypes = {} + static propTypes = { + feed: PropTypes.object + } componentWillMount () { this.props.onComponentMount(this.props) } @@ -22,7 +23,7 @@ export default class FeedLayout extends Component { } {this.props.feed.fetchStatus.error && - + An error occurred while trying to fetch the data } diff --git a/src/main/client/manager/components/reporter/components/PageLayout.js b/src/main/client/manager/components/reporter/components/PageLayout.js index 2ac64f18d..1ecfcfc4e 100644 --- a/src/main/client/manager/components/reporter/components/PageLayout.js +++ b/src/main/client/manager/components/reporter/components/PageLayout.js @@ -9,7 +9,6 @@ import Patterns from '../containers/Patterns' import Routes from '../containers/Routes' import Stops from '../containers/Stops' - export default class PageLayout extends React.Component { render () { diff --git a/src/main/client/manager/components/reporter/components/PatternLayout.js b/src/main/client/manager/components/reporter/components/PatternLayout.js index 3a197384d..c29fa1c77 100644 --- a/src/main/client/manager/components/reporter/components/PatternLayout.js +++ b/src/main/client/manager/components/reporter/components/PatternLayout.js @@ -19,9 +19,6 @@ export default class PatternLayout extends Component { patterns: PropTypes.object, tableOptions: PropTypes.object } - constructor (props) { - super(props) - } componentWillMount () { this.props.onComponentMount(this.props) } diff --git a/src/main/client/manager/components/reporter/components/RouteLayout.js b/src/main/client/manager/components/reporter/components/RouteLayout.js index 5dbaba335..15a3c34e9 100644 --- a/src/main/client/manager/components/reporter/components/RouteLayout.js +++ b/src/main/client/manager/components/reporter/components/RouteLayout.js @@ -25,7 +25,7 @@ export default class RouteLayout extends Component { } {this.props.routes.fetchStatus.error && - + An error occurred while trying to fetch the data } @@ -54,8 +54,8 @@ export default class RouteLayout extends Component { dataFormat={(cell, row) => { return (
    diff --git a/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js b/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js index 2f2f34a36..37441ca2c 100644 --- a/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js +++ b/src/main/client/manager/components/reporter/containers/ActiveDateTimeFilter.js @@ -1,10 +1,8 @@ -import React from 'react' import { connect } from 'react-redux' import DateTimeFilter from '../components/DateTimeFilter' import { updateDateTimeFilter } from '../../../../gtfs/actions/filter' - const mapStateToProps = (state, ownProps) => { return { dateTime: state.gtfs.filter.dateTimeFilter diff --git a/src/main/client/manager/components/reporter/containers/Feed.js b/src/main/client/manager/components/reporter/containers/Feed.js index 6e9cd5c8b..86c9c9d42 100644 --- a/src/main/client/manager/components/reporter/containers/Feed.js +++ b/src/main/client/manager/components/reporter/containers/Feed.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import { fetchFeed } from '../../../../gtfs/actions/feed' diff --git a/src/main/client/manager/components/reporter/containers/Patterns.js b/src/main/client/manager/components/reporter/containers/Patterns.js index 04e4a011b..079c04a3b 100644 --- a/src/main/client/manager/components/reporter/containers/Patterns.js +++ b/src/main/client/manager/components/reporter/containers/Patterns.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import PatternLayout from '../components/PatternLayout' @@ -33,7 +32,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, patternDateTimeFilterChange: (props) => { dispatch(patternDateTimeFilterChange(feedId, props)) - }, + } } } diff --git a/src/main/client/manager/components/reporter/containers/Routes.js b/src/main/client/manager/components/reporter/containers/Routes.js index bfb3323eb..a0964f82b 100644 --- a/src/main/client/manager/components/reporter/containers/Routes.js +++ b/src/main/client/manager/components/reporter/containers/Routes.js @@ -1,12 +1,9 @@ -import React from 'react' import { connect } from 'react-redux' import RouteLayout from '../components/RouteLayout' -// import { selectTab } from '../../../../gtfs/actions/pageLayout' import { fetchRoutes } from '../../../../gtfs/actions/routes' import { patternRouteFilterChange } from '../../../../gtfs/actions/patterns' - const mapStateToProps = (state, ownProps) => { return { routes: state.gtfs.routes diff --git a/src/main/client/manager/components/reporter/containers/Stops.js b/src/main/client/manager/components/reporter/containers/Stops.js index 6d3148c60..90a20d29a 100644 --- a/src/main/client/manager/components/reporter/containers/Stops.js +++ b/src/main/client/manager/components/reporter/containers/Stops.js @@ -1,12 +1,9 @@ -import React from 'react' import { connect } from 'react-redux' import StopLayout from '../components/StopLayout' -import { fetchPatterns } from '../../../../gtfs/actions/patterns' -import { fetchStops, stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../../../../gtfs/actions/stops' +import { stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../../../../gtfs/actions/stops' import { fetchRoutes } from '../../../../gtfs/actions/routes' - const mapStateToProps = (state, ownProps) => { return { stops: state.gtfs.stops, @@ -35,7 +32,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, stopDateTimeFilterChange: (props) => { dispatch(stopDateTimeFilterChange(feedId, props)) - }, + } // viewStops: (row) => { // dispatch(stopPatternFilterChange(feedId, row)) // dispatch(ownProps.selectTab('stops')) diff --git a/src/main/client/manager/components/validation/GtfsValidationExplorer.js b/src/main/client/manager/components/validation/GtfsValidationExplorer.js index fa6f9b7a9..b931410b2 100644 --- a/src/main/client/manager/components/validation/GtfsValidationExplorer.js +++ b/src/main/client/manager/components/validation/GtfsValidationExplorer.js @@ -1,114 +1,98 @@ import React, {Component, PropTypes} from 'react' -import { Grid, Row, Col, ButtonGroup, Button, Glyphicon, Tabs, Tab } from 'react-bootstrap' -import { Link, browserHistory } from 'react-router' +import { Row, Col, Tabs, Tab } from 'react-bootstrap' -import ManagerPage from '../../../common/components/ManagerPage' -import Breadcrumbs from '../../../common/components/Breadcrumbs' import IssuesMap from './IssuesMap' import IsochroneMap from './IsochroneMap' import GtfsValidationSummary from './GtfsValidationSummary' import TripsChart from './TripsChart' -import { getComponentMessages, getMessage } from '../../../common/util/config' export default class GtfsValidationExplorer extends Component { - + static propTypes = { + version: PropTypes.object + } constructor (props) { super(props) this.state = { activeTab: 'issues' } } - componentWillMount () { // this.props.onComponentMount(this.props) if (this.props.version && !this.props.version.validationResult) { this.props.fetchValidationResult(this.props.version) } } - componentWillReceiveProps (nextProps) { - // if (this.props.version && nextProps && nextProps.version && this.props.version.id !== nextProps.version.id && !nextProps.version.validationResult) { this.props.fetchValidationResult(nextProps.version) } } - render () { const version = this.props.version - const messages = getComponentMessages('GtfsValidationExplorer') if (!version || !this.props.version.validationResult) { - return ( - - - - - - - - - ) + return null } const tabRowStyle = { marginTop: '20px' } return ( - - - { - this.setState({activeTab: key}) - setTimeout(() => { - const map = this.refs[key + '-map'] - if (map) map.initializeMap() - }, 100); // Adjust timeout to tab transition - }} - > - - - - - - - { this.props.fetchValidationResult(version) }} - /> - - - + + + { + this.setState({activeTab: key}) + setTimeout(() => { + const map = this.refs[key + '-map'] + if (map) map.initializeMap() + }, 100) // Adjust timeout to tab transition + }} + > + + + + + + + { this.props.fetchValidationResult(version) }} + /> + + + - - - - - - - + + + + + + + - - - - - - - - - - + + + + + + + + + + ) } } diff --git a/src/main/client/manager/components/validation/GtfsValidationMap.js b/src/main/client/manager/components/validation/GtfsValidationMap.js deleted file mode 100644 index ac02ba07c..000000000 --- a/src/main/client/manager/components/validation/GtfsValidationMap.js +++ /dev/null @@ -1,274 +0,0 @@ -import React from 'react' -import { Grid, Row, Col, Button, Table, Input, Panel, Glyphicon } from 'react-bootstrap' -import { Link, browserHistory } from 'react-router' -import { Map, Marker, Popup, TileLayer, Rectangle, GeoJson, FeatureGroup } from 'react-leaflet' - -import ManagerPage from '../../../common/components/ManagerPage' -import GtfsValidationSummary from './GtfsValidationSummary' - -export default class GtfsValidationMap extends React.Component { - - constructor (props) { - super(props) - this.state = { - mapHeight: '500px' - } - } - - componentWillMount () { - this.props.onComponentMount(this.props) - } - componentDidMount () { - window.addEventListener('resize', this.handleResize) - } - handleResize () { - console.log(window.innerHeight) - } - render () { - console.log(this.props.version) - - if (!this.props.version || !this.props.version.validationResult) { - return ( - - - - - - - - - ) - } - const bs = ( - - - - - - - - - - - - - ) - const nonbs = ( - - - - - - - - ) - return ( - - - - - - - - - - - ) - } -} - -class ValidationMap extends React.Component { - constructor (props) { - super(props) - } - render () { - const version = this.props.version - const validation = version.validationResult - let errors = {} - validation && validation.errors.map(error => { - if (!errors[error.file]) { - errors[error.file] = [] - } - errors[error.file].push(error) - }) - const summary = version.validationSummary - console.log(validation) - const bounds = [[summary.bounds.north, summary.bounds.east], [summary.bounds.south, summary.bounds.west]] - - const getIsochrones = (e) => { - console.log(e) - const center = this.refs.validationMap.leafletElement.getCenter() - console.log(center) - this.props.fetchIsochrones(this.props.version, e.latlng.lat, e.latlng.lng, center.lat, center.lng) - } - const getIsochroneColor = (time) => { - return time ? 'blue' : 'red' - } - // console.log(errors.stop) - console.log(errors.route) - const stopIssues = errors.stop ? errors.stop.map(stop => { - // if (!stop.problemData) return null - // - // let s1 = stop.problemData.stop1 ? stop.problemData.stop1 : null - let s2 = null // stop.problemData.stop2 ? stop.problemData.stop2 : null - let s1 = stop.stop - return ( - - {s1 ? - - -
    -

    {s1.stop_name}

    -

    {stop.errorType}

    -
    -
    -
    - : null} - {s2 ? - - -
    -

    {s2.stop_name}

    -

    {stop.errorType}

    -
    -
    -
    - : null} -
    - ) - }) : null - const mapStyle = { - // position: 'absolute', - // top: '50px', - // bottom: 0, - height: '620px', - // height: '88%', - } - return ( - console.log(e)} - scrollWheelZoom - > - -
    - -
    - - {stopIssues} - { - version.isochrones ? version.isochrones.features.map(iso => { - if (iso.properties.time !== 60*60) return null - return ( - { - console.log(feature) - return { - color: getIsochroneColor(iso.properties.time), - } - }} - onEachFeature={(feature, layer) => { - // feature.properties.time = iso.properties.time - }} - > - - - ) - }) - : null - } -
    - ) - } -} - -class ValidationPanel extends React.Component { - constructor (props) { - super(props) - this.state = { - isVisible: true - } - } - render () { - const version = this.props.version - - const validation = this.props.version.validationResult - console.log(validation) - const dockStyle = { - marginLeft: '20px', - marginRight: '20px', - } - const panelStyle = { - position: 'absolute', - right: 0, - // height: '88%', - // width: '33%', - backgroundColor: 'white', - } - return ( -
    -

    {version.feedSource.name} Validation Results

    - - { this.props.fetchValidationResult(version) }} - /> -
    - ) - } -} diff --git a/src/main/client/manager/components/validation/GtfsValidationSummary.js b/src/main/client/manager/components/validation/GtfsValidationSummary.js index af56eb4a3..e1a4fcff6 100644 --- a/src/main/client/manager/components/validation/GtfsValidationSummary.js +++ b/src/main/client/manager/components/validation/GtfsValidationSummary.js @@ -1,7 +1,6 @@ import React, { Component, PropTypes } from 'react' -import { Table, Glyphicon, Button } from 'react-bootstrap' +import { Table, Button } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' -// import { connect } from 'react-redux' import { LinkContainer } from 'react-router-bootstrap' import Loading from '../../../common/components/Loading' @@ -29,7 +28,7 @@ export default class GtfsValidationSummary extends Component { render () { const result = this.props.version.validationResult if (!result) { - return + return } let errors = {} result && result.errors.map(error => { @@ -114,7 +113,7 @@ class ResultTable extends React.Component { file: val.file, affected: [val.affectedEntityId], description: val.problemDescription, - data: [val.problemData], + data: [val.problemData] } } problemMap[val.errorType].count++ diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index a49707f30..42cb8ceb5 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -1,9 +1,8 @@ import React from 'react' -import { Panel, Table, Glyphicon, Button, Badge } from 'react-bootstrap' -import { browserHistory } from 'react-router' +import { Panel, Table, Badge } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' -import { isModuleEnabled, isExtensionEnabled, getComponentMessages, getMessage } from '../../../common/util/config' +import { getComponentMessages, getMessage } from '../../../common/util/config' export default class GtfsValidationViewer extends React.Component { @@ -17,21 +16,10 @@ export default class GtfsValidationViewer extends React.Component { componentWillReceiveProps (nextProps) { if (!nextProps.validationResult) this.setState({ expanded: false }) } - render () { - const result = this.props.validationResult const messages = getComponentMessages('GtfsValidationViewer') - const header = ( -

    { - if (!result) this.props.fetchValidationResult() - this.setState({ expanded: !this.state.expanded }) - }}> - {getMessage(messages, 'title')} -

    - ) - let report = null let files = ['routes', 'stops', 'trips', 'shapes', 'stop_times'] let errors = {} diff --git a/src/main/client/manager/components/validation/IsochroneMap.js b/src/main/client/manager/components/validation/IsochroneMap.js deleted file mode 100644 index 290603c80..000000000 --- a/src/main/client/manager/components/validation/IsochroneMap.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react' -import { Map, Marker, Popup, TileLayer, Rectangle, GeoJson, FeatureGroup } from 'react-leaflet' - -import ValidationMap from './ValidationMap' - -export default class IsochroneMap extends ValidationMap { - - constructor (props) { - super(props) - } - - mapClicked (e) { - this.fetchIsochrones(e.latlng) - } - - fetchIsochrones (latlng) { - const center = super.getMap().leafletElement.getCenter() - this.props.fetchIsochrones(this.props.version, latlng.lat, latlng.lng, center.lat, center.lng) - this.setState({ lastClicked: latlng }) - } - - getMapComponents () { - let comps = [] - - if (this.props.version && this.props.version.isochrones) { - comps = this.props.version.isochrones.features.map((iso, index) => { - if (iso.properties.time !== 60*60) return null - return ( - { - return { - color: this.getIsochroneColor(iso.properties.time), - } - }} - /> - ) - }) - } - - if (this.state && this.state.lastClicked) { - comps.push( - { - this.fetchIsochrones(e.target.getLatLng()) - }} - /> - ) - } - - return comps - } - - getIsochroneColor (time) { - return time ? 'blue' : 'red' - } -} diff --git a/src/main/client/manager/components/validation/IssuesMap.js b/src/main/client/manager/components/validation/IssuesMap.js deleted file mode 100644 index 5375b9367..000000000 --- a/src/main/client/manager/components/validation/IssuesMap.js +++ /dev/null @@ -1,56 +0,0 @@ -import React from 'react' -import { Map, Marker, Popup, TileLayer, Rectangle, GeoJson, FeatureGroup } from 'react-leaflet' - -import ValidationMap from './ValidationMap' - -export default class IssuesMap extends ValidationMap { - - constructor (props) { - super(props) - } - - getMapComponents () { - const validation = this.props.version.validationResult - - let errors = {} - - validation && validation.errors.map(error => { - if (!errors[error.file]) { - errors[error.file] = [] - } - errors[error.file].push(error) - }) - - return errors.stop ? errors.stop.map(stop => { - let s2 = null // stop.problemData.stop2 ? stop.problemData.stop2 : null - let s1 = stop.stop - return ( - - {s1 ? - - -
    -

    {s1.stop_name}

    -

    {stop.errorType}

    -
    -
    -
    - : null} - {s2 ? - - -
    -

    {s2.stop_name}

    -

    {stop.errorType}

    -
    -
    -
    - : null} -
    - ) - }) : null - } -} diff --git a/src/main/client/manager/components/validation/TripsChart.js b/src/main/client/manager/components/validation/TripsChart.js index ab0334942..4241600dc 100644 --- a/src/main/client/manager/components/validation/TripsChart.js +++ b/src/main/client/manager/components/validation/TripsChart.js @@ -16,7 +16,7 @@ export default class TripsChart extends Component { } render () { if (!this.props.validationResult) { - return + return } const tripsPerDate = this.props.validationResult.tripsPerDate const data = Object.keys(tripsPerDate).map(key => [key, tripsPerDate[key]]) @@ -77,11 +77,11 @@ export default class TripsChart extends Component { /> {index % 14 === 0 /* label the date every 14 days */ ? - - - {d[0]} - - + + + {d[0]} + + : null } diff --git a/src/main/client/manager/components/validation/ValidationMap.js b/src/main/client/manager/components/validation/ValidationMap.js deleted file mode 100644 index 7cb961114..000000000 --- a/src/main/client/manager/components/validation/ValidationMap.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react' -import { Map, Marker, Popup, TileLayer, Rectangle, GeoJson, FeatureGroup } from 'react-leaflet' - -export default class ValidationMap extends React.Component { - - constructor (props) { - super(props) - } - - getMap() { - return this.refs.map - } - - initializeMap() { - if (this.mapInitialized || this.props.initialized) return - const leafletMap = this.getMap().leafletElement - leafletMap.invalidateSize() - const summary = this.props.version.validationSummary - const bounds = [[summary.bounds.north, summary.bounds.east], [summary.bounds.south, summary.bounds.west]] - leafletMap.fitBounds(bounds) - this.mapInitialized = true - } - - mapClicked (evt) { } - - getMapComponents () { - return null - } - - render () { - const version = this.props.version - const summary = version.validationSummary - const bounds = [[summary.bounds.north, summary.bounds.east], [summary.bounds.south, summary.bounds.west]] - - const mapStyle = { - height: '620px', - } - - return ( - this.mapClicked(e)} - scrollWheelZoom - > - - - - - {this.getMapComponents()} - - - ) - } -} diff --git a/src/main/client/manager/containers/ActiveDeploymentViewer.js b/src/main/client/manager/containers/ActiveDeploymentViewer.js index 30385ff26..4642ccb47 100644 --- a/src/main/client/manager/containers/ActiveDeploymentViewer.js +++ b/src/main/client/manager/containers/ActiveDeploymentViewer.js @@ -1,10 +1,7 @@ -import React from 'react' import { connect } from 'react-redux' import DeploymentViewer from '../components/DeploymentViewer' -import { fetchDeployment, - fetchDeploymentAndProject, - fetchDeploymentTargets, +import { fetchDeploymentAndProject, deployToTarget, updateDeployment, fetchDeploymentStatus, @@ -14,23 +11,8 @@ import { fetchDeployment, import { fetchProjectFeeds } from '../actions/feeds' const mapStateToProps = (state, ownProps) => { - // let deploymentId = ownProps.routeParams.deploymentId let user = state.user - // let project = state.projects.all - // ? state.projects.all.find(p => { - // if (!p.deployments) return false - // return (p.deployments.findIndex(dep => dep.id === deploymentId) !== -1) - // }) - // : null - - // let deployment - // if (project) { - // deployment = project.deployments.find(dep => dep.id === deploymentId) - // } - return { - // deployment, - // project, user } } @@ -50,7 +32,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) } }, - onDeploymentTargetsClick: () => { dispatch(fetchDeploymentTargets()) }, deployToTargetClicked: (deployment, target) => { dispatch(deployToTarget(deployment, target)) }, downloadDeployment: (deployment) => { dispatch(downloadDeployment(deployment)) }, // updateProjectSettings: (project, newSettings) => { dispatch(updateProject(project, newSettings)) }, // dispatch(updateProject(project, { [propName] : newValue })) diff --git a/src/main/client/manager/containers/ActiveFeedSourceViewer.js b/src/main/client/manager/containers/ActiveFeedSourceViewer.js index 615a7e80f..9aec279d1 100644 --- a/src/main/client/manager/containers/ActiveFeedSourceViewer.js +++ b/src/main/client/manager/containers/ActiveFeedSourceViewer.js @@ -1,29 +1,29 @@ import { connect } from 'react-redux' -import { browserHistory } from 'react-router' import FeedSourceViewer from '../components/FeedSourceViewer' - import { createFeedInfo } from '../../editor/actions/feedInfo' - import { - deleteFeedVersion, - downloadFeedViaToken, fetchFeedSourceAndProject, fetchFeedSource, deleteFeedSource, - fetchFeedVersions, - fetchNotesForFeedSource, - fetchNotesForFeedVersion, - fetchValidationResult, - postNoteForFeedSource, - postNoteForFeedVersion, - renameFeedVersion, runFetchFeed, updateExternalFeedResource, uploadFeed, updateFeedSource } from '../actions/feeds' - +import { + deleteFeedVersion, + downloadFeedViaToken, + fetchFeedVersions, + fetchValidationResult, + renameFeedVersion +} from '../actions/versions' +import { + fetchNotesForFeedSource, + fetchNotesForFeedVersion, + postNoteForFeedSource, + postNoteForFeedVersion +} from '../actions/notes' import { updateTargetForSubscription } from '../../manager/actions/user' import { createDeploymentFromFeedSource } from '../../manager/actions/deployments' import { loadFeedVersionForEditing } from '../../editor/actions/snapshots' @@ -36,9 +36,9 @@ const mapStateToProps = (state, ownProps) => { let project = state.projects.all ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) + if (!p.feedSources) return false + return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) + }) : null let feedSource diff --git a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js index ec0a61fc3..cdb4c8d3a 100644 --- a/src/main/client/manager/containers/ActiveFeedVersionNavigator.js +++ b/src/main/client/manager/containers/ActiveFeedVersionNavigator.js @@ -2,18 +2,15 @@ import { connect } from 'react-redux' import { browserHistory } from 'react-router' import FeedVersionNavigator from '../components/FeedVersionNavigator' - import { deleteFeedVersion, downloadFeedViaToken, - fetchNotesForFeedVersion, fetchValidationResult, - postNoteForFeedVersion, renameFeedVersion, setActiveVersion, - publishFeedVersion, - fetchFeedSource -} from '../actions/feeds' + publishFeedVersion +} from '../actions/versions' +import { postNoteForFeedVersion, fetchNotesForFeedVersion } from '../actions/notes' import { createDeploymentFromFeedSource } from '../../manager/actions/deployments' import { loadFeedVersionForEditing } from '../../editor/actions/snapshots' import { downloadGtfsPlusFeed } from '../../gtfsplus/actions/gtfsplus' @@ -90,7 +87,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { // .then(() => { // dispatch(fetchFeedSource(feedVersion.feedSource.id, true, true)) // }) - }, + } } } diff --git a/src/main/client/manager/containers/ActiveProjectViewer.js b/src/main/client/manager/containers/ActiveProjectViewer.js index 6c4d5c768..69d76588f 100644 --- a/src/main/client/manager/containers/ActiveProjectViewer.js +++ b/src/main/client/manager/containers/ActiveProjectViewer.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import { browserHistory } from 'react-router' import ProjectViewer from '../components/ProjectViewer' @@ -57,8 +56,9 @@ const mapDispatchToProps = (dispatch, ownProps) => { .then(project => { dispatch(fetchProjectFeeds(projectId)) }) + } else if (!initialProps.project.feedSources || initialProps.project.feedSources.length !== initialProps.project.numberOfFeeds) { + dispatch(fetchProjectFeeds(projectId)) } - else if (!initialProps.project.feedSources || initialProps.project.feedSources.length !== initialProps.project.numberOfFeeds) dispatch(fetchProjectFeeds(projectId)) }, onNewFeedSourceClick: () => { dispatch(createFeedSource(projectId)) }, updateProjectSettings: (project, newSettings) => { diff --git a/src/main/client/manager/containers/ActiveProjectsList.js b/src/main/client/manager/containers/ActiveProjectsList.js index 45b6d6aed..e3b13c65b 100644 --- a/src/main/client/manager/containers/ActiveProjectsList.js +++ b/src/main/client/manager/containers/ActiveProjectsList.js @@ -1,9 +1,8 @@ -import React from 'react' import { connect } from 'react-redux' import { fetchProjects, updateProject, createProject, saveProject } from '../actions/projects' import { setVisibilitySearchText } from '../actions/visibilityFilter' -import ProjectsList from '../components/ProjectsList' +import ProjectsList from '../components/ProjectsList' const mapStateToProps = (state, ownProps) => { return { @@ -24,9 +23,9 @@ const mapDispatchToProps = (dispatch, ownProps) => { dispatch(saveProject({ name })) }, projectNameChanged: (project, newName) => { - dispatch(updateProject(project, { name : newName })) + dispatch(updateProject(project, { name: newName })) }, - searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text))} + searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text)) } } } diff --git a/src/main/client/manager/containers/validation/ActiveGtfsValidationExplorer.js b/src/main/client/manager/containers/validation/ActiveGtfsValidationExplorer.js index 5417b12bc..ccbfbd4af 100644 --- a/src/main/client/manager/containers/validation/ActiveGtfsValidationExplorer.js +++ b/src/main/client/manager/containers/validation/ActiveGtfsValidationExplorer.js @@ -1,16 +1,12 @@ -import React from 'react' import { connect } from 'react-redux' import GtfsValidationExplorer from '../../components/validation/GtfsValidationExplorer' - +import { fetchFeedSourceAndProject } from '../../actions/feeds' import { - fetchFeedSourceAndProject, fetchFeedVersions, fetchValidationResult, fetchFeedVersionIsochrones -} from '../../actions/feeds' - -import { updateTargetForSubscription } from '../../actions/user' +} from '../../actions/versions' const mapStateToProps = (state, ownProps) => { let feedSourceId = ownProps.routeParams.feedSourceId @@ -20,9 +16,9 @@ const mapStateToProps = (state, ownProps) => { // find the containing project let project = state.projects.all ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) + if (!p.feedSources) return false + return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) + }) : null let feedSource, version @@ -30,7 +26,7 @@ const mapStateToProps = (state, ownProps) => { feedSource = project.feedSources.find(fs => fs.id === feedSourceId) } if (feedSource && feedSource.feedVersions) { - version = feedSource.feedVersions[feedVersionIndex-1] + version = feedSource.feedVersions[feedVersionIndex - 1] } return { version, @@ -66,7 +62,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, fetchIsochrones: (feedVersion, fromLat, fromLon, toLat, toLon) => { dispatch(fetchFeedVersionIsochrones(feedVersion, fromLat, fromLon, toLat, toLon)) - }, + } } } diff --git a/src/main/client/manager/containers/validation/ActiveGtfsValidationMap.js b/src/main/client/manager/containers/validation/ActiveGtfsValidationMap.js deleted file mode 100644 index ba8303da6..000000000 --- a/src/main/client/manager/containers/validation/ActiveGtfsValidationMap.js +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react' -import { connect } from 'react-redux' - -import GtfsValidationMap from '../../components/validation/GtfsValidationMap' - -import { - fetchFeedSource, - fetchFeedVersionIsochrones, - fetchFeedSourceAndProject, - updateFeedSource, - runFetchFeed, - fetchFeedVersions, - uploadFeed, - fetchPublicFeedSource, - receiveFeedVersions, - fetchPublicFeedVersions, - updateExternalFeedResource, - deleteFeedVersion, - fetchValidationResult, - downloadFeedViaToken, - fetchNotesForFeedSource, - postNoteForFeedSource, - fetchNotesForFeedVersion, - postNoteForFeedVersion -} from '../../actions/feeds' - -import { updateTargetForSubscription } from '../../actions/user' - -import { downloadGtfsPlusFeed } from '../../../gtfsplus/actions/gtfsplus' - -const mapStateToProps = (state, ownProps) => { - let feedSourceId = ownProps.routeParams.feedSourceId - let feedVersionId = ownProps.routeParams.feedVersionId - let user = state.user - // find the containing project - let project = state.projects.all - ? state.projects.all.find(p => { - if (!p.feedSources) return false - return (p.feedSources.findIndex(fs => fs.id === feedSourceId) !== -1) - }) - : null - - let feedSource, version - if (project) { - feedSource = project.feedSources.find(fs => fs.id === feedSourceId) - } - if (feedSource && feedSource.feedVersions) { - version = feedSource.feedVersions.find(v => v.id === feedVersionId) - } - return { - version, - project, - user - } -} - -const mapDispatchToProps = (dispatch, ownProps) => { - const feedSourceId = ownProps.routeParams.feedSourceId - const feedVersionId = ownProps.routeParams.feedVersionId - return { - onComponentMount: (initialProps) => { - let unsecured = true - if (initialProps.user.profile !== null) { - unsecured = false - } - if (!initialProps.project) { - dispatch(fetchFeedSourceAndProject(feedSourceId, unsecured)) - .then((feedSource) => { - console.log(feedSource) - return dispatch(fetchFeedVersions(feedSource, unsecured)) - }) - .then((feedVersions) => { - console.log(feedVersions) - let version = feedVersions.find(v => v.id === feedVersionId) - dispatch(fetchValidationResult(version)) - }) - } - else if (!initialProps.feedSource) { - dispatch(fetchFeedSource(feedSourceId)) - .then((feedSource) => { - console.log(feedSource) - return dispatch(fetchFeedVersions(feedSource, unsecured)) - }) - .then((feedVersions) => { - console.log(feedVersions) - let version = feedVersions.find(v => v.id === feedVersionId) - dispatch(fetchValidationResult(version)) - }) - } - else if (!initialProps.feedSource.versions) { - dispatch(fetchFeedVersions(initialProps.feedSource, unsecured)) - .then((feedVersions) => { - console.log(feedVersions) - let version = feedVersions.find(v => v.id === feedVersionId) - dispatch(fetchValidationResult(version)) - }) - } - else if (!initialProps.feedSource.versions.validationResult) { - // dispatch(fetchValidationResult(version)) - } - }, - fetchIsochrones: (feedVersion, fromLat, fromLon, toLat, toLon) => { - dispatch(fetchFeedVersionIsochrones(feedVersion, fromLat, fromLon, toLat, toLon)) - }, - } -} - -const ActiveGtfsValidationMap = connect( - mapStateToProps, - mapDispatchToProps -)(GtfsValidationMap) - -export default ActiveGtfsValidationMap diff --git a/src/main/client/manager/reducers/languages.js b/src/main/client/manager/reducers/languages.js index 7cc06d66c..a710085a0 100644 --- a/src/main/client/manager/reducers/languages.js +++ b/src/main/client/manager/reducers/languages.js @@ -4,15 +4,15 @@ import { getConfigProperty } from '../../common/util/config' const languages = (state = { all: getConfigProperty('messages.all'), // set active default to english - active: getConfigProperty('messages.active'), + active: getConfigProperty('messages.active') }, action) => { let languageIndex switch (action.type) { case 'SET_ACTIVE_LANGUAGE': languageIndex = state.all.findIndex(l => l.id === action.languageId) - DT_CONFIG.messages.active = state.all[languageIndex] - localStorage.setItem('lang', action.languageId) - return update(state, { active: { $set: state.all[languageIndex] }}) + window.DT_CONFIG.messages.active = state.all[languageIndex] + window.localStorage.setItem('lang', action.languageId) + return update(state, {active: { $set: state.all[languageIndex] }}) default: return state } diff --git a/src/main/client/manager/reducers/ui.js b/src/main/client/manager/reducers/ui.js index 9360e2d70..e9d33fd34 100644 --- a/src/main/client/manager/reducers/ui.js +++ b/src/main/client/manager/reducers/ui.js @@ -11,7 +11,7 @@ const ui = (state = { const sidebarExpanded = getUserMetadataProperty(action.profile, 'sidebarExpanded') return update(state, { sidebarExpanded: { $set: sidebarExpanded }, - hideTutorial: { $set: hideTutorial }, + hideTutorial: { $set: hideTutorial } }) case 'SETTING_TUTORIAL_VISIBILITY': return update(state, { hideTutorial: { $set: action.value } }) diff --git a/src/main/client/public/components/MarkerCluster.js b/src/main/client/public/components/MarkerCluster.js index da4a23d01..89079d777 100644 --- a/src/main/client/public/components/MarkerCluster.js +++ b/src/main/client/public/components/MarkerCluster.js @@ -1,5 +1,3 @@ - - import React from 'react' import Leaflet from 'leaflet' // import MarkerPopup from './MarkerPopup' diff --git a/src/main/client/public/components/MarkerCluster2.js b/src/main/client/public/components/MarkerCluster2.js deleted file mode 100644 index 58fa24113..000000000 --- a/src/main/client/public/components/MarkerCluster2.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react' -import { PropTypes } from 'react' -import { BaseTileLayer, MapLayer, Marker, Path } from 'react-leaflet' - -import Leaflet from 'leaflet' -require('leaflet.markercluster') - -export class MarkerCluster extends Path { - componentWillMount () { - super.componentWillMount() - console.log('mounting') - const { markers, map, ...props } = this.props - this.leafletElement = Leaflet.markerClusterGroup() - const newMarkers = markers.map( m => { - return ( - - ) - }) - console.log(newMarkers) - this.leafletElement.addLayers(newMarkers) - console.log('added markers') - } - - // componentDidUpdate () { - // const { markers, map, ...props } = this.props - // map.removeLayer(this.leafletElement) - // this.leafletElement = Leaflet.markerClusterGroup() - // this.leafletElement.addLayers(markers.map( m => { - // return ( - // - // ) - // })) - // } - - render () { - return null - } -} - -MarkerCluster.propTypes = { - markers: PropTypes.array.isRequired -} diff --git a/src/main/client/public/components/PublicFeedSourceViewer.js b/src/main/client/public/components/PublicFeedSourceViewer.js index ae26bffe5..74b1abf97 100644 --- a/src/main/client/public/components/PublicFeedSourceViewer.js +++ b/src/main/client/public/components/PublicFeedSourceViewer.js @@ -14,22 +14,14 @@ export default class PublicFeedSourceViewer extends Component { onComponentMount: PropTypes.func } - - constructor (props) { - super(props) - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { const fs = this.props.feedSource - if (!fs) { return } - return ( @@ -45,14 +37,15 @@ export default class PublicFeedSourceViewer extends Component {

    - {fs.name} Public view - {this.props.user.profile - ? (View private page) - : null - } - + {fs.name}{' '} + + Public view + {this.props.user.profile + ? (View private page) + : null + } +

    -
    @@ -82,10 +75,10 @@ export default class PublicFeedSourceViewer extends Component { Feed Versions)}> {fs.feedVersions && fs.feedVersions.length > 0 ? + feedSource={fs} + routeParams={this.props.routeParams} + isPublic + /> : No Feed Versions to show. } diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index 0b1c14899..3e62a0f2d 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -1,6 +1,6 @@ import React, {Component, PropTypes} from 'react' import {Icon} from '@conveyal/woonerf' -import { Grid, Row, Col, Button, Glyphicon, ButtonToolbar, DropdownButton, MenuItem } from 'react-bootstrap' +import { Grid, Row, Col, Button, Glyphicon, ButtonToolbar } from 'react-bootstrap' import { browserHistory } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' @@ -18,13 +18,6 @@ export default class PublicHeader extends Component { } render () { - const userDropdown = {this.props.username}} - > - browserHistory.push('/settings/profile')}>My Account - this.props.resetPassword()}>Change Password - this.props.logoutHandler()}>Log Out - return ( @@ -33,7 +26,7 @@ export default class PublicHeader extends Component {

    {this.props.title}

    - {/* Button Column*/} + {/* Button Column */} {/* TODO: Add Language Selector */} @@ -41,21 +34,21 @@ export default class PublicHeader extends Component { {/* "Log In" Button or User Dropdown */} {this.props.username ? - - - : + + : } {/* "Create Account" Button (only show if no active user) */} {this.props.username || getConfigProperty('modules.enterprise.enabled') ? null : + Create Account + } {/* "Log out" Button */} diff --git a/src/main/client/public/components/PublicPage.js b/src/main/client/public/components/PublicPage.js index 1675d0324..e675a1462 100644 --- a/src/main/client/public/components/PublicPage.js +++ b/src/main/client/public/components/PublicPage.js @@ -8,19 +8,12 @@ import ActivePublicHeader from '../containers/ActivePublicHeader' import { getConfigProperty } from '../../common/util/config' export default class PublicPage extends React.Component { - - constructor (props) { - super(props) - } - showConfirmModal (props) { this.refs.confirmModal.open(props) } - showSelectFileModal (props) { this.refs.selectFileModal.open(props) } - render () { return (
    diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index 4277b2fa8..b2c454a93 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -14,14 +14,9 @@ export default class UserAccount extends Component { resetPassword: PropTypes.func } - constructor (props) { - super(props) - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { var removeIconStyle = { cursor: 'pointer' @@ -32,33 +27,35 @@ export default class UserAccount extends Component { const accountSections = [ { id: 'profile', - component:
    - Profile information}> - - - Email address - { - this.props.updateUserName(this.props.user, value) - }} - /> - {/* */} - - -

    Avatar

    - - - Change on gravatar.com - -
    - -

    Password

    - -
    -
    -
    -
    + component: ( +
    + Profile information}> + + + Email address + { + this.props.updateUserName(this.props.user, value) + }} + /> + {/* */} + + +

    Avatar

    + + + Change on gravatar.com + +
    + +

    Password

    + +
    +
    +
    +
    + ) }, { id: 'account', @@ -72,77 +69,79 @@ export default class UserAccount extends Component { id: 'notifications', hidden: !getConfigProperty('application.notifications_enabled'), component: -
    - Notification methods} - > - - -

    Watching

    -

    Receive updates to any feed sources or comments you are watching.

    - Email{' '}Web -
    -
    -
    - - - {getMessage(messages, 'notifications.subscriptions')} - - } - > -
      - {subscriptions.length ? subscriptions.map(sub => { - return ( -
    • - {sub.type.replace('-', ' ')}   +
      + Notification methods} + > + + +

      Watching

      +

      Receive updates to any feed sources or comments you are watching.

      + Email{' '}Web +
      +
      +
      + + + {getMessage(messages, 'notifications.subscriptions')} + + } + > +
        + {subscriptions.length ? subscriptions.map(sub => { + return ( +
      • + {sub.type.replace('-', ' ')}{' '} { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} /> -
          - {sub.target.length ? sub.target.map(target => { - let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null - if (this.props.projects) { - for (var i = 0; i < this.props.projects.length; i++) { - let feed = this.props.projects[i].feedSources ? this.props.projects[i].feedSources.find(fs => fs.id === target) : null - fs = feed ? feed : fs +
            + {sub.target.length ? sub.target.map(target => { + let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null + if (this.props.projects) { + for (var i = 0; i < this.props.projects.length; i++) { + let feed = this.props.projects[i].feedSources + ? this.props.projects[i].feedSources.find(fs => fs.id === target) + : null + fs = feed || fs + } } - } - return ( -
          • - { - fs ? {fs.name} - : {target} - } {' '} - { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} - /> -
          • - ) - }) :
          • No feeds subscribed to.
          • - } -
          - - ) - }) - :
        • No subscriptions.
        • - } -
        - -
      + return ( +
    • + { + fs ? {fs.name} + : {target} + } {' '} + { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} + /> +
    • + ) + }) :
    • No feeds subscribed to.
    • + } +
    + + ) + }) + :
  • No subscriptions.
  • + } + +
    +
    }, { id: 'billing', @@ -197,7 +196,7 @@ export default class UserAccount extends Component { - + {visibleComponent} diff --git a/src/main/client/public/containers/ActivePublicFeedSourceViewer.js b/src/main/client/public/containers/ActivePublicFeedSourceViewer.js index 28fe04c45..dfcb854c2 100644 --- a/src/main/client/public/containers/ActivePublicFeedSourceViewer.js +++ b/src/main/client/public/containers/ActivePublicFeedSourceViewer.js @@ -1,15 +1,12 @@ -import React from 'react' import { connect } from 'react-redux' import PublicFeedSourceViewer from '../components/PublicFeedSourceViewer' - import { fetchFeedSourceAndProject, updateFeedSource, - runFetchFeed, - fetchFeedVersions, - uploadFeed + runFetchFeed } from '../../manager/actions/feeds' +import { fetchFeedVersions, uploadFeed } from '../../manager/actions/versions' const mapStateToProps = (state, ownProps) => { let feedSourceId = ownProps.routeParams.feedSourceId diff --git a/src/main/client/public/containers/ActivePublicFeedsViewer.js b/src/main/client/public/containers/ActivePublicFeedsViewer.js index 32a163d4e..e57626872 100644 --- a/src/main/client/public/containers/ActivePublicFeedsViewer.js +++ b/src/main/client/public/containers/ActivePublicFeedsViewer.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import PublicFeedsViewer from '../components/PublicFeedsViewer' @@ -18,8 +17,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { onComponentMount: (initialProps) => { dispatch(fetchProjectsWithPublicFeeds()) }, - searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text)) }, - // onFeedClick: (feed) => {dispatch()} + searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text)) } } } diff --git a/src/main/client/public/containers/ActiveSignupPage.js b/src/main/client/public/containers/ActiveSignupPage.js index 881672561..091891de5 100644 --- a/src/main/client/public/containers/ActiveSignupPage.js +++ b/src/main/client/public/containers/ActiveSignupPage.js @@ -1,11 +1,9 @@ -import React from 'react' import { connect } from 'react-redux' import { browserHistory } from 'react-router' import SignupPage from '../components/SignupPage' -import { setVisibilitySearchText } from '../../manager/actions/visibilityFilter' import { fetchProjectsWithPublicFeeds } from '../../manager/actions/projects' -import { login, createPublicUser, fetchUser, userLoggedIn, checkExistingLogin } from '../../manager/actions/user' +import { login, createPublicUser } from '../../manager/actions/user' const mapStateToProps = (state, ownProps) => { return { diff --git a/src/main/client/public/containers/ActiveUserAccount.js b/src/main/client/public/containers/ActiveUserAccount.js index 5af69138e..5a91798cf 100644 --- a/src/main/client/public/containers/ActiveUserAccount.js +++ b/src/main/client/public/containers/ActiveUserAccount.js @@ -1,11 +1,10 @@ -import React from 'react' import { connect } from 'react-redux' import { browserHistory } from 'react-router' import UserAccount from '../components/UserAccount' import { setVisibilitySearchText } from '../../manager/actions/visibilityFilter' -import { fetchProjectsWithPublicFeeds, fetchProjects } from '../../manager/actions/projects' -import { fetchFeedSource, receiveFeedSource } from '../../manager/actions/feeds' +import { fetchProjects } from '../../manager/actions/projects' +import { fetchFeedSource } from '../../manager/actions/feeds' import { updateUserData, fetchUser, @@ -33,7 +32,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (!ownProps.routeParams.subpage) { browserHistory.push('/settings/profile') } - if (!initialProps.projects){ + if (!initialProps.projects) { dispatch(fetchProjects()) .then(() => { let subscriptions = initialProps.user.profile.app_metadata.datatools.find(dt => dt.client_id === getConfigProperty('auth0.client_id')).subscriptions @@ -51,8 +50,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) } }) - } - else { + } else { let subscriptions = initialProps.user.profile.app_metadata.datatools.find(dt => dt.client_id === getConfigProperty('auth0.client_id')).subscriptions if (subscriptions) { Promise.all( @@ -68,7 +66,6 @@ const mapDispatchToProps = (dispatch, ownProps) => { }) } } - }, searchTextChanged: (text) => { dispatch(setVisibilitySearchText(text)) }, updateUserName: (user, permissions) => { dispatch(updateUserData(user, permissions)) }, diff --git a/src/main/client/scenario-editor/utils/reverse.js b/src/main/client/scenario-editor/utils/reverse.js index 2791f9161..2985bec61 100644 --- a/src/main/client/scenario-editor/utils/reverse.js +++ b/src/main/client/scenario-editor/utils/reverse.js @@ -5,12 +5,11 @@ import { getConfigProperty } from '../../common/util/config' export async function reversePelias (point) { const location = {lon: point.lng, lat: point.lat} - const api_key = getConfigProperty('MAPZEN_TURN_BY_TURN_KEY') + const apiKey = getConfigProperty('MAPZEN_TURN_BY_TURN_KEY') const params = { - api_key, + api_key: apiKey, ...location } - // api_key=mapzen-xxxxxx&point.lat=48.858268&point.lon=2.294471 const url = `https://search.mapzen.com/v1/reverse?${qs.stringify(params)}` const response = await fetch(url) return await response.json() diff --git a/src/main/client/signs/actions/activeSign.js b/src/main/client/signs/actions/activeSign.js index d7f8557f7..a11735067 100644 --- a/src/main/client/signs/actions/activeSign.js +++ b/src/main/client/signs/actions/activeSign.js @@ -83,7 +83,7 @@ export const addActiveEntity = (field = 'AGENCY', value = null, agency = null, n } } // set agency of new entity - if (agency){ + if (agency) { newEntity.entity.agency = agency } newEntity.entity[field.toLowerCase()] = value diff --git a/src/main/client/signs/actions/projects.js b/src/main/client/signs/actions/projects.js index 8cf9f99c7..428d1f271 100644 --- a/src/main/client/signs/actions/projects.js +++ b/src/main/client/signs/actions/projects.js @@ -7,7 +7,7 @@ import { getConfigProperty } from '../../common/util/config' function requestProjects () { return { - type: 'REQUEST_PROJECTS', + type: 'REQUEST_PROJECTS' } } diff --git a/src/main/client/signs/components/CreateSign.js b/src/main/client/signs/components/CreateSign.js index 40fe6f7e1..44486f7a2 100644 --- a/src/main/client/signs/components/CreateSign.js +++ b/src/main/client/signs/components/CreateSign.js @@ -2,9 +2,6 @@ import React from 'react' import { Button } from 'react-bootstrap' export default class CreateAlert extends React.Component { - constructor (props) { - super(props) - } render () { return (
    - ) - } -} diff --git a/src/main/client/signs/components/SignsList.js b/src/main/client/signs/components/SignsList.js index 6913d42b9..c47db3fb9 100644 --- a/src/main/client/signs/components/SignsList.js +++ b/src/main/client/signs/components/SignsList.js @@ -21,9 +21,6 @@ export default class SignsList extends Component { searchTextChanged: PropTypes.func, visibilityFilterChanged: PropTypes.func } - constructor (props) { - super(props) - } render () { let sortedSigns = this.props.signs.sort((a, b) => { if (a.id < b.id) return -1 @@ -59,23 +56,22 @@ export default class SignsList extends Component {
     
    - - {this.props.isFetching - ?

    - : sortedSigns.length - ? sortedSigns.map((sign) => { - return - }) - :

    No signs found.

    - } + {this.props.isFetching + ?

    + : sortedSigns.length + ? sortedSigns.map((sign) => { + return + }) + :

    No signs found.

    + }
    ) diff --git a/src/main/client/signs/components/SignsViewer.js b/src/main/client/signs/components/SignsViewer.js index 44cd9a97c..802b32c66 100644 --- a/src/main/client/signs/components/SignsViewer.js +++ b/src/main/client/signs/components/SignsViewer.js @@ -1,7 +1,6 @@ import React from 'react' import Helmet from 'react-helmet' - -import { Grid, Row, Col, Button } from 'react-bootstrap' +import { Grid, Row, Col } from 'react-bootstrap' import ManagerPage from '../../common/components/ManagerPage' import CreateSign from '../components/CreateSign' @@ -9,26 +8,17 @@ import VisibleSignsList from '../containers/VisibleSignsList' import GlobalGtfsFilter from '../../gtfs/containers/GlobalGtfsFilter' import GtfsMapSearch from '../../gtfs/components/gtfsmapsearch' -import { Link } from 'react-router' - export default class SignsViewer extends React.Component { - - constructor (props) { - super(props) - //console.log("AV activeFeeds", this.props.activeFeeds); - } - componentWillMount () { this.props.onComponentMount(this.props) } - render () { const createDisabled = this.props.project && this.props.user ? !this.props.user.permissions.hasProjectPermission(this.props.project.id, 'edit-etid') : true return ( - + diff --git a/src/main/client/signs/containers/ActiveSignEditor.js b/src/main/client/signs/containers/ActiveSignEditor.js index 6bb11c7ee..47e559067 100644 --- a/src/main/client/signs/containers/ActiveSignEditor.js +++ b/src/main/client/signs/containers/ActiveSignEditor.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import { fetchProjects } from '../actions/projects' diff --git a/src/main/client/signs/containers/MainSignsViewer.js b/src/main/client/signs/containers/MainSignsViewer.js index c306c54e8..0b552e2fd 100644 --- a/src/main/client/signs/containers/MainSignsViewer.js +++ b/src/main/client/signs/containers/MainSignsViewer.js @@ -1,4 +1,3 @@ -import React from 'react' import { connect } from 'react-redux' import SignsViewer from '../components/SignsViewer' diff --git a/src/main/client/signs/containers/VisibleSignsList.js b/src/main/client/signs/containers/VisibleSignsList.js index b62aa31a1..31b0b2d2f 100644 --- a/src/main/client/signs/containers/VisibleSignsList.js +++ b/src/main/client/signs/containers/VisibleSignsList.js @@ -1,12 +1,8 @@ -import React from 'react' import { connect } from 'react-redux' -import moment from 'moment' import { editSign, deleteSign } from '../actions/signs' import { setVisibilitySearchText, setVisibilityFilter } from '../actions/visibilityFilter' - import SignsList from '../components/SignsList' - import { getFeedsForPermission } from '../../common/util/permissions' import { FILTERS } from '../util' @@ -14,7 +10,6 @@ const getVisibleSigns = (signs, visibilityFilter) => { if (!signs) return [] let visibleSigns = signs.filter(sign => sign.title.toLowerCase().indexOf((visibilityFilter.searchText || '').toLowerCase()) !== -1) - let now = moment() switch (visibilityFilter.filter) { case 'ALL': return visibleSigns @@ -25,7 +20,6 @@ const getVisibleSigns = (signs, visibilityFilter) => { default: return visibleSigns } - return visibleSigns } const mapStateToProps = (state, ownProps) => { diff --git a/src/main/client/signs/reducers/activeSign.js b/src/main/client/signs/reducers/activeSign.js index 16f2c9fbd..b74191f18 100644 --- a/src/main/client/signs/reducers/activeSign.js +++ b/src/main/client/signs/reducers/activeSign.js @@ -26,12 +26,13 @@ const activeSign = (state = null, action) => { case 'SET_ACTIVE_SIGN_PUBLISHED': return update(state, {published: {$set: action.published}}) case 'RECEIVED_RTD_DISPLAYS': - if (state !== null){ + if (state !== null) { let displayMap = {} for (var i = 0; i < action.rtdDisplays.length; i++) { let d = action.rtdDisplays[i] - if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) + if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) { continue + } if (d.DraftDisplayConfigurationId) { if (displayMap[d.DraftDisplayConfigurationId] && displayMap[d.DraftDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { displayMap[d.DraftDisplayConfigurationId].push(d) @@ -55,13 +56,13 @@ const activeSign = (state = null, action) => { case 'RECEIVED_SIGN_GTFS_ENTITIES': // TODO: update GTFS entities for active sign if (state !== null && state.affectedEntities !== null) { - for (var i = 0; i < action.gtfsObjects.length; i++) { + for (let i = 0; i < action.gtfsObjects.length; i++) { let ent = action.gtfsObjects[i] if (typeof ent.gtfs !== 'undefined' && ent.SignId === state.id) { // let sign = action.gtfsSigns.find(a => a.id === ent.entity.SignId) let updatedEntity = state.affectedEntities.find(e => e.id === ent.entity.Id) updatedEntity[ent.type] = ent.gtfs - entities.push(selectedEnt) + // entities.push(selectedEnt) entities = [ ...state.affectedEntities.slice(0, foundIndex), updatedEntity, @@ -87,7 +88,7 @@ const activeSign = (state = null, action) => { // if (action.configId) // return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId, DraftDisplayConfigurationId: null}}}}) // else { - return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId}}}}) + return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId}}}}) // } } return state @@ -133,15 +134,14 @@ const activeSign = (state = null, action) => { stop_id: {$set: stopId}, agency: {$set: action.agency}, route: {$set: null}, - route_id: {$set: null}, + route_id: {$set: null} // TODO: update agency id from feed id? }) - } - else { + } else { updatedEntity = update(action.entity, { stop: {$set: action.value}, stop_id: {$set: stopId}, - agency: {$set: action.agency}, + agency: {$set: action.agency} // TODO: update agency id from feed id? }) } @@ -153,7 +153,7 @@ const activeSign = (state = null, action) => { return update(state, {affectedEntities: {$set: entities}}) case 'ROUTES': updatedEntity = update(action.entity, { - route: {$set: action.value}, + route: {$set: action.value} }) entities = [ ...state.affectedEntities.slice(0, foundIndex), From 3148e94417cce900818a982e8fd7aa068f7edeac Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 6 Dec 2016 12:34:16 -0500 Subject: [PATCH 153/323] add reduce-reducers and language packages --- package.json | 2 ++ yarn.lock | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8df6805f9..1b227ea67 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "font-awesome": "^4.7.0", "gravatar": "^1.5.2", "history": "^2.0.1", + "iso-639-1": "^1.2.4", "isomorphic-fetch": "^2.2.1", "jszip": "^3.0.0", "jwt-decode": "^2.1.0", @@ -66,6 +67,7 @@ "react-virtualized": "^8.5.0", "react-virtualized-select": "^2.1.0", "reactcss": "^1.0.4", + "reduce-reducers": "^0.1.2", "redux": "^3.3.1", "redux-logger": "^2.6.1", "redux-thunk": "^2.0.1", diff --git a/yarn.lock b/yarn.lock index 2cc6d943b..c33126d51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1040,7 +1040,7 @@ babel-register@^6.18.0: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.9.0, babel-runtime@^6.9.1: +babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" dependencies: @@ -3602,6 +3602,12 @@ isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" +iso-639-1@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-1.2.4.tgz#b0cef68adf904839c7d3e90bea249e79cc9498ee" + dependencies: + babel-runtime "^6.18.0" + isobject@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" @@ -6015,7 +6021,7 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "~0.1.0" -reduce-reducers@^0.1.0: +reduce-reducers@^0.1.0, reduce-reducers@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/reduce-reducers/-/reduce-reducers-0.1.2.tgz#fa1b4718bc5292a71ddd1e5d839c9bea9770f14b" From c3647555536f507b10bfa32f22b5c5ff9009e142 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 13:49:54 -0500 Subject: [PATCH 154/323] add r5 url to config --- config.yml.template | 1 + 1 file changed, 1 insertion(+) diff --git a/config.yml.template b/config.yml.template index b9cbc3920..73fd9ee9a 100644 --- a/config.yml.template +++ b/config.yml.template @@ -16,6 +16,7 @@ application: changelog_url: https://changelog.example.com support_email: support@example.com osm_vex: http://localhost:1000 + r5: http://localhost:8080 date_format: MMM Do YYYY auth0: From c0a50a174151df615309c94769fa3149aa69f552 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 13:50:59 -0500 Subject: [PATCH 155/323] fix check for s3 on gtfsFile and clean up datastore --- .../datatools/manager/persistence/DataStore.java | 13 +++---------- .../datatools/manager/persistence/FeedStore.java | 12 ++++++------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/DataStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/DataStore.java index 0087b42c5..5defa9aa6 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/DataStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/DataStore.java @@ -39,7 +39,7 @@ public DataStore(File directory, String dataFile) { directory.mkdirs(); try { - LOG.info(directory.getCanonicalPath()); + LOG.info(String.join("/", directory.getCanonicalPath(), dataFile)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -60,7 +60,7 @@ public DataStore(File directory, String dataFile, List>inpu directory.mkdirs(); try { - LOG.info(directory.getCanonicalPath()); + LOG.info(String.join("/", directory.getCanonicalPath(), dataFile)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -71,14 +71,7 @@ public DataStore(File directory, String dataFile, List>inpu .closeOnJvmShutdown() .make(); - Comparator> comparator = new Comparator>(){ - - @Override - public int compare(Tuple2 o1, - Tuple2 o2) { - return o1.a.compareTo(o2.a); - } - }; + Comparator> comparator = (o1, o2) -> o1.a.compareTo(o2.a); // need to reverse sort list Iterator> iter = Pump.sort(inputData.iterator(), diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index d6acfb64d..f696e02dc 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -147,13 +147,13 @@ private String getS3Key (String id) { */ public File getFeed (String id) { // local storage - if (path != null) { + if (!DataManager.useS3) { File feed = new File(path, id); // don't let folks get feeds outside of the directory if (feed.getParentFile().equals(path) && feed.exists()) return feed; } // s3 storage - else if (s3Bucket != null) { + else { AWSCredentials creds = getAWSCreds(); try { LOG.info("Downloading feed from s3"); @@ -184,7 +184,7 @@ private File storeFeedLocally(String id, InputStream inputStream, FeedSource fee // store latest as feed-source-id.zip if (feedSource != null) { try { - File version = writeFileUsingInputStream(id, inputStream, feedSource); + File version = writeFileUsingInputStream(id, inputStream); copyVersionToLatest(version, feedSource); return version; } catch (Exception e) { @@ -197,7 +197,7 @@ private File storeFeedLocally(String id, InputStream inputStream, FeedSource fee private void copyVersionToLatest(File version, FeedSource feedSource) { File latest = new File(String.valueOf(path), feedSource.id + ".zip"); try { - FileUtils.copyFile(version, latest); + FileUtils.copyFile(version, latest, true); LOG.info("Copying version to latest {}", feedSource); } catch (IOException e) { e.printStackTrace(); @@ -205,9 +205,9 @@ private void copyVersionToLatest(File version, FeedSource feedSource) { } } - private File writeFileUsingInputStream(String id, InputStream inputStream, FeedSource feedSource) throws IOException { + private File writeFileUsingInputStream(String filename, InputStream inputStream) throws IOException { OutputStream output = null; - File out = new File(path, id); + File out = new File(path, filename); try { output = new FileOutputStream(out); byte[] buf = new byte[1024]; From 3390fd4688dc70872923c80be4bedff67674b6a5 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 14:30:45 -0500 Subject: [PATCH 156/323] serve js/css from s3 --- .gitignore | 1 - config.yml.template | 1 + .../datatools/manager/DataManager.java | 49 +++++-------------- src/main/resources/public/index.html | 15 ++++++ 4 files changed, 29 insertions(+), 37 deletions(-) create mode 100644 src/main/resources/public/index.html diff --git a/.gitignore b/.gitignore index ad3a24cb2..321b54668 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ coverage .idea server_config.js src/main/client/config.js -src/main/resources/public application.conf target/ .java-version diff --git a/config.yml.template b/config.yml.template index 73fd9ee9a..71703a9f4 100644 --- a/config.yml.template +++ b/config.yml.template @@ -8,6 +8,7 @@ application: gtfs_s3_bucket: bucket-name port: 4000 title: Data Manager + assets_bucket: bucket-name logo: http://gtfs-assets-dev.conveyal.com/data_manager.png # defaults to src/main/client/assets/application_logo.png active_project: project-id notifications_enabled: false diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index fa64f77ea..5ca57dbba 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -1,6 +1,5 @@ package com.conveyal.datatools.manager; -import com.amazonaws.services.s3.AmazonS3Client; import com.conveyal.datatools.manager.auth.Auth0Connection; import com.conveyal.datatools.manager.controllers.DumpController; @@ -12,42 +11,31 @@ import com.conveyal.datatools.manager.extensions.transitfeeds.TransitFeedsFeedResource; import com.conveyal.datatools.manager.extensions.transitland.TransitLandFeedResource; -import com.conveyal.datatools.manager.jobs.FetchProjectFeedsJob; -import com.conveyal.datatools.manager.jobs.LoadGtfsApiFeedJob; import com.conveyal.datatools.common.status.MonitorableJob; -import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.Project; import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.CorsFilter; import com.conveyal.gtfs.GTFSCache; -import com.conveyal.gtfs.api.ApiMain; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.google.common.util.concurrent.UncheckedExecutionException; -import com.google.gson.Gson; -import org.apache.http.concurrent.Cancellable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import spark.utils.IOUtils; import java.io.File; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.MissingResourceException; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; -import static java.util.concurrent.TimeUnit.*; - import static spark.Spark.*; public class DataManager { @@ -193,16 +181,10 @@ public static void main(String[] args) throws IOException { response.type("application/json"); response.header("Content-Encoding", "gzip"); }); - - // return js for any other request - get("/main.js", (request, response) -> { - try (InputStream stream = DataManager.class.getResourceAsStream("/public/main.js")) { - return IOUtils.toString(stream); - } catch (IOException e) { - return null; - // if the resource doesn't exist we just carry on. - } - }); + // load index.html + InputStream stream = DataManager.class.getResourceAsStream("/public/index.html"); + String index = IOUtils.toString(stream).replace("${S3BUCKET}", getConfigPropertyAsText("application.assets_bucket")); + stream.close(); // return 404 for any api response that's not found get(apiPrefix + "*", (request, response) -> { @@ -210,23 +192,18 @@ public static void main(String[] args) throws IOException { return null; }); - // return assets as byte array - get("/assets/*", (request, response) -> { - try (InputStream stream = DataManager.class.getResourceAsStream("/public" + request.pathInfo())) { - return IOUtils.toByteArray(stream); - } catch (IOException e) { - return null; - } - }); +// // return assets as byte array +// get("/assets/*", (request, response) -> { +// try (InputStream stream = DataManager.class.getResourceAsStream("/public" + request.pathInfo())) { +// return IOUtils.toByteArray(stream); +// } catch (IOException e) { +// return null; +// } +// }); // return index.html for any sub-directory get("/*", (request, response) -> { response.type("text/html"); - try (InputStream stream = DataManager.class.getResourceAsStream("/public/index.html")) { - return IOUtils.toString(stream); - } catch (IOException e) { - return null; - // if the resource doesn't exist we just carry on. - } + return index; }); registerExternalResources(); } diff --git a/src/main/resources/public/index.html b/src/main/resources/public/index.html new file mode 100644 index 000000000..a626c051e --- /dev/null +++ b/src/main/resources/public/index.html @@ -0,0 +1,15 @@ + + + + + + + Catalogue + + + + +
    + + + From af5dc5c5f2981c668352e4c535097889fde203e2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 15:16:53 -0500 Subject: [PATCH 157/323] fix log class --- .../datatools/manager/controllers/api/GtfsApiController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java index b112475b1..516411e95 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsApiController.java @@ -25,7 +25,7 @@ * Created by landon on 4/12/16. */ public class GtfsApiController { - public static final Logger LOG = LoggerFactory.getLogger(ProjectController.class); + public static final Logger LOG = LoggerFactory.getLogger(GtfsApiController.class); public static String feedBucket; public static FeedUpdater feedUpdater; private static AmazonS3Client s3 = new AmazonS3Client(); From f45a6d13a932d1b7a2f276efaf21e4a61f37c095 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 17:52:18 -0500 Subject: [PATCH 158/323] log missing zip error --- .../datatools/manager/controllers/api/ProjectController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 9ba1d5e84..5a72cc7d9 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -470,7 +470,8 @@ private static Object downloadMergedFeed(Request req, Response res) throws IOExc feedSourceMap.put(fs, zipFile); } catch(Exception e) { e.printStackTrace(); - halt(500); +// halt(500); + LOG.error("Zipfile for version {} not found", version.id); } } @@ -490,7 +491,6 @@ private static Object downloadMergedFeed(Request req, Response res) throws IOExc out.write(tableOut); out.closeEntry(); } - } out.close(); From da205c12deb62fd428f09b61534158ed8ab06f73 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 12 Dec 2016 18:48:43 -0500 Subject: [PATCH 159/323] fixed merged feed export --- gtfs.yml | 14 ++++----- .../controllers/api/ProjectController.java | 30 ++++++++++++++----- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/gtfs.yml b/gtfs.yml index a8d82ccfe..dbfc59577 100644 --- a/gtfs.yml +++ b/gtfs.yml @@ -85,7 +85,7 @@ fields: - name: "agency_id" required: false - inputType: ID + inputType: GTFS_ID columnWidth: 12 helpContent: "The agency_id field is an ID that uniquely identifies a transit agency. A transit feed may represent data from more than one agency. The agency_id is dataset unique. This field is optional for transit feeds that only contain data for a single agency." - name: "agency_name" @@ -141,12 +141,12 @@ fields: - name: "stop_id" required: true - inputType: ID + inputType: GTFS_ID columnWidth: 6 helpContent: "The stop_id field contains an ID that uniquely identifies a stop or station. Multiple routes may use the same stop. The stop_id is dataset unique." - name: "stop_code" required: false - inputType: ID + inputType: GTFS_ID columnWidth: 6 helpContent: "The stop_code field contains short text or a number that uniquely identifies the stop for passengers. Stop codes are often used in phone-based transit information systems or printed on stop signage to make it easier for riders to get a stop schedule or real-time arrival information for a particular stop." - name: "stop_name" @@ -242,7 +242,7 @@ # helpContent: The route_id field contains an ID that uniquely identifies a route. The route_id is dataset unique. - name: route_id required: true - inputType: ID + inputType: GTFS_ID columnWidth: 5 helpContent: The route_id field contains an ID that uniquely identifies a route. The route_id is dataset unique. - name: route_short_name @@ -334,7 +334,7 @@ helpContent: "The service_id contains an ID that uniquely identifies a set of dates when service is available for one or more routes. This value is referenced from thecalendar.txt or calendar_dates.txt file." - name: "trip_id" required: true - inputType: ID + inputType: GTFS_ID columnWidth: 6 helpContent: "The trip_id field contains an ID that identifies a trip. The trip_id is dataset unique." - name: "trip_headsign" @@ -460,7 +460,7 @@ fields: - name: "service_id" required: true - inputType: ID + inputType: GTFS_ID columnWidth: 6 helpContent: "The service_id contains an ID that uniquely identifies a set of dates when service is available for one or more routes. Each service_id value can appear at most once in a calendar.txt file. This value is dataset unique. It is referenced by the trips.txt file." - name: "description" @@ -567,7 +567,7 @@ fields: - name: "fare_id" required: true - inputType: ID + inputType: GTFS_ID columnWidth: 6 helpContent: "The fare_id field contains an ID that uniquely identifies a fare class. The fare_id is dataset unique." - name: "price" diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 5a72cc7d9..1e83ec536 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -14,6 +14,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.google.gson.JsonObject; import org.apache.http.concurrent.Cancellable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -538,13 +540,17 @@ private static byte[] mergeTables(JsonNode tableNode, Map f int feedIndex = 0; - JsonNode fieldsNode = tableNode.get("fields"); - String[] headers = new String[fieldsNode.size()]; + ArrayNode fieldsNode = (ArrayNode) tableNode.get("fields"); +// fieldsNode. + List headers = new ArrayList<>(); for (int i = 0; i < fieldsNode.size(); i++) { JsonNode fieldNode = fieldsNode.get(i); String fieldName = fieldNode.get("name").asText(); - - headers[i] = fieldName; + Boolean notInSpec = fieldNode.has("datatools") && fieldNode.get("datatools").asBoolean(); + if (notInSpec) { + fieldsNode.remove(i); + } + headers.add(fieldName); } // write headers to table @@ -572,15 +578,25 @@ private static byte[] mergeTables(JsonNode tableNode, Map f while((line = in.readLine()) != null) { String[] newValues = new String[fieldsNode.size()]; String[] values = line.split(",", -1); - for(int v=0; v < fieldsNode.size(); v++) { + if (values.length == 1) { + LOG.warn("Found blank line. Skipping..."); + continue; + } + for(int v = 0; v < fieldsNode.size(); v++) { JsonNode fieldNode = fieldsNode.get(v); String fieldName = fieldNode.get("name").asText(); // get index of field from GTFS spec as it appears in feed int index = fieldList.indexOf(fieldName); String val = ""; - if(index != -1) { - val = values[index]; + try { + index = fieldList.indexOf(fieldName); + if(index != -1) { + val = values[index]; + } + } catch (ArrayIndexOutOfBoundsException e) { + LOG.warn("Index {} out of bounds for file {} and feed {}", index, entry.getName(), fs.name); + continue; } String fieldType = fieldNode.get("inputType").asText(); From 8a8187c657b6dc74eef1414ce029a4b76b3038e5 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:08:29 -0500 Subject: [PATCH 160/323] create feed info if none exists --- .../controllers/api/FeedInfoController.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java index fdda2be5a..ee4067b1d 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/FeedInfoController.java @@ -6,6 +6,7 @@ import com.conveyal.datatools.editor.datastore.VersionedDataStore; import com.conveyal.datatools.editor.models.transit.EditorFeed; import com.conveyal.datatools.editor.models.transit.GtfsRouteType; +import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; import com.conveyal.gtfs.model.FeedInfo; @@ -43,7 +44,24 @@ public static Object getFeedInfo(Request req, Response res) { // TODO: return all feedInfos for project? } GlobalTx gtx = VersionedDataStore.getGlobalTx(); - + if (!gtx.feeds.containsKey(id)) { + // create new EditorFeed if id exists in manager + if (FeedSource.get(id) != null) { + EditorFeed fs = new EditorFeed(id); + gtx.feeds.put(fs.id, fs); + gtx.commit(); + try { + return Base.toJson(fs, false); + } catch (IOException e) { + e.printStackTrace(); + } + } + else { + gtx.rollback(); + halt(404, "Feed id does not exist"); + return null; + } + } EditorFeed fs = gtx.feeds.get(id); return fs; } @@ -62,7 +80,7 @@ public static Object createFeedInfo(Request req, Response res) { if (gtx.feeds.containsKey(fs.id)) { gtx.rollback(); - halt(404); + halt(404, "Feed id already exists in editor database"); return null; } From ef358276b4b4cb0735a3ea3351c97e4ee21d4c90 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:10:26 -0500 Subject: [PATCH 161/323] add pattern direction --- .../conveyal/datatools/editor/controllers/Base.java | 3 +++ .../editor/jobs/ProcessGtfsSnapshotExport.java | 8 +++++++- .../editor/jobs/ProcessGtfsSnapshotMerge.java | 7 +++++++ .../conveyal/datatools/editor/models/transit/Trip.java | 2 +- .../datatools/editor/models/transit/TripDirection.java | 10 +++++++++- .../datatools/editor/models/transit/TripPattern.java | 3 ++- 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/Base.java b/src/main/java/com/conveyal/datatools/editor/controllers/Base.java index c49c429dd..71ce33d5c 100755 --- a/src/main/java/com/conveyal/datatools/editor/controllers/Base.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/Base.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.editor.controllers; import com.conveyal.datatools.editor.models.transit.GtfsRouteType; +import com.conveyal.datatools.editor.models.transit.TripDirection; import com.conveyal.geojson.GeoJsonModule; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerationException; @@ -27,6 +28,8 @@ public class Base { mod.addSerializer(LocalDate.class, new JacksonSerializers.LocalDateSerializer()); mod.addDeserializer(GtfsRouteType.class, new JacksonSerializers.GtfsRouteTypeDeserializer()); mod.addSerializer(GtfsRouteType.class, new JacksonSerializers.GtfsRouteTypeSerializer()); + mod.addDeserializer(TripDirection.class, new JacksonSerializers.TripDirectionDeserializer()); + mod.addSerializer(TripDirection.class, new JacksonSerializers.TripDirectionSerializer()); mapper.registerModule(mod); mapper.registerModule(new GeoJsonModule()); } diff --git a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java index 91c1b399e..4e4522a99 100755 --- a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java +++ b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java @@ -165,10 +165,16 @@ public void run() { gtfsTrip.service_id = feed.services.get(trip.calendarId).service_id; gtfsTrip.trip_headsign = trip.tripHeadsign; gtfsTrip.trip_short_name = trip.tripShortName; - gtfsTrip.direction_id = trip.tripDirection == TripDirection.A ? 0 : 1; TripPattern pattern = feedTx.tripPatterns.get(trip.patternId); + // assign pattern direction if not null + if (pattern.patternDirection != null) { + gtfsTrip.direction_id = pattern.patternDirection.toGtfs(); + } + else { + gtfsTrip.direction_id = trip.tripDirection.toGtfs(); + } Tuple2 nextKey = feed.shape_points.ceilingKey(new Tuple2(pattern.id, null)); if ((nextKey == null || !pattern.id.equals(nextKey.a)) && pattern.shape != null && !pattern.useStraightLineDistances) { // this shape has not yet been saved diff --git a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java index 8acd2d6b1..4b0e7bf77 100755 --- a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java +++ b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java @@ -505,6 +505,7 @@ public TripPattern createTripPatternFromTrip (com.conveyal.gtfs.model.Trip gtfsT patt.id = gtfsPattern.pattern_id; patt.patternStops = new ArrayList(); + patt.patternDirection = TripDirection.fromGtfs(gtfsTrip.direction_id); com.conveyal.gtfs.model.StopTime[] stopTimes = input.stop_times.subMap(new Tuple2(gtfsTrip.trip_id, 0), new Tuple2(gtfsTrip.trip_id, Fun.HI)).values().toArray(new com.conveyal.gtfs.model.StopTime[0]); @@ -524,8 +525,14 @@ public TripPattern createTripPatternFromTrip (com.conveyal.gtfs.model.Trip gtfsT Stop stop = stopIdMap.get(new Tuple2(st.stop_id, patt.feedId)); tps.stopId = stop.id; + // set timepoint according to first gtfs value and then whether arrival and departure times are present if (st.timepoint != Entity.INT_MISSING) tps.timepoint = st.timepoint == 1; + else if (st.arrival_time != Entity.INT_MISSING && st.departure_time != Entity.INT_MISSING) { + tps.timepoint = true; + } + else + tps.timepoint = false; if (st.departure_time != Entity.INT_MISSING && st.arrival_time != Entity.INT_MISSING) tps.defaultDwellTime = st.departure_time - st.arrival_time; diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/Trip.java b/src/main/java/com/conveyal/datatools/editor/models/transit/Trip.java index c191c05d3..27fa630e6 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/Trip.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/Trip.java @@ -50,7 +50,7 @@ public Trip(com.conveyal.gtfs.model.Trip trip, Route route, TripPattern pattern, gtfsTripId = trip.trip_id; tripHeadsign = trip.trip_headsign; tripShortName = trip.trip_short_name; - tripDirection = trip.direction_id == 0 ? TripDirection.A : TripDirection.B; + tripDirection = TripDirection.fromGtfs(trip.direction_id); blockId = trip.block_id; this.routeId = route.id; this.patternId = pattern.id; diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/TripDirection.java b/src/main/java/com/conveyal/datatools/editor/models/transit/TripDirection.java index fb7e474f7..e17faa837 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/TripDirection.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/TripDirection.java @@ -2,5 +2,13 @@ public enum TripDirection { A, - B + B; + + public int toGtfs () { + return this == TripDirection.A ? 0 : 1; + } + + public static TripDirection fromGtfs (int dir) { + return dir == 0 ? TripDirection.A : TripDirection.B; + } } \ No newline at end of file diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java b/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java index 40d4900e9..caa086c28 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/TripPattern.java @@ -20,7 +20,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -45,6 +44,8 @@ public class TripPattern extends Model implements Cloneable, Serializable { public String feedId; + public TripDirection patternDirection; + public List patternStops = new ArrayList(); public int getNumberOfTrips () { From f03b22b0f1d20457cb99040feb6103efee45a2c3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:11:22 -0500 Subject: [PATCH 162/323] better error messages, beginnings of secure requests --- .../controllers/api/RouteController.java | 46 +++++++++++++++++-- .../controllers/api/TripController.java | 1 + 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java index c95776af5..b31f8bfcd 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/RouteController.java @@ -2,6 +2,8 @@ import com.conveyal.datatools.editor.utils.S3Utils; import com.conveyal.datatools.editor.datastore.FeedTx; +import com.conveyal.datatools.manager.auth.Auth0UserProfile; +import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; import com.google.common.base.Function; @@ -92,7 +94,7 @@ public static Object createRoute(Request req, Response res) { GlobalTx gtx = VersionedDataStore.getGlobalTx(); if (!gtx.feeds.containsKey(route.feedId)) { gtx.rollback(); - halt(400); + halt(400, String.join("Feed %s does not exist in editor", route.feedId)); } if (req.session().attribute("feedId") != null && !req.session().attribute("feedId").equals(route.feedId)) @@ -104,7 +106,7 @@ public static Object createRoute(Request req, Response res) { if (tx.routes.containsKey(route.id)) { tx.rollback(); - halt(400); + halt(400, "Failed to create route with duplicate id"); } // check if gtfsRouteId is specified, if not create from DB id @@ -326,7 +328,45 @@ public Trip apply(Tuple2 input) { throw e; } } - +// public static FeedTx requestFeedTx(Request req, FeedSource s, String action) { +// Auth0UserProfile userProfile = req.attribute("user"); +// Boolean publicFilter = Boolean.valueOf(req.queryParams("public")); +// +// // check for null feedsource +// if (s == null) +// halt(400, "Feed source ID does not exist"); +// +// boolean authorized; +// switch (action) { +// case "manage": +// authorized = userProfile.canManageFeed(s.projectId, s.id); +// break; +// case "view": +// authorized = userProfile.canViewFeed(s.projectId, s.id); +// break; +// default: +// authorized = false; +// break; +// } +// +// // if requesting public sources +// if (publicFilter){ +// // if feed not public and user not authorized, halt +// if (!s.isPublic && !authorized) +// halt(403, "User not authorized to perform action on feed source"); +// // if feed is public, but action is managerial, halt (we shouldn't ever get here, but just in case) +// else if (s.isPublic && action.equals("manage")) +// halt(403, "User not authorized to perform action on feed source"); +// +// } +// else { +// if (!authorized) +// halt(403, "User not authorized to perform action on feed source"); +// } +// +// // if we make it here, user has permission and it's a valid feedsource +// return s; +// } public static void register (String apiPrefix) { get(apiPrefix + "secure/route/:id", RouteController::getRoute, json::write); options(apiPrefix + "secure/route", (q, s) -> ""); diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java index 2f63655ae..c97a9de2a 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripController.java @@ -58,6 +58,7 @@ else if (patternId != null && calendarId != null) { halt(404); } else { + LOG.info("requesting trips for pattern/cal"); return Base.toJson(tx.getTripsByPatternAndCalendar(patternId, calendarId), false); } } From 24872426d3fbf5dcf03edfc925a398d476d3eb63 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:11:57 -0500 Subject: [PATCH 163/323] de-lambdafy --- .../com/conveyal/datatools/editor/datastore/FeedTx.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java index 5f3b4a108..cea46f98b 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java @@ -211,7 +211,12 @@ public Collection getTripsByPatternAndCalendar(String patternId, String ca Set, String>> matchedKeys = tripsByPatternAndCalendar.subSet(new Tuple2(new Tuple2(patternId, calendarId), null), new Tuple2(new Tuple2(patternId, calendarId), Fun.HI)); - return Collections2.transform(matchedKeys, input -> trips.get(input.b)); +// return Collections2.transform(matchedKeys, input -> trips.get(input.b)); + return Collections2.transform(matchedKeys, new Function, String>, Trip>() { + public Trip apply(Tuple2, String> input) { + return trips.get(input.b); + } + }); } public Collection getStopsWithinBoundingBox (double north, double east, double south, double west) { From cd45f3b1dc331df5dc881ea2c34c838b9eebec83 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:12:27 -0500 Subject: [PATCH 164/323] direction serializer --- .../editor/utils/JacksonSerializers.java | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java b/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java index 4c6bf95fd..03c11ae38 100644 --- a/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java +++ b/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.editor.utils; import com.conveyal.datatools.editor.models.transit.GtfsRouteType; +import com.conveyal.datatools.editor.models.transit.TripDirection; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; @@ -149,7 +150,7 @@ public LocalDate deserialize(JsonParser jp, } } - /** serialize local dates as noon GMT epoch times */ + /** serialize GtfsRouteType as GTFS integer value */ public static class GtfsRouteTypeSerializer extends StdScalarSerializer { public GtfsRouteTypeSerializer() { super(GtfsRouteType.class, false); @@ -157,13 +158,12 @@ public GtfsRouteTypeSerializer() { @Override public void serialize(GtfsRouteType gtfsRouteType, JsonGenerator jgen, - SerializerProvider arg2) throws IOException, - JsonGenerationException { + SerializerProvider arg2) throws IOException { jgen.writeNumber(gtfsRouteType.toGtfs()); } } - /** deserialize local dates from GMT epochs */ + /** serialize GTFS integer value to GtfsRouteType */ public static class GtfsRouteTypeDeserializer extends StdScalarDeserializer { public GtfsRouteTypeDeserializer () { super(GtfsRouteType.class); @@ -171,12 +171,35 @@ public GtfsRouteTypeDeserializer () { @Override public GtfsRouteType deserialize(JsonParser jp, - DeserializationContext arg1) throws IOException, - JsonProcessingException { + DeserializationContext arg1) throws IOException { return GtfsRouteType.fromGtfs(jp.getValueAsInt()); } } + /** serialize GtfsRouteType as GTFS integer value */ + public static class TripDirectionSerializer extends StdScalarSerializer { + public TripDirectionSerializer() { + super(TripDirection.class, false); + } + + @Override + public void serialize(TripDirection gtfsRouteType, JsonGenerator jgen, + SerializerProvider arg2) throws IOException { + jgen.writeNumber(gtfsRouteType.toGtfs()); + } + } + + /** serialize GTFS integer value to TripDirection */ + public static class TripDirectionDeserializer extends StdScalarDeserializer { + public TripDirectionDeserializer () { super(TripDirection.class); } + + @Override + public TripDirection deserialize(JsonParser jp, + DeserializationContext arg1) throws IOException { + return TripDirection.fromGtfs(jp.getValueAsInt()); + } + } + public static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd"); /** Serialize a local date to an ISO date (year-month-day) */ From 7bef812725219a66f1dd9a7f67787f73e16ca1b3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:12:57 -0500 Subject: [PATCH 165/323] add permissions check for edit/approve --- .../manager/auth/Auth0UserProfile.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/auth/Auth0UserProfile.java b/src/main/java/com/conveyal/datatools/manager/auth/Auth0UserProfile.java index bd8d2bf32..c13012672 100644 --- a/src/main/java/com/conveyal/datatools/manager/auth/Auth0UserProfile.java +++ b/src/main/java/com/conveyal/datatools/manager/auth/Auth0UserProfile.java @@ -260,6 +260,34 @@ public boolean canManageFeed(String projectID, String feedID) { return false; } + public boolean canEditGTFS(String projectID, String feedID) { + if (canAdministerApplication() || canAdministerProject(projectID)) { + return true; + } + Project[] projectList = app_metadata.getDatatoolsInfo().projects; + for(Project project : projectList) { + System.out.println("project_id: " + project.project_id); + if (project.project_id.equals(projectID)) { + return checkFeedPermission(project, feedID, "edit-gtfs"); + } + } + return false; + } + + public boolean canApproveGTFS(String projectID, String feedID) { + if (canAdministerApplication() || canAdministerProject(projectID)) { + return true; + } + Project[] projectList = app_metadata.getDatatoolsInfo().projects; + for(Project project : projectList) { + System.out.println("project_id: " + project.project_id); + if (project.project_id.equals(projectID)) { + return checkFeedPermission(project, feedID, "approve-gtfs"); + } + } + return false; + } + public boolean checkFeedPermission(Project project, String feedID, String permissionType) { String feeds[] = project.defaultFeeds; From 15face8e69b3bedbeed52f46eb715eae8183fc25 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:13:21 -0500 Subject: [PATCH 166/323] delete transport network on delete --- .../com/conveyal/datatools/manager/models/FeedVersion.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index dfec29438..06c4d74f8 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -599,6 +599,9 @@ public void delete() { d.feedVersionIds.remove(this.id); } + getTransportNetworkPath().delete(); + + versionStore.delete(this.id); } @JsonIgnore From 6570c2da0300f93682ff6e44926a98cc2b293fac Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:13:41 -0500 Subject: [PATCH 167/323] clean up unused creds --- .../com/conveyal/datatools/manager/persistence/FeedStore.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index f696e02dc..1fddedf1f 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -154,7 +154,6 @@ public File getFeed (String id) { } // s3 storage else { - AWSCredentials creds = getAWSCreds(); try { LOG.info("Downloading feed from s3"); S3Object object = s3Client.getObject( From 27a7c0f9cf092d9288850070b29de6a20b37a076 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 13 Dec 2016 08:14:32 -0500 Subject: [PATCH 168/323] clean up formatting; add ControlPoints layers --- .../client/editor/components/EntityDetails.js | 7 +- .../client/editor/components/GtfsEditor.js | 41 ++--- .../components/map/AddableStopsLayer.js | 5 +- .../editor/components/map/ControlPoint.js | 61 +++++++ .../components/map/ControlPointsLayer.js | 150 ++++++------------ .../client/editor/components/map/EditorMap.js | 33 +++- .../components/map/EditorMapLayersControl.js | 21 ++- .../editor/components/map/PatternStopPopup.js | 18 +-- .../components/map/PatternStopsLayer.js | 10 +- .../editor/components/map/PatternsLayer.js | 9 +- .../editor/components/map/StopMarkersLayer.js | 5 +- .../editor/components/map/StopsLayer.js | 4 +- .../pattern/CalculateDefaultTimesForm.js | 2 +- .../components/pattern/EditSchedulePanel.js | 3 +- .../components/pattern/EditShapePanel.js | 9 +- .../components/pattern/PatternStopsPanel.js | 28 ++-- .../components/pattern/TripPatternList.js | 1 - .../components/pattern/TripPatternViewer.js | 6 +- .../components/timetable/TimetableEditor.js | 6 +- 19 files changed, 224 insertions(+), 195 deletions(-) create mode 100644 src/main/client/editor/components/map/ControlPoint.js diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 5ad42c9c1..2537353a9 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -169,7 +169,7 @@ export default class EntityDetails extends Component { : {editorField}{field.required ? ' *' : ''} switch (field.inputType) { - case 'ID': + case 'GTFS_ID': isNotValid = field.required && !currentValue let indices = [] let idList = this.props.entities.map(e => e[field.name]) @@ -267,7 +267,7 @@ export default class EntityDetails extends Component { /> ] - if (field.name === 'agencyBrandingUrl' || field.name === 'routeBrandingUrl') { + if (field.name === 'agency_branding_url' || field.name === 'route_branding_url') { elements.push( ,
    {this.props.subComponent === 'trippattern' ? + showConfirmModal={this.props.showConfirmModal} /> : this.state.editFareRules ? fareRulesForm : this.props.activeComponent === 'scheduleexception' diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 5d5f02f8d..a09dc6cd3 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -65,7 +65,7 @@ export default class GtfsEditor extends Component { this.props.onComponentMount(this.props) } componentDidUpdate (prevProps) { - // console.log(prevProps, this.props) + // handles back button this.props.onComponentUpdate(prevProps, this.props) } componentWillReceiveProps (nextProps) { @@ -80,12 +80,8 @@ export default class GtfsEditor extends Component { this.props.getGtfsTable(nextProps.activeComponent, nextProps.feedSource.id) } // fetch sub components of active entity on active entity switch (e.g., fetch trip patterns when route changed) - if (nextProps.feedSource && nextProps.activeEntity && (!this.props.activeEntity || nextProps.activeEntity.id !== this.props.activeEntity.id)) { - // console.log(nextProps.activeComponent) - // console.log(nextProps.activeEntity, nextProps.activeEntityId) - if (nextProps.activeComponent === 'route' && this.props.activeComponent !== 'route') { - this.props.fetchTripPatternsForRoute(nextProps.feedSource.id, nextProps.activeEntity.id) - } + if (nextProps.activeComponent === 'route' && nextProps.feedSource && nextProps.activeEntity && (!this.props.activeEntity || nextProps.activeEntity.id !== this.props.activeEntity.id)) { + this.props.fetchTripPatternsForRoute(nextProps.feedSource.id, nextProps.activeEntity.id) } // fetch required sub sub component entities if active sub entity changes if (nextProps.subSubComponent && nextProps.activeSubSubEntity && !shallowEqual(nextProps.activeSubSubEntity, this.props.activeSubSubEntity)) { @@ -114,7 +110,6 @@ export default class GtfsEditor extends Component { : 0 } render () { - // console.log(this.props) const { feedSource, user, @@ -162,30 +157,26 @@ export default class GtfsEditor extends Component { }} getGtfsEntityIndex={(type, id) => { return entities.findIndex(ent => ent.id === id) - }} - /> + }} /> : null const defaultTitle = `${getConfigProperty('application.title')}: GTFS Editor` return (
    + titleTemplate={`${defaultTitle} - %s`} /> + setActiveEntity={setActiveEntity} />
    + top: 0}}> {subSubComponent === 'timetable' // && activeEntity ? + updateCellValue={updateCellValue} /> : activeComponent === 'feedinfo' ? + {...this.props} /> : activeComponent ? [ , + key='entity-list' />, entityDetails ] : null @@ -236,13 +224,11 @@ export default class GtfsEditor extends Component { drawStops={mapState.zoom > 14} // && (activeComponent === 'stop' || editSettings.addStops)} zoomToTarget={mapState.target} sidebarExpanded={sidebarExpanded} - {...this.props} - /> + {...this.props} /> {!activeComponent && !hideTutorial ? + setTutorialHidden={setTutorialHidden} /> : null } this.showConfirmModal(props)} setActiveEntity={setActiveEntity} - feedInfo={feedInfo} - /> + feedInfo={feedInfo} />
    diff --git a/src/main/client/editor/components/map/AddableStopsLayer.js b/src/main/client/editor/components/map/AddableStopsLayer.js index 41df497ad..68e591d81 100644 --- a/src/main/client/editor/components/map/AddableStopsLayer.js +++ b/src/main/client/editor/components/map/AddableStopsLayer.js @@ -21,8 +21,7 @@ export default class AddableStopsLayer extends Component { ref='addableStops' key='addableStops' > - { - stops.length && activePattern && editSettings.addStops && mapState.zoom > 14 + {stops && activePattern && editSettings.addStops && mapState.zoom > 14 ? stops .filter(stop => { if (!bounds) return false @@ -73,7 +72,7 @@ export default class AddableStopsLayer extends Component { {activePattern.patternStops && activePattern.patternStops.map((stop, i) => { let index = activePattern.patternStops.length - i return ( - + {index === 1 ? 'Add to beginning' : `Insert as stop #${index}`} ) diff --git a/src/main/client/editor/components/map/ControlPoint.js b/src/main/client/editor/components/map/ControlPoint.js new file mode 100644 index 000000000..283cf10c3 --- /dev/null +++ b/src/main/client/editor/components/map/ControlPoint.js @@ -0,0 +1,61 @@ +import React, { Component, PropTypes } from 'react' +import { Marker } from 'react-leaflet' + +import { handlePatternEdit, handleControlPointDragEnd } from '../../util/map' + +export default class ControlPoint extends Component { + static propTypes = { + position: PropTypes.array + } + constructor (props) { + super(props) + this.state = { + timer: null, + latlng: null + } + } + render () { + const { position, icon, previous, next, activePattern, index, permanent, removeControlPoint, updateActiveEntity, updateControlPoint, editSettings } = this.props + const { patternCoordinates, followStreets } = editSettings + return ( + { + const timerFunction = () => { + const latlng = this.refs.marker.leafletElement.getLatLng() + console.log(latlng) + handlePatternEdit(latlng, this.props.previous, this.props.next, this.props.activePattern, followStreets, patternCoordinates) + .then(coords => { + this.setState({latlng}) + this.props.updatePatternCoordinates(coords) + }) + } + timerFunction() + let timer = setInterval(timerFunction, 500) + this.setState({timer}) + }} + onDragEnd={(e) => { + console.log('drag end') + // clear timer + if (this.state.timer) clearInterval(this.state.timer) + const { snap, distTraveled } = handleControlPointDragEnd(e, patternCoordinates) + updateActiveEntity(activePattern, 'trippattern', {shape: {type: 'LineString', coordinates: patternCoordinates}}) + updateControlPoint(index, snap, distTraveled) + this.setState({latlng: null}) + }} + onClick={(e) => { + console.log('control point clicked', e) + // only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop) + if (!permanent) { + removeControlPoint(activePattern, index, previous, next) + } + }} + color='black' + /> + ) + } +} diff --git a/src/main/client/editor/components/map/ControlPointsLayer.js b/src/main/client/editor/components/map/ControlPointsLayer.js index 71e2d819d..9ded60350 100644 --- a/src/main/client/editor/components/map/ControlPointsLayer.js +++ b/src/main/client/editor/components/map/ControlPointsLayer.js @@ -1,118 +1,72 @@ import React, { Component, PropTypes } from 'react' import { divIcon } from 'leaflet' -import { Marker, FeatureGroup } from 'react-leaflet' -import point from 'turf-point' +import { FeatureGroup } from 'react-leaflet' import along from 'turf-along' +import ControlPoint from './ControlPoint' + export default class ControlPointsLayer extends Component { static propTypes = { - stops: PropTypes.array + stops: PropTypes.array, + handlePatternEdit: PropTypes.func, + polyline: PropTypes.object, + handleControlPointDragEnd: PropTypes.func, + onDrag: PropTypes.func, + removeControlPoint: PropTypes.func + } + getPrevious (index, controlPoints, pattern) { + let prevControlPoint = controlPoints[index - 1] + return prevControlPoint + ? prevControlPoint.point + : along(pattern.shape, 0, 'meters') + } + getNext (index, controlPoints) { + let nextControlPoint = controlPoints[index + 1] + if (nextControlPoint) { + return nextControlPoint.point + } else { + return null + } } render () { const { stops, activePattern, editSettings, - handlePatternEdit, - polyline, - handleControlPointDragEnd, - onDrag, - removeControlPoint, controlPoints } = this.props - let timer = null - const circleIcon = divIcon({ - className: '', - html: `` - }) - const beginPoint = activePattern && activePattern.shape ? [activePattern.shape.coordinates[0][1], activePattern.shape.coordinates[0][0]] : null return ( - {stops && stops.length && activePattern && activePattern.shape && editSettings.editGeometry && controlPoints - ? controlPoints.map((cp, index) => { - // don't include controlPoint on end of segment (for now) or hidden controlPoints - console.log(cp) - if (cp.stopId && editSettings.snapToStops) { - return null - } - let prevControlPoint = controlPoints[index - 1] - let nextControlPoint = controlPoints[index + 1] - - let begin = prevControlPoint - ? prevControlPoint.point - : along(activePattern.shape, 0, 'meters') - let end - if (nextControlPoint) { - end = nextControlPoint.point - } - let position = cp.point - const color = cp.permanent ? '#000' : '#888' - const iconType = cp.stopId ? 'fa-square' : 'fa-times' - if (!position || !position.geometry || !position.geometry.coordinates) { - return null - } - const timesIcon = divIcon({ - className: '', - // iconSize: [24, 24], - html: `` - }) - const pos = [position.geometry.coordinates[1], position.geometry.coordinates[0]] - console.log(pos) - return ( - { - const timerFunction = () => { - handlePatternEdit(`controlPoint-${index}`, begin, end, polyline, activePattern) - } - timerFunction() - timer = setInterval(timerFunction, 500) - }} - onDragEnd={(e) => { - handleControlPointDragEnd(e, timer, `controlPoint-${index}`, index) - }} - onClick={(e) => { - console.log('control point clicked', e) - // only remove controlPoint if it's not based on pattern stop (i.e., has permanent prop) - if (!cp.permanent) { - removeControlPoint(activePattern, index, begin, end, polyline) - } - }} - color='black' - /> - ) - }) - : null - } - {beginPoint && editSettings.editGeometry && activePattern - ? { this.controlPoint = Marker }} - draggable - onDragStart={(e) => { - let beginStop = stops.find(s => s.id === activePattern.patternStops[0].stopId) - let begin = point([beginStop.stop_lon, beginStop.stop_lat]) - const timerFunction = () => { - const coords = handlePatternEdit('controlPointBegin', null, begin, polyline, activePattern) - onDrag(coords) + {stops && activePattern && activePattern.shape && editSettings.editGeometry && controlPoints + ? controlPoints.map((cp, index) => { + // don't include controlPoint on end of segment (for now) or hidden controlPoints + if (cp.stopId && editSettings.snapToStops) { + return null } - timerFunction() - timer = setInterval(timerFunction, 1000) - }} - onDragEnd={(e) => { - const coords = handleControlPointDragEnd(e, timer, this.controlPoint, null, polyline, activePattern) - onDrag(coords) - }} - color='black' - /> - : null + let position = cp.point + const color = cp.permanent ? '#000' : '#888' + const iconType = cp.stopId ? 'fa-square' : 'fa-times' + if (!position || !position.geometry || !position.geometry.coordinates) { + return null + } + const icon = divIcon({ + className: '', + html: `` + }) + return ( + + ) + }) + : null } ) diff --git a/src/main/client/editor/components/map/EditorMap.js b/src/main/client/editor/components/map/EditorMap.js index e8834c8ec..d02d01def 100644 --- a/src/main/client/editor/components/map/EditorMap.js +++ b/src/main/client/editor/components/map/EditorMap.js @@ -93,6 +93,17 @@ export default class EditorMap extends Component { break } } + // async addStopAtPoint (latlng, addToPattern = false, index) { + // // create stop + // const stop = await constructStop(latlng, this.props.feedSource.id) + // const s = await this.props.newGtfsEntity(this.props.feedSource.id, 'stop', stop, true) + // const gtfsStop = stopToGtfs(s) + // // add stop to end of pattern + // if (addToPattern) { + // await this.props.addStopToPattern(this.props.activePattern, gtfsStop, index) + // } + // return gtfsStop + // } _mapBaseLayerChanged = (e) => { const layer = MAP_LAYERS.find(l => l.name === e.name) console.log('base layer changed', e) @@ -117,7 +128,7 @@ export default class EditorMap extends Component { case 'NO_ACTION': break case 'ADD_STOP_AT_CLICK': - return this.props.addStopAtPoint(e.latlng, true, this.props.activePattern) + return this.props.addStopAtPoint(e.latlng, true, null, this.props.activePattern) case 'ADD_STOPS_AT_INTERSECTIONS': return this.props.addStopAtIntersection(e.latlng, this.props.activePattern) case 'ADD_STOPS_AT_INTERVAL': @@ -142,11 +153,6 @@ export default class EditorMap extends Component { } } } - onControlPointDrag (newCoordinates) { - // update active pattern leafletElement - // let leafletPattern = this.refs[this.props.activePattern.id].leafletElement - // leafletPattern.setLatLngs(leafletCoords) - } getMapComponents (component, entity, subEntityId, activePattern, stops, editSettings, mapState) { switch (component) { case 'route': @@ -157,6 +163,7 @@ export default class EditorMap extends Component { route={entity} subEntityId={subEntityId} activePattern={activePattern} + patternCoordinates={this.props.editSettings.patternCoordinates} activeEntity={entity} editSettings={editSettings} controlPoints={this.props.controlPoints} @@ -169,13 +176,24 @@ export default class EditorMap extends Component { activePattern={activePattern} editSettings={editSettings} handlePatternEdit={this.props.handlePatternEdit} + updateControlPoint={this.props.updateControlPoint} + removeControlPoint={this.props.removeControlPoint} + updateActiveEntity={this.props.updateActiveEntity} handleControlPointDragEnd={this.props.handleControlPointDragEnd} - onDrag={this.onControlPointDrag} + updatePatternCoordinates={this.props.updatePatternCoordinates} controlPoints={this.props.controlPoints} polyline={activePattern && this.refs[activePattern.id]} /> + component: } ] const activeMapLayerIndex = MAP_LAYERS.findIndex(l => l.id === getUserMetadataProperty(user.profile, 'editor.map_id')) return ( {MAP_LAYERS.map((layer, index) => ( - - + ))} + {isExtensionEnabled('nysdot') && + + + + } {OVERLAYS.map((overlay, i) => ( - {overlay.component} - + ))} ) diff --git a/src/main/client/editor/components/map/PatternStopPopup.js b/src/main/client/editor/components/map/PatternStopPopup.js index d643496a7..cfbff60ef 100644 --- a/src/main/client/editor/components/map/PatternStopPopup.js +++ b/src/main/client/editor/components/map/PatternStopPopup.js @@ -6,7 +6,7 @@ import MinuteSecondInput from '../MinuteSecondInput' export default class PatternStopPopup extends Component { render () { - const { stop, index, patternStop, activePattern } = this.props + const { stop, index, patternStop, activePattern, entityEdited, saveActiveEntity, setActiveEntity, feedSource, removeStopFromPattern, addStopToPattern, updateActiveEntity, controlPoints } = this.props return (
    }
    From e37fc9b1a0120a44bf5a87861635490f16156c1c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:02:41 -0500 Subject: [PATCH 179/323] added Fare.toGtfs for snapshot export of fares --- .../datatools/editor/models/transit/Fare.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/Fare.java b/src/main/java/com/conveyal/datatools/editor/models/transit/Fare.java index 6b768138e..2356dfae9 100644 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/Fare.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/Fare.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.editor.models.transit; import com.conveyal.datatools.editor.models.Model; +import com.conveyal.gtfs.model.FareAttribute; import com.conveyal.gtfs.model.FareRule; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.common.collect.Lists; @@ -24,7 +25,7 @@ public class Fare extends Model implements Cloneable, Serializable { public Integer paymentMethod; public Integer transfers; public Integer transferDuration; - public List fareRules = Lists.newArrayList(); + public List fareRules = Lists.newArrayList(); public Fare() {}; @@ -62,4 +63,19 @@ public Fare clone () throws CloneNotSupportedException { f.fareRules.addAll(fareRules); return f; } + + public com.conveyal.gtfs.model.Fare toGtfs() { + com.conveyal.gtfs.model.Fare fare = new com.conveyal.gtfs.model.Fare(this.gtfsFareId); + fare.fare_attribute = new com.conveyal.gtfs.model.FareAttribute(); + fare.fare_attribute.fare_id = this.gtfsFareId; + fare.fare_attribute.price = this.price == null ? Double.NaN : this.price; + fare.fare_attribute.currency_type = this.currencyType; + fare.fare_attribute.payment_method = this.paymentMethod == null ? Integer.MIN_VALUE : this.paymentMethod; + fare.fare_attribute.transfers = this.transfers == null ? Integer.MIN_VALUE : this.transfers; + fare.fare_attribute.transfer_duration = this.transferDuration == null ? Integer.MIN_VALUE : this.transferDuration; + fare.fare_attribute.feed_id = this.feedId; + + fare.fare_rules.addAll(this.fareRules); + return fare; + } } From b7cd98c4dcc25d4cbdff79f95ffd9d130f096f17 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:03:54 -0500 Subject: [PATCH 180/323] fix s3 missing object errors on metadata props and fixed name of tempFile --- .../datatools/manager/persistence/FeedStore.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java index 1fddedf1f..786466d52 100644 --- a/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java +++ b/src/main/java/com/conveyal/datatools/manager/persistence/FeedStore.java @@ -98,7 +98,7 @@ public List getAllFeeds () { public Long getFeedLastModified (String id) { // s3 storage if (DataManager.useS3){ - return s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getLastModified().getTime(); + return s3Client.doesObjectExist(s3Bucket, getS3Key(id)) ? s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getLastModified().getTime() : null; } else { File feed = getFeed(id); @@ -121,7 +121,7 @@ public void deleteFeed (String id) { public Long getFeedSize (String id) { // s3 storage if (DataManager.useS3) { - return s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getContentLength(); + return s3Client.doesObjectExist(s3Bucket, getS3Key(id)) ? s3Client.getObjectMetadata(s3Bucket, getS3Key(id)).getContentLength() : null; } else { File feed = getFeed(id); @@ -160,7 +160,7 @@ public File getFeed (String id) { new GetObjectRequest(s3Bucket, getS3Key(id))); InputStream objectData = object.getObjectContent(); - return createTempFile(objectData); + return createTempFile(id, objectData); } catch (AmazonServiceException ase) { LOG.error("Error downloading from s3"); ase.printStackTrace(); @@ -225,8 +225,8 @@ private File writeFileUsingInputStream(String filename, InputStream inputStream) } } - private File createTempFile (InputStream in) throws IOException { - final File tempFile = File.createTempFile("test", ".zip"); + private File createTempFile (String name, InputStream in) throws IOException { + final File tempFile = new File(new File(System.getProperty("java.io.tmpdir")), name); tempFile.deleteOnExit(); try (FileOutputStream out = new FileOutputStream(tempFile)) { IOUtils.copy(in, out); @@ -241,7 +241,7 @@ private File uploadToS3 (InputStream inputStream, String id, FeedSource feedSour try { // Use tempfile LOG.info("Creating temp file for {}", id); - File tempFile = createTempFile(inputStream); + File tempFile = createTempFile(id, inputStream); LOG.info("Uploading feed {} to S3 from tempfile", id); TransferManager tm = new TransferManager(getAWSCreds()); From 6f9e5e3c221f630c16c73beb76c8617fdaf59769 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:05:23 -0500 Subject: [PATCH 181/323] snapshot direct GTFS download --- .../controllers/api/SnapshotController.java | 57 ++++++++++++++----- .../manager/models/FeedDownloadToken.java | 22 ++++++- .../manager/utils/json/JsonManager.java | 2 +- 3 files changed, 64 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java index 51175abcc..8bb3a24bc 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java @@ -10,6 +10,8 @@ import com.conveyal.datatools.editor.models.transit.Stop; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0UserProfile; +import com.conveyal.datatools.manager.controllers.api.FeedVersionController; +import com.conveyal.datatools.manager.models.FeedDownloadToken; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; @@ -213,6 +215,7 @@ public static Object restoreSnapshot (Request req, Response res) { public static Object exportSnapshot (Request req, Response res) { String id = req.params("id"); Tuple2 decodedId; + FeedDownloadToken token; try { decodedId = JacksonSerializers.Tuple2IntDeserializer.deserialize(id); } catch (IOException e1) { @@ -229,27 +232,16 @@ public static Object exportSnapshot (Request req, Response res) { } local = gtx.snapshots.get(decodedId); - - File out = new File(DataManager.config.get("application.publicDataDirectory").asText(), "gtfs_" + ".zip"); - - new ProcessGtfsSnapshotExport(local, out).run(); - -// redirect(Play.configuration.getProperty("application.appBase") + "/public/data/" + out.getName()); + token = new FeedDownloadToken(local); + token.save(); } finally { gtx.rollbackIfOpen(); } - return null; + return token; } /** Write snapshot to disk as GTFS */ - public static boolean writeSnapshotAsGtfs (String id, File outFile) { - Tuple2 decodedId; - try { - decodedId = JacksonSerializers.Tuple2IntDeserializer.deserialize(id); - } catch (IOException e1) { - return false; - } - + public static boolean writeSnapshotAsGtfs (Tuple2 decodedId, File outFile) { GlobalTx gtx = VersionedDataStore.getGlobalTx(); Snapshot local; try { @@ -266,6 +258,15 @@ public static boolean writeSnapshotAsGtfs (String id, File outFile) { return true; } + public static boolean writeSnapshotAsGtfs (String id, File outFile) { + Tuple2 decodedId; + try { + decodedId = JacksonSerializers.Tuple2IntDeserializer.deserialize(id); + } catch (IOException e1) { + return false; + } + return writeSnapshotAsGtfs(decodedId, outFile); + } public static Object deleteSnapshot(Request req, Response res) { String id = req.params("id"); @@ -285,6 +286,29 @@ public static Object deleteSnapshot(Request req, Response res) { return true; } + private static Object downloadSnapshotWithToken (Request req, Response res) { + String id = req.params("token"); + FeedDownloadToken token = FeedDownloadToken.get(id); + + if(token == null || !token.isValid()) { + halt(400, "Feed download token not valid"); + } + + Snapshot snapshot = token.getSnapshot(); + + token.delete(); + File file = null; + + try { + file = File.createTempFile("snapshot", ".zip"); + writeSnapshotAsGtfs(snapshot.id, file); + } catch (Exception e) { + e.printStackTrace(); + String message = "Unable to create temp file for snapshot"; + LOG.error(message); + } + return FeedVersionController.downloadFile(file, res); + } public static void register (String apiPrefix) { get(apiPrefix + "secure/snapshot/:id", SnapshotController::getSnapshot, json::write); options(apiPrefix + "secure/snapshot", (q, s) -> ""); @@ -293,6 +317,9 @@ public static void register (String apiPrefix) { post(apiPrefix + "secure/snapshot/import", SnapshotController::importSnapshot, json::write); put(apiPrefix + "secure/snapshot/:id", SnapshotController::updateSnapshot, json::write); post(apiPrefix + "secure/snapshot/:id/restore", SnapshotController::restoreSnapshot, json::write); + get(apiPrefix + "secure/snapshot/:id/downloadtoken", SnapshotController::exportSnapshot, json::write); delete(apiPrefix + "secure/snapshot/:id", SnapshotController::deleteSnapshot, json::write); + + get(apiPrefix + "downloadsnapshot/:token", SnapshotController::downloadSnapshotWithToken); } } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedDownloadToken.java b/src/main/java/com/conveyal/datatools/manager/models/FeedDownloadToken.java index 1643d28ad..ed96312bf 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedDownloadToken.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedDownloadToken.java @@ -1,6 +1,9 @@ package com.conveyal.datatools.manager.models; +import com.conveyal.datatools.editor.models.Snapshot; import com.conveyal.datatools.manager.persistence.DataStore; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.mapdb.Fun; import java.util.Date; @@ -11,9 +14,11 @@ */ public class FeedDownloadToken extends Model { + private static final long serialVersionUID = 1L; private static DataStore tokenStore = new DataStore("feeddownloadtokens"); private String feedVersionId; + private Fun.Tuple2 snapshotId; private Date timestamp; @@ -23,6 +28,12 @@ public FeedDownloadToken (FeedVersion feedVersion) { timestamp = new Date(); } + public FeedDownloadToken (Snapshot snapshot) { + super(); + snapshotId = snapshot.id; + timestamp = new Date(); + } + public FeedDownloadToken (Project project) { super(); feedVersionId = project.id; @@ -33,10 +44,19 @@ public static FeedDownloadToken get (String id) { return tokenStore.getById(id); } + @JsonIgnore public FeedVersion getFeedVersion () { - return FeedVersion.get(feedVersionId); + if (feedVersionId != null) return FeedVersion.get(feedVersionId); + else return null; + } + + @JsonIgnore + public Snapshot getSnapshot () { + if (snapshotId != null) return Snapshot.get(snapshotId); + else return null; } + @JsonIgnore public Project getProject () { return Project.get(feedVersionId); } diff --git a/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java b/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java index 69b5e3c68..6ca38c47a 100644 --- a/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java +++ b/src/main/java/com/conveyal/datatools/manager/utils/json/JsonManager.java @@ -20,7 +20,6 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; /** * Helper methods for writing REST API routines @@ -52,6 +51,7 @@ public JsonManager (Class theClass, Class view) { deser.addDeserializer(Rectangle2D.class, new Rectangle2DDeserializer()); om.registerModule(deser); + om.getSerializerProvider().setNullKeySerializer(new JacksonSerializers.MyDtoNullKeySerializer()); // om.registerModule(new JavaTimeModule()); SimpleFilterProvider filters = new SimpleFilterProvider(); filters.addFilter("bbox", SimpleBeanPropertyFilter.filterOutAllExcept("west", "east", "south", "north")); From d167eea5ce79090d89bcba08f004e3130b497c78 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:06:48 -0500 Subject: [PATCH 182/323] clientside snapshot download --- src/main/client/editor/actions/snapshots.js | 41 +++++++++++++++++++ .../containers/ActiveEditorFeedSourcePanel.js | 3 +- src/main/client/manager/actions/versions.js | 31 ++++++++++---- 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/src/main/client/editor/actions/snapshots.js b/src/main/client/editor/actions/snapshots.js index 7c04619e4..f178c9f58 100644 --- a/src/main/client/editor/actions/snapshots.js +++ b/src/main/client/editor/actions/snapshots.js @@ -61,6 +61,47 @@ export function restoreSnapshot (feedSource, snapshot) { } } +export function downloadingSnapshot (feedSource, snapshot) { + return { + type: 'DOWNLOADING_SNAPSHOT', + feedSource, + snapshot + } +} + +export function downloadedSnapshot (name) { + return { + type: 'DOWNLOADED_SNAPSHOT', + name + } +} + +// Download a GTFS file for a FeedVersion +export function downloadFeedViaToken (feedVersion, isPublic) { + return function (dispatch, getState) { + const route = isPublic ? 'public' : 'secure' + const url = `/api/manager/${route}/feedversion/${feedVersion.id}/downloadtoken` + secureFetch(url, getState()) + .then(response => response.json()) + .then(result => { + window.location.assign(`/api/manager/downloadfeed/${result.id}`) + }) + } +} + +export function downloadSnapshotViaToken (feedSource, snapshot) { + return function (dispatch, getState) { + dispatch(downloadingSnapshot(feedSource, snapshot)) + const url = `/api/manager/secure/snapshot/${snapshot.id}/downloadtoken` + secureFetch(url, getState()) + .then((response) => response.json()) + .then((result) => { + console.log(result) + window.location.assign(`/api/manager/downloadsnapshot/${result.id}`) + }) + } +} + export function creatingSnapshot () { return { type: 'CREATING_SNAPSHOT' diff --git a/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js b/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js index c697b30c6..45cc81ecf 100644 --- a/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js +++ b/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux' -import { fetchSnapshots, restoreSnapshot, deleteSnapshot, loadFeedVersionForEditing } from '../actions/snapshots.js' +import { fetchSnapshots, restoreSnapshot, deleteSnapshot, loadFeedVersionForEditing, downloadSnapshotViaToken } from '../actions/snapshots.js' import { createFeedVersionFromSnapshot } from '../../manager/actions/versions' import EditorFeedSourcePanel from '../components/EditorFeedSourcePanel' @@ -14,6 +14,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { getSnapshots: (feedSource) => { dispatch(fetchSnapshots(feedSource)) }, restoreSnapshot: (feedSource, snapshot) => { dispatch(restoreSnapshot(feedSource, snapshot)) }, deleteSnapshot: (feedSource, snapshot) => { dispatch(deleteSnapshot(feedSource, snapshot)) }, + downloadSnapshot: (feedSource, snapshot) => { dispatch(downloadSnapshotViaToken(feedSource, snapshot)) }, exportSnapshotAsVersion: (feedSource, snapshot) => { dispatch(createFeedVersionFromSnapshot(feedSource, snapshot.id)) }, loadFeedVersionForEditing: (feedVersion) => { dispatch(loadFeedVersionForEditing(feedVersion)) diff --git a/src/main/client/manager/actions/versions.js b/src/main/client/manager/actions/versions.js index cd0f58ba0..cf6a3b73c 100644 --- a/src/main/client/manager/actions/versions.js +++ b/src/main/client/manager/actions/versions.js @@ -170,8 +170,8 @@ export function deleteFeedVersion (feedVersion, changes) { const url = '/api/manager/secure/feedversion/' + feedVersion.id return secureFetch(url, getState(), 'delete') .then((res) => { - // fetch feed source with versions - return dispatch(fetchFeedSource(feedVersion.feedSource.id, true)) + // fetch feed source with versions + snapshots + return dispatch(fetchFeedSource(feedVersion.feedSource.id, true, true)) }) } } @@ -201,18 +201,33 @@ export function fetchValidationResult (feedVersion, isPublic) { } } -export function requestingFeedVersionIsochrones () { +export function requestingFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) { return { - type: 'REQUESTING_FEEDVERSION_ISOCHRONES' + type: 'REQUESTING_FEEDVERSION_ISOCHRONES', + feedVersion, + fromLat, + fromLon, + toLat, + toLon, + date, + fromTime, + toTime } } -export function receiveFeedVersionIsochrones (feedSource, feedVersion, isochrones) { +export function receiveFeedVersionIsochrones (feedSource, feedVersion, isochrones, fromLat, fromLon, toLat, toLon, date, fromTime, toTime) { return { type: 'RECEIVE_FEEDVERSION_ISOCHRONES', feedSource, feedVersion, - isochrones + isochrones, + fromLat, + fromLon, + toLat, + toLon, + date, + fromTime, + toTime } } @@ -223,7 +238,7 @@ export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat fromTime = getState().gtfs.filter.dateTimeFilter.from toTime = getState().gtfs.filter.dateTimeFilter.to } - dispatch(requestingFeedVersionIsochrones()) + dispatch(requestingFeedVersionIsochrones(feedVersion, fromLat, fromLon, toLat, toLon, date, fromTime, toTime)) const params = {fromLat, fromLon, toLat, toLon, date, fromTime, toTime} const url = `/api/manager/secure/feedversion/${feedVersion.id}/isochrones?${qs.stringify(params)}` return secureFetch(url, getState()) @@ -238,7 +253,7 @@ export function fetchFeedVersionIsochrones (feedVersion, fromLat, fromLon, toLat }) .then(isochrones => { console.log('received isochrones ', isochrones) - dispatch(receiveFeedVersionIsochrones(feedVersion.feedSource, feedVersion, isochrones)) + dispatch(receiveFeedVersionIsochrones(feedVersion.feedSource, feedVersion, isochrones, fromLat, fromLon, toLat, toLon, date, fromTime, toTime)) return isochrones }) } From 85771e55abcb006140a4883d81be2217a89bc979 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:09:50 -0500 Subject: [PATCH 183/323] controller tweaks --- .../datatools/common/utils/SparkUtils.java | 7 ++++ .../controllers/api/SnapshotController.java | 3 +- .../controllers/api/FeedSourceController.java | 11 ++++++ .../api/FeedVersionController.java | 34 +++---------------- .../controllers/api/GtfsPlusController.java | 3 +- .../controllers/api/StatusController.java | 4 +-- 6 files changed, 27 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java diff --git a/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java new file mode 100644 index 000000000..ac318d3c3 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java @@ -0,0 +1,7 @@ +package com.conveyal.datatools.common.utils; + +/** + * Created by landon on 12/15/16. + */ +public class SparkUtils { +} diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java index 8bb3a24bc..8ae3143d5 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java @@ -29,6 +29,7 @@ import spark.Request; import spark.Response; +import static com.conveyal.datatools.common.utils.SparkUtils.downloadFile; import static spark.Spark.*; @@ -307,7 +308,7 @@ private static Object downloadSnapshotWithToken (Request req, Response res) { String message = "Unable to create temp file for snapshot"; LOG.error(message); } - return FeedVersionController.downloadFile(file, res); + return downloadFile(file, res); } public static void register (String apiPrefix) { get(apiPrefix + "secure/snapshot/:id", SnapshotController::getSnapshot, json::write); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index 9ea902849..37862cd58 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -1,10 +1,12 @@ package com.conveyal.datatools.manager.controllers.api; +import com.amazonaws.services.s3.model.CannedAccessControlList; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.FetchSingleFeedJob; import com.conveyal.datatools.manager.jobs.NotifyUsersForSubscriptionJob; import com.conveyal.datatools.manager.models.*; +import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.json.JsonManager; import com.conveyal.datatools.manager.utils.json.JsonUtil; import com.fasterxml.jackson.core.JsonProcessingException; @@ -174,6 +176,15 @@ public static void applyJsonToFeedSource(FeedSource source, String json) throws if(entry.getKey().equals("isPublic")) { source.isPublic = entry.getValue().asBoolean(); + // TODO: set AWS GTFS zips to publix/private after "isPublic" change + if (DataManager.useS3) { +// if (source.isPublic) { +// FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, keyName, CannedAccessControlList.PublicRead); +// } +// else { +// FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, keyName, CannedAccessControlList.AuthenticatedRead); +// } + } } if(entry.getKey().equals("deployable")) { diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index c6ffdc285..36a18392c 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -45,6 +45,7 @@ import javax.servlet.ServletException; import javax.servlet.http.Part; +import static com.conveyal.datatools.common.utils.SparkUtils.downloadFile; import static com.conveyal.datatools.manager.controllers.api.FeedSourceController.requestFeedSource; import static spark.Spark.*; @@ -313,33 +314,6 @@ private static AnalystClusterRequest buildProfileRequest(Request req) { return clusterRequest; } - private static Object downloadFeedVersion(FeedVersion version, Response res) { - if(version == null) halt(500, "FeedVersion is null"); - - File file = version.getGtfsFile(); - - res.raw().setContentType("application/octet-stream"); - res.raw().setHeader("Content-Disposition", "attachment; filename=" + file.getName()); - - try { - BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(res.raw().getOutputStream()); - BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); - - byte[] buffer = new byte[1024]; - int len; - while ((len = bufferedInputStream.read(buffer)) > 0) { - bufferedOutputStream.write(buffer, 0, len); - } - - bufferedOutputStream.flush(); - bufferedOutputStream.close(); - } catch (Exception e) { - halt(500, "Error serving GTFS file"); - } - - return res.raw(); - } - public static Boolean renameFeedVersion (Request req, Response res) throws JsonProcessingException { FeedVersion v = requestFeedVersion(req, "manage"); @@ -355,10 +329,10 @@ public static Boolean renameFeedVersion (Request req, Response res) throws JsonP private static Object downloadFeedVersionDirectly(Request req, Response res) { FeedVersion version = requestFeedVersion(req, "view"); - return downloadFeedVersion(version, res); + return downloadFile(version.getGtfsFile(), res); } - private static FeedDownloadToken getDownloadToken (Request req, Response res) { + public static FeedDownloadToken getDownloadToken (Request req, Response res) { FeedVersion version = requestFeedVersion(req, "view"); FeedDownloadToken token = new FeedDownloadToken(version); token.save(); @@ -404,7 +378,7 @@ private static Object downloadFeedVersionWithToken (Request req, Response res) { token.delete(); - return downloadFeedVersion(version, res); + return downloadFile(version.getGtfsFile(), res); } public static void register (String apiPrefix) { diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsPlusController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsPlusController.java index e48729038..d42b8d2a8 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsPlusController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/GtfsPlusController.java @@ -259,8 +259,7 @@ private static Collection getGtfsPlusValidation(Request req, Re // load the main GTFS -// GTFSFeed gtfsFeed = GTFSFeed.fromFile(feedVersion.getGtfsFile().getAbsolutePath()); - GTFSFeed gtfsFeed = DataManager.gtfsCache.get(feedVersion.id); + GTFSFeed gtfsFeed = feedVersion.getGtfsFeed(); // check for saved GTFS+ data File file = gtfsPlusStore.getFeed(feedVersionId); if(file == null) { diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/StatusController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/StatusController.java index 67608521d..83b897e1e 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/StatusController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/StatusController.java @@ -33,11 +33,11 @@ public class StatusController { return DataManager.userJobsMap.get(userId); }*/ - public static Object getUserJobs(Request req, Response res) { + public static Set getUserJobs(Request req, Response res) { Auth0UserProfile userProfile = req.attribute("user"); String userId = userProfile.getUser_id(); return DataManager.userJobsMap.containsKey(userId) - ? DataManager.userJobsMap.get(userId).toArray() + ? DataManager.userJobsMap.get(userId) : new HashSet<>(); } From 8fb6b7c39f4c95fe49390556fff86655547a51a3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:10:17 -0500 Subject: [PATCH 184/323] file download method --- .../datatools/common/utils/SparkUtils.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java index ac318d3c3..38ac5552d 100644 --- a/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java +++ b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java @@ -1,7 +1,41 @@ package com.conveyal.datatools.common.utils; +import spark.Response; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; + +import static spark.Spark.halt; + /** * Created by landon on 12/15/16. */ public class SparkUtils { + + public static Object downloadFile(File file, Response res) { + if(file == null) halt(404, "File is null"); + + res.raw().setContentType("application/octet-stream"); + res.raw().setHeader("Content-Disposition", "attachment; filename=" + file.getName()); + + try { + BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(res.raw().getOutputStream()); + BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); + + byte[] buffer = new byte[1024]; + int len; + while ((len = bufferedInputStream.read(buffer)) > 0) { + bufferedOutputStream.write(buffer, 0, len); + } + + bufferedOutputStream.flush(); + bufferedOutputStream.close(); + } catch (Exception e) { + halt(500, "Error serving GTFS file"); + } + + return res.raw(); + } } From bbd7ef4cba3f564df6821d6ee453ec2897062b31 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:10:56 -0500 Subject: [PATCH 185/323] export fares, suppress URL warnings for null values --- .../editor/jobs/ProcessGtfsSnapshotExport.java | 10 ++++++++++ .../editor/jobs/ProcessGtfsSnapshotMerge.java | 4 +--- .../datatools/editor/models/Snapshot.java | 6 +++++- .../datatools/editor/models/transit/Agency.java | 6 +++--- .../datatools/editor/models/transit/Route.java | 4 ++-- .../datatools/editor/models/transit/Stop.java | 2 +- .../editor/utils/JacksonSerializers.java | 17 +++++++++++++++++ 7 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java index 4e4522a99..5da7b5ae9 100755 --- a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java +++ b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotExport.java @@ -89,6 +89,16 @@ public void run() { feed.agency.put(agency.agencyId, agency.toGtfs()); } + Collection fares = feedTx.fares.values(); + + for (Fare fare : fares) { + com.conveyal.gtfs.model.Fare gtfsFare = fare.toGtfs(); + LOG.info("Exporting fare {}", gtfsFare); + + // write the feeds.txt entry + feed.fares.put(fare.gtfsFareId, gtfsFare); + } + // write all of the calendars and calendar dates if (feedTx.calendars != null) { for (ServiceCalendar cal : feedTx.calendars.values()) { diff --git a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java index 4b0e7bf77..a93bce047 100755 --- a/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java +++ b/src/main/java/com/conveyal/datatools/editor/jobs/ProcessGtfsSnapshotMerge.java @@ -9,14 +9,12 @@ import com.conveyal.datatools.editor.models.transit.Stop; import com.conveyal.datatools.editor.models.transit.StopTime; import com.conveyal.datatools.editor.models.transit.Trip; -import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.gtfs.GTFSFeed; import com.conveyal.gtfs.model.*; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; -import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; @@ -424,7 +422,7 @@ public Agency apply(Tuple2 input) { LOG.info("GtfsImporter: importing fares..."); Map fares = input.fares; for (com.conveyal.gtfs.model.Fare f : fares.values()) { - com.conveyal.datatools.editor.models.transit.Fare fare = new com.conveyal.datatools.editor.models.transit.Fare(f.fare_attribute, f.fare_rules, feed); + Fare fare = new Fare(f.fare_attribute, f.fare_rules, feed); feedTx.fares.put(fare.id, fare); fareCount++; } diff --git a/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java b/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java index 58da5011c..2a54a06b8 100644 --- a/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java +++ b/src/main/java/com/conveyal/datatools/editor/models/Snapshot.java @@ -118,9 +118,13 @@ public static Snapshot get(String snapshotId) { } GlobalTx gtx = VersionedDataStore.getGlobalTx(); - if (!gtx.snapshots.containsKey(decodedId)) return null; + return gtx.snapshots.get(decodedId); + } + public static Snapshot get(Tuple2 decodedId) { + GlobalTx gtx = VersionedDataStore.getGlobalTx(); + if (!gtx.snapshots.containsKey(decodedId)) return null; return gtx.snapshots.get(decodedId); } } diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/Agency.java b/src/main/java/com/conveyal/datatools/editor/models/transit/Agency.java index 0b94153cf..d019e3c27 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/Agency.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/Agency.java @@ -43,19 +43,19 @@ public com.conveyal.gtfs.model.Agency toGtfs() { ret.agency_id = agencyId; ret.agency_name = name; try { - ret.agency_url = new URL(url); + ret.agency_url = ret.agency_url == null ? null : new URL(url); } catch (MalformedURLException e) { LOG.warn("Unable to coerce agency URL {} to URL", url); ret.agency_url = null; } try { - ret.agency_branding_url = new URL(agencyBrandingUrl); + ret.agency_branding_url = ret.agency_branding_url == null ? null : new URL(agencyBrandingUrl); } catch (MalformedURLException e) { LOG.warn("Unable to coerce agency branding URL {} to URL", agencyBrandingUrl); ret.agency_branding_url = null; } try { - ret.agency_fare_url = new URL(agencyFareUrl); + ret.agency_fare_url = ret.agency_fare_url == null ? null : new URL(agencyFareUrl); } catch (MalformedURLException e) { LOG.warn("Unable to coerce agency fare URL {} to URL", agencyFareUrl); ret.agency_fare_url = null; diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/Route.java b/src/main/java/com/conveyal/datatools/editor/models/transit/Route.java index 625617f95..03c837f4e 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/Route.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/Route.java @@ -133,13 +133,13 @@ public com.conveyal.gtfs.model.Route toGtfs(com.conveyal.gtfs.model.Agency a, Gl // TODO also handle HVT types here //ret.route_type = mapGtfsRouteType(routeTypeId); try { - ret.route_url = new URL(routeUrl); + ret.route_url = ret.route_url == null ? null : new URL(routeUrl); } catch (MalformedURLException e) { LOG.warn("Cannot coerce route URL {} to URL", routeUrl); ret.route_url = null; } try { - ret.route_branding_url = new URL(routeBrandingUrl); + ret.route_branding_url = ret.route_branding_url == null ? null : new URL(routeBrandingUrl); } catch (MalformedURLException e) { LOG.warn("Unable to coerce route branding URL {} to URL", routeBrandingUrl); ret.route_branding_url = null; diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/Stop.java b/src/main/java/com/conveyal/datatools/editor/models/transit/Stop.java index 88a436bee..70057bdaf 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/Stop.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/Stop.java @@ -123,7 +123,7 @@ public com.conveyal.gtfs.model.Stop toGtfs() { ret.stop_name = id.toString(); try { - ret.stop_url = new URL(stopUrl); + ret.stop_url = ret.stop_url == null ? null : new URL(stopUrl); } catch (MalformedURLException e) { LOG.warn("Unable to coerce stop URL {} to URL", stopUrl); ret.stop_url = null; diff --git a/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java b/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java index 03c11ae38..7ec16020b 100644 --- a/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java +++ b/src/main/java/com/conveyal/datatools/editor/utils/JacksonSerializers.java @@ -11,6 +11,7 @@ import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer; import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; import com.google.common.io.BaseEncoding; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; @@ -176,6 +177,22 @@ public GtfsRouteType deserialize(JsonParser jp, } } + public static class MyDtoNullKeySerializer extends StdSerializer { + public MyDtoNullKeySerializer() { + this(null); + } + + public MyDtoNullKeySerializer(Class t) { + super(t); + } + + @Override + public void serialize(Object nullKey, JsonGenerator jsonGenerator, SerializerProvider unused) + throws IOException, JsonProcessingException { + jsonGenerator.writeFieldName(""); + } + } + /** serialize GtfsRouteType as GTFS integer value */ public static class TripDirectionSerializer extends StdScalarSerializer { public TripDirectionSerializer() { From 04050fd02fac5b49412764653d24b4916ea2a3bf Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:11:36 -0500 Subject: [PATCH 186/323] add option for cli-specified config files --- .../datatools/manager/DataManager.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/DataManager.java b/src/main/java/com/conveyal/datatools/manager/DataManager.java index 5ca57dbba..ed51ebac8 100644 --- a/src/main/java/com/conveyal/datatools/manager/DataManager.java +++ b/src/main/java/com/conveyal/datatools/manager/DataManager.java @@ -258,15 +258,20 @@ private static void registerExternalResources() { } } private static void loadConfig (String[] args) throws IOException { - FileInputStream in; + FileInputStream configStream; + FileInputStream serverConfigStream; - if (args.length == 0) - in = new FileInputStream(new File("config.yml")); - else - in = new FileInputStream(new File(args[0])); + if (args.length == 0) { + configStream = new FileInputStream(new File("config.yml")); + serverConfigStream = new FileInputStream(new File("config_server.yml")); + } + else { + configStream = new FileInputStream(new File(args[0])); + serverConfigStream = new FileInputStream(new File(args[1])); + } - config = yamlMapper.readTree(in); - serverConfig = yamlMapper.readTree(new File("config_server.yml")); + config = yamlMapper.readTree(configStream); + serverConfig = yamlMapper.readTree(serverConfigStream); } private static void registerExternalResource(ExternalFeedResource resource) { feedResources.put(resource.getResourceType(), resource); From 378435ce90491e0aeb7458d1b1927b59e8d9fee6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 16:12:26 -0500 Subject: [PATCH 187/323] add isochrone download --- package.json | 2 + .../manager/components/FeedVersionReport.js | 35 ++- yarn.lock | 266 ++++-------------- 3 files changed, 80 insertions(+), 223 deletions(-) diff --git a/package.json b/package.json index da923b40f..6dd0d8d89 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "react-dnd-html5-backend": "^2.1.2", "react-dom": "^15.4.1", "react-dropzone": "^3.5.3", + "react-file-download": "^0.3.2", "react-helmet": "^3.1.0", "react-leaflet": "^0.12.1", "react-pure-render": "^1.0.2", @@ -73,6 +74,7 @@ "redux-thunk": "^2.0.1", "request": "^2.67.0", "selectn": "^1.0.20", + "shp-write": "^0.3.2", "shpjs": "^3.3.2", "truncate": "^2.0.0", "turf-along": "^3.0.10", diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index f8fef2c90..12132dcd0 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -4,13 +4,14 @@ import moment from 'moment' import {Icon} from '@conveyal/woonerf' import numeral from 'numeral' import Rcslider from 'rc-slider' +import fileDownload from 'react-file-download' + import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' import { getComponentMessages, getMessage, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' import { getProfileLink } from '../../common/util/util' - -// import Feed from './reporter/containers/Feed' +// import { downloadAsShapefile } from '../util' import Patterns from './reporter/containers/Patterns' import Routes from './reporter/containers/Routes' import Stops from './reporter/containers/Stops' @@ -58,14 +59,26 @@ export default class FeedVersionReport extends Component { } renderIsochroneMessage (version) { if (version.isochrones && version.isochrones.features) { - return 'Move marker or change date/time to recalculate travel shed.' + return + Move marker or change date/time to recalculate travel shed.
    + +
    } else if (version.isochrones) { return 'Reading transport network, please try again later.' } else { return 'Click on map above to show travel shed for this feed.' } } - renderValidatorTabs () { + renderValidatorTabs (version) { if (!isModuleEnabled('validator')) { return null } @@ -74,10 +87,10 @@ export default class FeedVersionReport extends Component { title: 'Validation', key: 'validation', component: { this.props.fetchValidationResult(this.props.version) }} + fetchValidationResult={() => { this.props.fetchValidationResult(version) }} /> }, { @@ -88,7 +101,7 @@ export default class FeedVersionReport extends Component { {/* isochrone message */}

    - {this.renderIsochroneMessage(this.props.version)} + {this.renderIsochroneMessage(version)}

    @@ -114,7 +127,7 @@ export default class FeedVersionReport extends Component { }, @@ -128,8 +141,8 @@ export default class FeedVersionReport extends Component { { this.props.fetchValidationResult(this.props.version) }} + validationResult={version.validationResult} + fetchValidationResult={() => { this.props.fetchValidationResult(version) }} /> } @@ -291,7 +304,7 @@ export default class FeedVersionReport extends Component { tableOptions={tableOptions} /> - {this.renderValidatorTabs()} + {this.renderValidatorTabs(version)} diff --git a/yarn.lock b/yarn.lock index a8e88c8ee..7624b44d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,13 +40,6 @@ abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" -accepts@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" - dependencies: - mime-types "~2.1.11" - negotiator "0.6.1" - acorn-globals@^1.0.4: version "1.0.9" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" @@ -193,10 +186,6 @@ 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" -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" @@ -281,7 +270,7 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@1.x, async@^1.4.0, async@^1.4.2, async@^1.5.0: +async@1.x, async@^1.4.0, async@^1.4.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -1117,17 +1106,6 @@ base64-js@^1.0.2: version "1.2.0" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1" -base64-url@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/base64-url/-/base64-url-1.3.3.tgz#f8b6c537f09a4fc58c99cb86e0b0e9c61461a20f" - -base64url@~1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-1.0.6.tgz#d64d375d68a7c640d912e2358d170dca5bb54681" - dependencies: - concat-stream "~1.4.7" - meow "~2.0.0" - bcrypt-pbkdf@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.0.tgz#3ca76b85241c7170bf7d9703e7b9aa74630040d4" @@ -1426,10 +1404,6 @@ budo@^9.0.0: watchify-middleware "^1.6.0" xtend "^4.0.0" -buffer-equal-constant-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" @@ -1495,13 +1469,6 @@ camel-case@^3.0.0: no-case "^2.2.0" upper-case "^1.1.1" -camelcase-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-1.0.0.tgz#bd1a11bf9b31a1ce493493a930de1a0baf4ad7ec" - dependencies: - camelcase "^1.0.1" - map-obj "^1.0.0" - camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -1509,7 +1476,7 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase@^1.0.1, camelcase@^1.0.2: +camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" @@ -1821,7 +1788,7 @@ concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@ readable-stream "~2.0.0" typedarray "~0.0.5" -concat-stream@~1.4.5, concat-stream@~1.4.7: +concat-stream@~1.4.5: version "1.4.10" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.4.10.tgz#acc3bbf5602cb8cc980c6ac840fa7d8603e3ef36" dependencies: @@ -1854,10 +1821,6 @@ constants-browserify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" -content-disposition@0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.1.tgz#87476c6a67c8daa87e32e87616df883ba7fb071b" - content-type-parser@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" @@ -1878,14 +1841,6 @@ 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" -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - core-js@^1.0.0, core-js@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -2038,6 +1993,12 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" +dbf@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/dbf/-/dbf-0.1.4.tgz#162f0d8fd1db37c976458ac6fe6f55b48c72f466" + dependencies: + jdataview "~2.5.0" + debounce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.0.0.tgz#0948af513d2e4ce407916f8506a423d3f9cf72d8" @@ -2245,12 +2206,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -ecdsa-sig-formatter@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.7.tgz#3137e976a1d6232517e2513e04e32f79bcbdf126" - dependencies: - base64-url "^1.2.1" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2295,7 +2250,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "~1.3.0" -envify@^3.4.1: +envify@^3.0.0, envify@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" dependencies: @@ -2627,50 +2582,6 @@ expand-tilde@^1.2.0, expand-tilde@^1.2.1, expand-tilde@^1.2.2: dependencies: os-homedir "^1.0.1" -express-jwt@^3.3.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-3.4.0.tgz#e6fe8730e61da81a8deab64680154ae46c0028cc" - dependencies: - async "^1.5.0" - express-unless "^0.3.0" - jsonwebtoken "^5.0.0" - lodash.set "^4.0.0" - -express-unless@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.0.tgz#5c795e7392571512dd28f520b3857a52b21261a2" - -express@^4.13.3: - version "4.14.0" - resolved "https://registry.yarnpkg.com/express/-/express-4.14.0.tgz#c1ee3f42cdc891fb3dc650a8922d51ec847d0d66" - dependencies: - accepts "~1.3.3" - array-flatten "1.1.1" - content-disposition "0.5.1" - content-type "~1.0.2" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "~2.2.0" - depd "~1.1.0" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.7.0" - finalhandler "0.5.0" - fresh "0.3.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.1" - path-to-regexp "0.1.7" - proxy-addr "~1.1.2" - qs "6.2.0" - range-parser "~1.2.0" - send "0.14.1" - serve-static "~1.11.1" - type-is "~1.6.13" - utils-merge "1.0.0" - vary "~1.1.0" - extend@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/extend/-/extend-1.3.0.tgz#d1516fb0ff5624d2ebf9123ea1dac5a1994004f8" @@ -2744,6 +2655,16 @@ fbjs@^0.3.1: ua-parser-js "^0.7.9" whatwg-fetch "^0.9.0" +fbjs@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.6.1.tgz#9636b7705f5ba9684d44b72f78321254afc860f7" + dependencies: + core-js "^1.0.0" + loose-envify "^1.0.0" + promise "^7.0.3" + ua-parser-js "^0.7.9" + whatwg-fetch "^0.9.0" + fbjs@^0.8.1, fbjs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.5.tgz#f69ba8a876096cb1b9bffe4d7c1e71c19d39d008" @@ -2791,16 +2712,6 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -finalhandler@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-0.5.0.tgz#e9508abece9b6dba871a6942a1d7911b91911ac7" - dependencies: - debug "~2.2.0" - escape-html "~1.0.3" - on-finished "~2.3.0" - statuses "~1.3.0" - unpipe "~1.0.0" - find-node-modules@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-1.0.3.tgz#36117ea45c13d5d8352f82ba791c2b835d730a14" @@ -2893,10 +2804,6 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" -forwarded@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363" - fresh@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" @@ -3293,14 +3200,6 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" -indent-string@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-1.2.2.tgz#db99bcc583eb6abbb1e48dcbb1999a986041cb6b" - dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - repeating "^1.1.0" - indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -3416,10 +3315,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -ipaddr.js@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.1.1.tgz#c791d95f52b29c1247d5df80ada39b8a73647230" - is-absolute@^0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" @@ -3715,6 +3610,10 @@ jasmine-check@^0.1.4: dependencies: testcheck "^0.1.0" +jdataview@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/jdataview/-/jdataview-2.5.0.tgz#3081b3fea651f9317ec6bd4feb2ddc98aa41d595" + jest-changed-files@^16.0.0: version "16.0.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-16.0.0.tgz#7931deff4424182b8173d80e06800d7363b19c45" @@ -4010,14 +3909,6 @@ jsonpointer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" -jsonwebtoken@^5.0.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz#1c90f9a86ce5b748f5f979c12b70402b4afcddb4" - dependencies: - jws "^3.0.0" - ms "^0.7.1" - xtend "^4.0.1" - jspm-github@^0.14.11: version "0.14.11" resolved "https://registry.yarnpkg.com/jspm-github/-/jspm-github-0.14.11.tgz#5093b3a79289d63ff6e3982f3b527878ac808d5c" @@ -4110,11 +4001,11 @@ jsx-ast-utils@^1.3.1: acorn-jsx "^3.0.1" object-assign "^4.1.0" -jszip@^2.4.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.6.1.tgz#b88f3a7b2e67a2a048152982c7a3756d9c4828f0" +jszip@2.5.0, jszip@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/jszip/-/jszip-2.5.0.tgz#7444fd8551ddf3e5da7198fea0c91bc8308cc274" dependencies: - pako "~1.0.2" + pako "~0.2.5" jszip@^3.0.0: version "3.1.3" @@ -4126,21 +4017,6 @@ jszip@^3.0.0: pako "~1.0.2" readable-stream "~2.0.6" -jwa@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.3.tgz#fa9f2f005ff0c630e7c41526a31f37f79733cd6d" - dependencies: - base64url "~1.0.4" - buffer-equal-constant-time "^1.0.1" - ecdsa-sig-formatter "^1.0.0" - -jws@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.3.tgz#b88f1b4581a2c5ee8813c06b3fdf90ea9b5c7e6c" - dependencies: - base64url "~1.0.4" - jwa "^1.1.2" - jwt-decode@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-2.1.0.tgz#d3079cef1689d82d56bbb7aedcfea28b12f0e36a" @@ -4365,10 +4241,6 @@ lodash.pickby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" -lodash.set@^4.0.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - lodash.template@^4.2.4: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" @@ -4547,27 +4419,10 @@ meow@^3.3.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-2.0.0.tgz#8f530a8ecf5d40d3f4b4df93c3472900fba2a8f1" - dependencies: - camelcase-keys "^1.0.0" - indent-string "^1.1.0" - minimist "^1.1.0" - object-assign "^1.0.0" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - merge@^1.1.3, merge@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - mgrs@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/mgrs/-/mgrs-0.0.3.tgz#3058d38ae92e1abfbf74b32a8f6cb5225a6eaa05" @@ -4686,7 +4541,7 @@ ms@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.0.tgz#865be94c2e7397ad8a57da6a633a6e2f30798b83" -ms@0.7.1, ms@^0.7.1: +ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -4723,10 +4578,6 @@ ncp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - netrc@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" @@ -4853,10 +4704,6 @@ object-assign@4.x, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4. version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" -object-assign@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-1.0.0.tgz#e65dc8766d3b47b4b8307465c8311da030b070a6" - object-assign@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-2.1.1.tgz#43c36e5d569ff8e4816c4efa8be02d26967c18aa" @@ -4987,7 +4834,7 @@ pad-right@^0.2.2: dependencies: repeat-string "^1.5.2" -pako@~0.2.0: +pako@~0.2.0, pako@~0.2.5: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" @@ -5117,10 +4964,6 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.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" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -5501,13 +5344,6 @@ proper-lockfile@^1.1.2: graceful-fs "^4.1.2" retry "^0.10.0" -proxy-addr@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.2.tgz#b4cc5f22610d9535824c123aef9d3cf73c40ba37" - dependencies: - forwarded "~0.1.0" - ipaddr.js "1.1.1" - prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" @@ -5689,11 +5525,7 @@ rc@~1.1.6: version "15.3.2" resolved "https://registry.yarnpkg.com/react-addons-css-transition-group/-/react-addons-css-transition-group-15.3.2.tgz#d8fa52bec9bb61bdfde8b9e4652b80297cbff667" -react-addons-perf@^15.3.2: - version "15.3.2" - resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.3.2.tgz#bbdbebe8649f936f9636a5750ac145bf5c620213" - -react-addons-perf@^15.4.1: +react-addons-perf@^15.3.2, react-addons-perf@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-perf/-/react-addons-perf-15.4.1.tgz#c6dd5a7011f43cd3222f47b7cb1aebe9d4174cb0" @@ -5775,6 +5607,12 @@ react-dropzone@^3.5.3: dependencies: attr-accept "^1.0.3" +react-file-download@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/react-file-download/-/react-file-download-0.3.2.tgz#369c66b20dcf05670c521c10a778237d5c86d735" + dependencies: + react "^0.14.7" + react-helmet@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/react-helmet/-/react-helmet-3.1.0.tgz#63486194682f33004826f3687dc49a138b557050" @@ -5885,6 +5723,13 @@ react-virtualized@^8.0.5, react-virtualized@^8.5.0: classnames "^2.2.3" dom-helpers "^2.4.0" +react@^0.14.7: + version "0.14.8" + resolved "https://registry.yarnpkg.com/react/-/react-0.14.8.tgz#078dfa454d4745bcc54a9726311c2bf272c23684" + dependencies: + envify "^3.0.0" + fbjs "^0.6.1" + "react@^15.0.0 || ^16.0.0", react@^15.3.2, react@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react/-/react-15.4.1.tgz#498e918602677a3983cd0fd206dfe700389a0dd6" @@ -6107,12 +5952,6 @@ repeat-string@^1.5.2, repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" -repeating@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" - dependencies: - is-finite "^1.0.0" - repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" @@ -6338,7 +6177,7 @@ sentence-case@^2.1.0: no-case "^2.2.0" upper-case-first "^1.1.2" -serve-static@^1.10.0, serve-static@~1.11.1: +serve-static@^1.10.0: version "1.11.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" dependencies: @@ -6411,6 +6250,13 @@ shellwords@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14" +shp-write@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/shp-write/-/shp-write-0.3.2.tgz#cac3e84bcfdc953c4fc273a1b2f2843d6db8f69d" + dependencies: + dbf "0.1.4" + jszip "2.5.0" + shpjs@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/shpjs/-/shpjs-3.3.2.tgz#f8ca857a4c70ee9526965a280d229a46775fb553" @@ -7139,7 +6985,7 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" -unpipe@1.0.0, unpipe@~1.0.0: +unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -7187,10 +7033,6 @@ util@0.10.3, util@~0.10.1: dependencies: inherits "2.0.1" -utils-merge@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" - uuid@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" @@ -7206,7 +7048,7 @@ validator@^5.5.0: version "5.7.0" resolved "https://registry.yarnpkg.com/validator/-/validator-5.7.0.tgz#7a87a58146b695ac486071141c0c49d67da05e5c" -vary@^1, vary@~1.1.0: +vary@^1: version "1.1.0" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140" From 8fc2641d3a7a0a5c435b750ee231dfeced9f371c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 17:07:28 -0500 Subject: [PATCH 188/323] refactor/reduce EntityList file size --- .../components/EditorFeedSourcePanel.js | 33 +- .../client/editor/components/EditorInput.js | 523 ++++++ .../client/editor/components/EntityDetails.js | 1401 +---------------- .../editor/components/EntityDetailsHeader.js | 179 +++ .../client/editor/components/FareRulesForm.js | 179 +++ .../components/ScheduleExceptionForm.js | 225 +++ .../editor/containers/ActiveGtfsEditor.js | 1 + src/main/client/editor/reducers/settings.js | 9 +- src/main/client/editor/util/index.js | 51 +- src/main/client/editor/util/validation.js | 99 ++ 10 files changed, 1367 insertions(+), 1333 deletions(-) create mode 100644 src/main/client/editor/components/EditorInput.js create mode 100644 src/main/client/editor/components/EntityDetailsHeader.js create mode 100644 src/main/client/editor/components/FareRulesForm.js create mode 100644 src/main/client/editor/components/ScheduleExceptionForm.js create mode 100644 src/main/client/editor/util/validation.js diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 300c019c3..207310aaa 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -106,7 +106,14 @@ class SnapshotItem extends Component { feedSource: PropTypes.object } render () { - const { snapshot, feedSource } = this.props + const { + snapshot, + feedSource, + restoreSnapshot, + downloadSnapshot, + exportSnapshotAsVersion, + deleteSnapshot + } = this.props const dateFormat = getConfigProperty('application.date_format') const timeFormat = 'h:MMa' const messages = getComponentMessages('EditorFeedSourcePanel') @@ -117,26 +124,26 @@ class SnapshotItem extends Component { - { - // - } - + - diff --git a/src/main/client/editor/components/EditorInput.js b/src/main/client/editor/components/EditorInput.js new file mode 100644 index 000000000..094e4565c --- /dev/null +++ b/src/main/client/editor/components/EditorInput.js @@ -0,0 +1,523 @@ +import React, {Component, PropTypes} from 'react' +import { Checkbox, FormControl, FormGroup, ControlLabel, Tooltip, OverlayTrigger } from 'react-bootstrap' +import Select from 'react-select' +import reactCSS from 'reactcss' +import moment from 'moment' +import DateTimeField from 'react-bootstrap-datetimepicker' +import { sentence as toSentenceCase } from 'change-case' + +import { getEntityName } from '../util/gtfs' +import VirtualizedEntitySelect from './VirtualizedEntitySelect' +import GtfsSearch from '../../gtfs/components/gtfssearch' +import TimezoneSelect from '../../common/components/TimezoneSelect' +import LanguageSelect from '../../common/components/LanguageSelect' +import { SketchPicker } from 'react-color' +import Dropzone from 'react-dropzone' + +export default class EditorInput extends Component { + static propTypes = { + entities: PropTypes.array + } + constructor (props) { + super(props) + this.state = { + color: { + r: '241', + g: '112', + b: '19', + a: '1' + } + } + } + handleClick (field) { + this.setState({ [field]: !this.state[field] }) + } + handleClose (field) { + this.setState({ [field]: !this.state[field] }) + } + render () { + const { + activeEntity, + updateActiveEntity, + activeComponent, + uploadBrandingAsset, + feedSource, + getGtfsEntity, + fieldEdited, + gtfsEntitySelected, + tableData, + row, + field, + currentValue, + approveGtfsDisabled, + zoneOptions, + table, + isNotValid + } = this.props + const formProps = { + controlId: `${editorField}`, + className: `col-xs-${field.columnWidth}`, + validationState: isNotValid + ? 'error' + : field.required + ? 'success' + : '' + } + const simpleFormProps = { + controlId: `${editorField}`, + className: `col-xs-${field.columnWidth}` + } + const styles = reactCSS({ + 'default': { + swatch: { + padding: '5px', + marginRight: '30px', + background: '#fff', + zIndex: 1, + borderRadius: '1px', + boxShadow: '0 0 0 1px rgba(0,0,0,.1)', + display: 'inline-block', + cursor: 'pointer' + }, + popover: { + position: 'absolute', + zIndex: '200' + }, + cover: { + position: 'fixed', + top: '0', + right: '0', + bottom: '0', + left: '0' + } + } + }) + const editorField = field.displayName || field.name + const basicLabel = field.helpContent + ? {field.helpContent}}> + {editorField}{field.required ? ' *' : ''} + + : {editorField}{field.required ? ' *' : ''} + switch (field.inputType) { + case 'GTFS_ID': + return ( + + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + updateActiveEntity(activeEntity, activeComponent, props) + }} + /> + + ) + case 'TEXT': + case 'GTFS_TRIP': + case 'GTFS_SHAPE': + case 'GTFS_BLOCK': + case 'GTFS_FARE': + case 'GTFS_SERVICE': + return ( + + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + updateActiveEntity(activeEntity, activeComponent, props) + }} + /> + + ) + case 'URL': + let elements = [ + + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + updateActiveEntity(activeEntity, activeComponent, props) + }} + /> + + ] + if (field.name === 'agency_branding_url' || field.name === 'route_branding_url') { + elements.push( + + Upload {activeComponent} branding asset + { + uploadBrandingAsset(feedSource.id, activeEntity.id, activeComponent, files[0]) + this.setState({file: files[0]}) + }}> +
    Drop {activeComponent} image here, or click to select image to upload.
    + {activeEntity && activeEntity[field.name] + ? + : null + } +
    +
    + ) + } + return {elements} + case 'EMAIL': + return ( + + {basicLabel} + { + let props = {} + props[field.name] = evt.target.value + updateActiveEntity(activeEntity, activeComponent, props) + }} + /> + + ) + case 'GTFS_ZONE': + return ( + + {basicLabel} + { + console.log(input) + let props = {} + let val = input ? input.value : null + props[field.name] = val + this.setState({[editorField]: val}) + updateActiveEntity(activeEntity, activeComponent, props) + }} + options={tableData.agency + ? tableData.agency.map(agency => { + return { + value: agency.id, + label: agency.agency_name, + agency + } + }) + : [] + } /> + + ) + case 'GTFS_STOP': + const stopIndex = tableData.stop && tableData.stop.findIndex(s => s.id === activeEntity.id) + const stop = tableData.stop.find(s => s.id === currentValue) + let stops = [...tableData.stop] + // remove current entity from list of stops + if (stopIndex !== -1) { + stops.splice(stopIndex, 1) + } + return ( + + {basicLabel} + { + console.log(input) + let props = {} + let val = input ? input.value : null + props[field.name] = val + this.setState({[editorField]: val}) + updateActiveEntity(activeEntity, activeComponent, props) + }} /> + + ) + default: + return null + } + } +} diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index 2537353a9..c220ee467 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -1,25 +1,15 @@ import React, {Component, PropTypes} from 'react' -import { Checkbox, Radio, Button, ButtonToolbar, Form, FormControl, FormGroup, ControlLabel, Nav, NavItem, Tooltip, OverlayTrigger, Panel } from 'react-bootstrap' +import { Form } from 'react-bootstrap' import {Icon} from '@conveyal/woonerf' -import reactCSS from 'reactcss' -import validator from 'validator' import { shallowEqual } from 'react-pure-render' -import Select from 'react-select' -import { sentence as toSentenceCase } from 'change-case' -import DateTimeField from 'react-bootstrap-datetimepicker' -import moment from 'moment' -import { SketchPicker } from 'react-color' -import Dropzone from 'react-dropzone' -import update from 'react-addons-update' -import GtfsSearch from '../../gtfs/components/gtfssearch' -import TimezoneSelect from '../../common/components/TimezoneSelect' -import LanguageSelect from '../../common/components/LanguageSelect' import ActiveTripPatternList from '../containers/ActiveTripPatternList' -import VirtualizedEntitySelect from './VirtualizedEntitySelect' -import { getEntityName, getEntityBounds } from '../util/gtfs' -import { gtfsIcons } from '../util/ui' -import { getConfigProperty } from '../../common/util/config' +import EditorInput from './EditorInput' +import FareRulesForm from './FareRulesForm' +import EntityDetailsHeader from './EntityDetailsHeader' +import ScheduleExceptionForm from './ScheduleExceptionForm' +import { getZones, getEditorTable, canApproveGtfs } from '../util' +import { validate } from '../util/validation' export default class EntityDetails extends Component { @@ -28,12 +18,9 @@ export default class EntityDetails extends Component { project: PropTypes.object, entities: PropTypes.array, activeEntity: PropTypes.object, - mapState: PropTypes.object, - activeEntityId: PropTypes.string, width: PropTypes.number.isRequired, - setActiveEntity: PropTypes.func.isRequired, saveActiveEntity: PropTypes.func.isRequired, resetActiveEntity: PropTypes.func.isRequired, @@ -46,7 +33,6 @@ export default class EntityDetails extends Component { getGtfsEntity: PropTypes.func, showConfirmModal: PropTypes.func, updateMapSetting: PropTypes.func, - activeComponent: PropTypes.string.isRequired, subEntityId: PropTypes.string, user: PropTypes.object, @@ -55,26 +41,12 @@ export default class EntityDetails extends Component { entityEdited: PropTypes.bool, subComponent: PropTypes.string } - constructor (props) { super(props) - this.state = { - color: { - r: '241', - g: '112', - b: '19', - a: '1' - } - } - } - handleClick (field) { - this.setState({ [field]: !this.state[field] }) - } - handleClose (field) { - this.setState({ [field]: !this.state[field] }) + this.state = {} } shouldComponentUpdate (nextProps, nextState) { - return !shallowEqual(nextState, this.state) || // for color picker + return !shallowEqual(nextState, this.state) || // for editFareRules !shallowEqual(nextProps.feedSource, this.props.feedSource) || !shallowEqual(nextProps.subComponent, this.props.subComponent) || !shallowEqual(nextProps.subEntityId, this.props.subEntityId) || @@ -85,1299 +57,96 @@ export default class EntityDetails extends Component { !shallowEqual(nextProps.width, this.props.width) } render () { - const approveGtfsDisabled = this.props.project && this.props.feedSource && this.props.user && !this.props.user.permissions.hasFeedPermission(this.props.project.id, this.props.feedSource.id, 'approve-gtfs') - const styles = reactCSS({ - 'default': { - swatch: { - padding: '5px', - marginRight: '30px', - background: '#fff', - zIndex: 1, - borderRadius: '1px', - boxShadow: '0 0 0 1px rgba(0,0,0,.1)', - display: 'inline-block', - cursor: 'pointer' - }, - popover: { - position: 'absolute', - zIndex: '200' - }, - cover: { - position: 'fixed', - top: '0', - right: '0', - bottom: '0', - left: '0' - } - } - }) - let entity = this.props.activeEntity - - let panelWidth = `${this.props.width}px` - - let panelStyle = { - width: panelWidth, + const { + activeComponent, + project, + feedSource, + user, + activeEntity, + width, + offset, + tableData, + subComponent, + showConfirmModal, + entities + } = this.props + const approveGtfsDisabled = canApproveGtfs(project, feedSource, user) + const panelStyle = { + width: `${width}px`, height: '100%', position: 'absolute', - // overflowY: 'scroll', overflowX: 'visible', top: '0px', - left: this.props.offset || '0px', + left: offset || '0px', zIndex: 2, backgroundColor: '#F2F2F2', paddingRight: '5px', paddingLeft: '5px' } - - let zones = {} - if (this.props.tableData.stop) { - for (var i = 0; i < this.props.tableData.stop.length; i++) { - let stop = this.props.tableData.stop[i] - if (stop.zone_id) { - let zone = zones[stop.zone_id] - if (!zone) { - zone = [] - } - zone.push(stop) - zones[stop.zone_id] = zone - } - } - // add any new zone - if (entity && entity.zone_id && !zones[entity.zone_id]) { - let zone = zones[entity.zone_id] - if (!zone) { - zone = [] - } - zone.push(entity) - zones[entity.zone_id] = zone - } - } - let zoneOptions = Object.keys(zones).map(key => { - return { - value: key, - label: `${key} zone (${zones[key] ? zones[key].length : 0} stops)` - } - }) - const validationErrors = [] - const getInput = (row, field, currentValue, index) => { - const editorField = field.displayName || field.name // .split(/_(.+)?/)[1] - let isNotValid - // const standardLabel = {toSentenceCase(editorField.split(/_(.+)?/)[1])} ({editorField}) - const basicLabel = field.helpContent - ? {field.helpContent}}> - {editorField}{field.required ? ' *' : ''} - - : {editorField}{field.required ? ' *' : ''} - switch (field.inputType) { - case 'GTFS_ID': - isNotValid = field.required && !currentValue - let indices = [] - let idList = this.props.entities.map(e => e[field.name]) - let idx = idList.indexOf(currentValue) - while (idx !== -1) { - indices.push(idx) - idx = idList.indexOf(currentValue, idx + 1) - } - let isNotUnique = currentValue && (indices.length > 1 || indices.length && this.props.entities[indices[0]].id !== this.props.activeEntity.id) - if (isNotValid || isNotUnique) { - validationErrors.push({field: field.name, invalid: isNotValid || isNotUnique}) - } - return ( - - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'TEXT': - case 'GTFS_TRIP': - case 'GTFS_SHAPE': - case 'GTFS_BLOCK': - case 'GTFS_FARE': - case 'GTFS_SERVICE': - isNotValid = field.required && !currentValue - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'URL': - isNotValid = field.required && !currentValue || currentValue && !validator.isURL(currentValue) - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - let elements = [ - - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ] - if (field.name === 'agency_branding_url' || field.name === 'route_branding_url') { - elements.push( - - Upload {this.props.activeComponent} branding asset - { - this.props.uploadBrandingAsset(this.props.feedSource.id, this.props.activeEntity.id, this.props.activeComponent, files[0]) - this.setState({file: files[0]}) - }} - > -
    Drop {this.props.activeComponent} image here, or click to select image to upload.
    - {this.props.activeEntity && this.props.activeEntity[field.name] - ? - : null - } -
    -
    - ) - } - return elements - case 'EMAIL': - isNotValid = field.required && !currentValue || currentValue && !validator.isEmail(currentValue) - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - let props = {} - props[field.name] = evt.target.value - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - case 'GTFS_ZONE': - isNotValid = field.required && (currentValue === null || typeof currentValue === 'undefined') - if (isNotValid) { - validationErrors.push({field: field.name, invalid: isNotValid}) - } - return ( - - {basicLabel} - { - console.log(input) - let props = {} - let val = input ? input.value : null - props[field.name] = val - this.setState({[editorField]: val}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - options={this.props.tableData.agency - ? this.props.tableData.agency.map(agency => { - return { - value: agency.id, - label: agency.agency_name, - agency - } - }) - : [] - } - /> - - ) - case 'GTFS_STOP': - const stopIndex = this.props.tableData.stop && this.props.tableData.stop.findIndex(s => s.id === this.props.activeEntity.id) - const stop = this.props.tableData.stop.find(s => s.id === currentValue) - - let stops = [...this.props.tableData.stop] - - // remove current entity from list of stops - if (stopIndex !== -1) { - stops.splice(stopIndex, 1) - } - return ( - - {basicLabel} - { - console.log(input) - let props = {} - let val = input ? input.value : null - props[field.name] = val - this.setState({[editorField]: val}) - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, props) - }} - /> - - ) - } - } - const rowIndex = 0 - const table = getConfigProperty('modules.editor.spec').find( - t => this.props.activeComponent === 'scheduleexception' - ? t.id === 'calendar_dates' - : this.props.activeComponent === 'fare' - ? t.id === 'fare_attributes' - : t.id === this.props.activeComponent - ) - const exemplars = [ - 'MONDAY', - 'TUESDAY', - 'WEDNESDAY', - 'THURSDAY', - 'FRIDAY', - 'SATURDAY', - 'SUNDAY', - 'NO_SERVICE', - 'CUSTOM', - 'SWAP' - ] - - const fareRulesForm = this.state.editFareRules && this.props.activeEntity - ?
    -

    Specify which routes or zones {this.props.activeEntity.fare_id} fare applies to.

    - {this.props.activeEntity && this.props.activeEntity.fareRules.length} rules apply to this fare - - {this.props.activeEntity.fareRules.map((rule, index) => { - let ruleEntity - if (rule.route_id) { - ruleEntity = this.props.tableData.route && this.props.tableData.route.find(r => r.route_id === rule.route_id) - } - return ( - - - - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].route_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - Route - - {' '} - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].origin_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - To/From - - {' '} - { - let rules = [...this.props.activeEntity.fareRules] - rules[index] = {fare_id: this.props.activeEntity.fare_id} - rules[index].contains_id = true - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - > - Contains - - - {rule.route_id - ? { - console.log(input) - let rules = [...this.props.activeEntity.fareRules] - rules[index].route_id = input ? input.value : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - /> - : rule.contains_id - ? { - console.log(input) - let rules = [...this.props.activeEntity.fareRules] - rules[index].origin_id = input ? input.value : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {fareRules: rules}) - }} - options={zoneOptions} - />, - { - console.log(input) - let val = input ? input.map(i => i.value) : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {customSchedule: val}) - }} - options={this.props.tableData.calendar - ? this.props.tableData.calendar.map(calendar => { - return { - value: calendar.id, - label: calendar.description, - calendar - } - }) - : [] - } - /> - - : null - } - {this.props.activeEntity && this.props.activeEntity.exemplar === 'SWAP' - ? - Select calendars to add: - { - console.log(input) - let val = input ? input.map(i => i.value) : null - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {removedService: val}) - }} - options={this.props.tableData.calendar - ? this.props.tableData.calendar - .filter(cal => !this.props.activeEntity.addedService || this.props.activeEntity.addedService.indexOf(cal.id) === -1) - .map(calendar => { - return { - value: calendar.id, - label: calendar.description, - calendar - } - }) - : [] - } - /> - - : null - } - - On these dates: - {this.props.activeEntity && this.props.activeEntity.dates.length - ? this.props.activeEntity.dates.map((date, index) => { - let isNotValid = false - const dateString = moment(+date).format('YYYYMMDD') - // check if date already exists in this or other exceptions - if (dateMap[dateString] && dateMap[dateString].length > 1) { - validationErrors.push({field: `dates-${index}`, invalid: true}) - isNotValid = true - } - let dateTimeProps = { - mode: 'date', - dateTime: date ? +moment(date) : +moment(), - onChange: (millis) => { - let dates = [...this.props.activeEntity.dates] - dates[index] = +millis - this.props.updateActiveEntity(this.props.activeEntity, this.props.activeComponent, {dates}) - } - } - if (!date) { - dateTimeProps.defaultText = 'Please select a date' - } - return ( -
    - - - {isNotValid - ? {moment(+date).format('MM/DD/YYYY')} appears in another schedule exception. Please choose another date. - : null - } -
    - ) - } - ) - :
    No dates specified
    - } -
    -
    - -
    - - -
    - : null - const entityForm = this.props.activeComponent === 'scheduleexception' - ? null - : ( -
    -
    - {table.fields.map((field, colIndex) => { - // get editor field by splitting on first underscore - const editorField = field.name // .split(/_(.+)?/)[1] - // const validationIssue = this.props.validation - // ? this.props.validation.find(v => - // (v.rowIndex === data.origRowIndex && v.fieldName === field.name)) - // : null - - // const tooltip = validationIssue - // ? {validationIssue.description} - // : null - - return entity - ? getInput(rowIndex, field, entity[editorField], (rowIndex * table.fields.length) + colIndex + 1) - : null - }) - } -

    * = field is required

    -
    -
    - ) - let entityName = this.props.activeComponent === 'feedinfo' ? 'Feed Info' : getEntityName(this.props.activeComponent, entity) - let icon = gtfsIcons.find(i => i.id === this.props.activeComponent) - let iconName = icon ? icon.icon : null - // .icon || 'circle' - // console.log(validationErrors) - const subNav = this.props.activeComponent === 'route' - ? - : this.props.activeComponent === 'fare' - ? - : null - const header = ( -
    - - {this.props.activeComponent === 'stop' || this.props.activeComponent === 'route' - ? Zoom to {this.props.activeComponent}}> - - - : null - } - Undo changes}> - - - Save changes}> - - - - {this.props.activeComponent === 'route' && entity - ? - - - - : iconName - ? - - - - // schedule exception icon if no icon founds - : - - - - } - {' '} - - { - `${entityName && entityName.length > 18 ? entityName.substr(0, 18) + '...' : entityName}` - } - -
    - ) - - return ( -
    - {!entity - ?
    + if (!activeEntity) { + return ( +
    +

    + style={{marginTop: '150px'}}>

    - : [ -
    - {header} - {!this.props.tableData[this.props.activeComponent] && this.props.activeComponent === 'feedinfo' - ? Complete feed info to begin editing GTFS. - : null - } - {validationErrors.length > 0 - ? Fix validation issues before saving - : null - } - {subNav} -
    ,
    - {this.props.subComponent === 'trippattern' - ? - : this.state.editFareRules - ? fareRulesForm - : this.props.activeComponent === 'scheduleexception' - ? scheduleExceptionForm - : entityForm - } -
    - ] - } +
    + ) + } + const {zones, zoneOptions} = getZones(tableData.stop, activeEntity) + const rowIndex = 0 + const validationErrors = [] + const currentTable = getEditorTable(activeComponent) + const inputs = currentTable.fields.map((field, colIndex) => { + const isNotValid = validate(field.inputType, field.required, field.name, activeEntity[field.name], entities, activeEntity.id) + isNotValid && validationErrors.push(isNotValid) + return ( + + ) + }) + return ( +
    +
    + this.setState({editFareRules: bool})} + {...this.props} /> +
    + {/* Render relevant form based on entity type */} + {subComponent === 'trippattern' + ? + : this.state.editFareRules && activeEntity + ? + : activeComponent === 'scheduleexception' + ? + :
    +
    + {inputs} +

    * = field is required

    +
    +
    + } +
    +
    ) } diff --git a/src/main/client/editor/components/EntityDetailsHeader.js b/src/main/client/editor/components/EntityDetailsHeader.js new file mode 100644 index 000000000..3d998636f --- /dev/null +++ b/src/main/client/editor/components/EntityDetailsHeader.js @@ -0,0 +1,179 @@ +import React, {Component, PropTypes} from 'react' +import update from 'react-addons-update' +import {Icon} from '@conveyal/woonerf' +import { Button, ButtonToolbar, Tooltip, OverlayTrigger, Nav, NavItem } from 'react-bootstrap' + +import { getEntityBounds, getEntityName } from '../util/gtfs' +import { gtfsIcons } from '../util/ui' + +export default class EntityDetailsHeader extends Component { + static propTypes = { + activeComponent: PropTypes.string + } + render () { + const { + activeComponent, + subComponent, + mapState, + subEntityId, + updateMapSetting, + entityEdited, + resetActiveEntity, + saveActiveEntity, + activeEntity, + validationErrors, + setActiveEntity, + feedSource, + tableData, + editFareRules, + toggleEditFareRules + } = this.props + const entityName = activeComponent === 'feedinfo' ? 'Feed Info' : getEntityName(activeComponent, activeEntity) + const icon = gtfsIcons.find(i => i.id === activeComponent) + const iconName = icon ? icon.icon : null + return ( +
    +
    + + {activeComponent === 'stop' || activeComponent === 'route' + ? Zoom to {activeComponent}}> + + + : null + } + Undo changes}> + + + Save changes}> + + + + {activeComponent === 'route' && activeEntity + ? + + + + : iconName + ? + + + + // schedule exception icon if no icon founds + : + + + + } + {' '} + + { + `${entityName && entityName.length > 18 ? entityName.substr(0, 18) + '...' : entityName}` + } + +
    + {!tableData[activeComponent] && activeComponent === 'feedinfo' + ? Complete feed info to begin editing GTFS. + : null + } + {validationErrors.length > 0 + ? Fix validation issues before saving + : null + } + {activeComponent === 'route' + ? + : activeComponent === 'fare' + ? + : null + } +
    + ) + } +} diff --git a/src/main/client/editor/components/FareRulesForm.js b/src/main/client/editor/components/FareRulesForm.js new file mode 100644 index 000000000..b7096e7bd --- /dev/null +++ b/src/main/client/editor/components/FareRulesForm.js @@ -0,0 +1,179 @@ +import React, {Component, PropTypes} from 'react' +import { Radio, Button, FormGroup, Panel } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' +import Select from 'react-select' + +import VirtualizedEntitySelect from './VirtualizedEntitySelect' +import { getEntityName } from '../util/gtfs' + +export default class FareRulesForm extends Component { + static propTypes = { + activeEntity: PropTypes.object, + updateActiveEntity: PropTypes.func, + activeComponent: PropTypes.string, + tableData: PropTypes.object, + zones: PropTypes.object, + zoneOptions: PropTypes.array + } + render () { + const { + activeEntity, + updateActiveEntity, + activeComponent, + tableData, + zones, + zoneOptions + } = this.props + return ( +
    +

    Specify which routes or zones {activeEntity.fare_id} fare applies to.

    + {activeEntity && activeEntity.fareRules.length} rules apply to this fare + + {activeEntity.fareRules.map((rule, index) => { + let ruleEntity + if (rule.route_id) { + ruleEntity = tableData.route && tableData.route.find(r => r.route_id === rule.route_id) + } + return ( + + + + { + let rules = [...activeEntity.fareRules] + rules[index] = {fare_id: activeEntity.fare_id} + rules[index].route_id = true + updateActiveEntity(activeEntity, activeComponent, {fareRules: rules}) + }} + > + Route + + {' '} + { + let rules = [...activeEntity.fareRules] + rules[index] = {fare_id: activeEntity.fare_id} + rules[index].origin_id = true + updateActiveEntity(activeEntity, activeComponent, {fareRules: rules}) + }} + > + To/From + + {' '} + { + let rules = [...activeEntity.fareRules] + rules[index] = {fare_id: activeEntity.fare_id} + rules[index].contains_id = true + updateActiveEntity(activeEntity, activeComponent, {fareRules: rules}) + }} + > + Contains + + + {rule.route_id + ? { + console.log(input) + let rules = [...activeEntity.fareRules] + rules[index].route_id = input ? input.value : null + updateActiveEntity(activeEntity, activeComponent, {fareRules: rules}) + }} + /> + : rule.contains_id // can be boolean (no zone specified) or string (specified zone_id) + ? { + console.log(input) + let rules = [...activeEntity.fareRules] + rules[index].origin_id = input ? input.value : null + updateActiveEntity(activeEntity, activeComponent, {fareRules: rules}) + }} + options={zoneOptions} + />, + { + console.log(input) + const val = input ? input.map(i => i.value) : null + updateActiveEntity(activeEntity, activeComponent, {customSchedule: val}) + }} + options={tableData.calendar + ? tableData.calendar.map(calendar => { + return { + value: calendar.id, + label: calendar.description, + calendar + } + }) + : [] + } + /> + + : null + } + {activeEntity && activeEntity.exemplar === 'SWAP' + ? + Select calendars to add: + { + console.log(input) + const val = input ? input.map(i => i.value) : null + updateActiveEntity(activeEntity, activeComponent, {removedService: val}) + }} + options={tableData.calendar + ? tableData.calendar + .filter(cal => !activeEntity.addedService || activeEntity.addedService.indexOf(cal.id) === -1) + .map(calendar => { + return { + value: calendar.id, + label: calendar.description, + calendar + } + }) + : [] + } /> + + : null + } + + On these dates: + {activeEntity && activeEntity.dates.length + ? activeEntity.dates.map((date, index) => { + let isNotValid = false + const dateString = moment(+date).format('YYYYMMDD') + // check if date already exists in this or other exceptions + if (dateMap[dateString] && dateMap[dateString].length > 1) { + validate({field: `dates-${index}`, invalid: true}) + isNotValid = true + } + const dateTimeProps = { + mode: 'date', + dateTime: date ? +moment(date) : +moment(), + onChange: (millis) => { + let dates = [...activeEntity.dates] + dates[index] = +millis + updateActiveEntity(activeEntity, activeComponent, {dates}) + } + } + if (!date) { + dateTimeProps.defaultText = 'Please select a date' + } + return ( +
    + + + {isNotValid + ? {moment(+date).format('MM/DD/YYYY')} appears in another schedule exception. Please choose another date. + : null + } +
    + ) + } + ) + :
    No dates specified
    + } +
    +
    + +
    + +
    + ) + } +} diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index 13fe8ba88..c2bfb8549 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -196,6 +196,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { }, // NEW GENERIC GTFS/EDITOR FUNCTIONS + getGtfsTable: (activeComponent, feedSourceId) => dispatch(getGtfsTable(activeComponent, feedSourceId)), updateEditSetting: (setting, value) => { dispatch(updateEditSetting(setting, value)) }, diff --git a/src/main/client/editor/reducers/settings.js b/src/main/client/editor/reducers/settings.js index c96ec098a..caba5adec 100644 --- a/src/main/client/editor/reducers/settings.js +++ b/src/main/client/editor/reducers/settings.js @@ -72,7 +72,8 @@ const editSettings = (state = defaultState, action) => { } return update(state, { controlPoints: {$set: [controlPoints]}, - patternCoordinates: {$set: coordinates} + patternCoordinates: {$set: coordinates}, + coordinatesHistory: {$set: [coordinates]} }) case 'UPDATE_EDIT_SETTING': if (action.setting === 'editGeometry' && !state.editGeometry) { @@ -132,7 +133,8 @@ const editSettings = (state = defaultState, action) => { case 'trippattern': coordinates = state.coordinatesHistory[0] return update(state, { - patternCoordinates: {$set: coordinates} + patternCoordinates: {$set: coordinates}, + coordinatesHistory: {$set: [coordinates]} }) default: return state @@ -161,7 +163,8 @@ const editSettings = (state = defaultState, action) => { coordinates = action.tripPattern && action.tripPattern.shape && action.tripPattern.shape.coordinates return update(state, { controlPoints: {$set: [controlPoints]}, - patternCoordinates: {$set: coordinates} + patternCoordinates: {$set: coordinates}, + coordinatesHistory: {$set: [coordinates]} }) default: return state diff --git a/src/main/client/editor/util/index.js b/src/main/client/editor/util/index.js index b8cfd2cd1..92f098bf8 100644 --- a/src/main/client/editor/util/index.js +++ b/src/main/client/editor/util/index.js @@ -1,8 +1,10 @@ -import { getAbbreviatedStopName } from '../util/gtfs' +import { getAbbreviatedStopName } from './gtfs' +import { getConfigProperty } from '../../common/util/config' export const CLICK_OPTIONS = ['DRAG_HANDLES', 'ADD_STOP_AT_CLICK', 'ADD_STOPS_AT_INTERVAL', 'ADD_STOPS_AT_INTERSECTIONS'] export const YEAR_FORMAT = 'YYYY-MM-DD' export const TIMETABLE_FORMATS = ['HH:mm:ss', 'h:mm:ss a', 'h:mm:ssa', 'h:mm a', 'h:mma', 'h:mm', 'HHmm', 'hmm', 'HH:mm'].map(format => `YYYY-MM-DDT${format}`) +export const EXEMPLARS = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY', 'NO_SERVICE', 'CUSTOM', 'SWAP'] export function isTimeFormat (type) { return /TIME/.test(type) @@ -101,3 +103,50 @@ export function sortAndFilterTrips (trips, useFrequency) { }) : [] } + +export function getZones (stops, activeStop) { + let zones = {} + if (stops) { + for (var i = 0; i < stops.length; i++) { + let stop = stops[i] + if (stop.zone_id) { + let zone = zones[stop.zone_id] + if (!zone) { + zone = [] + } + zone.push(stop) + zones[stop.zone_id] = zone + } + } + // add any new zone + if (activeStop && activeStop.zone_id && !zones[activeStop.zone_id]) { + let zone = zones[activeStop.zone_id] + if (!zone) { + zone = [] + } + zone.push(activeStop) + zones[activeStop.zone_id] = zone + } + } + let zoneOptions = Object.keys(zones).map(key => { + return { + value: key, + label: `${key} zone (${zones[key] ? zones[key].length : 0} stops)` + } + }) + return {zones, zoneOptions} +} + +export function getEditorTable (component) { + return getConfigProperty('modules.editor.spec').find( + t => component === 'scheduleexception' + ? t.id === 'calendar_dates' + : component === 'fare' + ? t.id === 'fare_attributes' + : t.id === component + ) +} + +export function canApproveGtfs (project, feedSource, user) { + return project && feedSource && user && !user.permissions.hasFeedPermission(project.id, feedSource.id, 'approve-gtfs') +} diff --git a/src/main/client/editor/util/validation.js b/src/main/client/editor/util/validation.js new file mode 100644 index 000000000..01a3ee733 --- /dev/null +++ b/src/main/client/editor/util/validation.js @@ -0,0 +1,99 @@ +import validator from 'validator' + +export function validate (type, required, name, value, entities, id) { + let isNotValid + switch (type) { + case 'GTFS_ID': + isNotValid = required && !value + let indices = [] + let idList = entities.map(e => e[name]) + let idx = idList.indexOf(value) + while (idx !== -1) { + indices.push(idx) + idx = idList.indexOf(value, idx + 1) + } + let isNotUnique = value && (indices.length > 1 || indices.length && entities[indices[0]].id !== id) + if (isNotValid || isNotUnique) { + return {field: name, invalid: isNotValid || isNotUnique} + } else { + return false + } + case 'TEXT': + case 'GTFS_TRIP': + case 'GTFS_SHAPE': + case 'GTFS_BLOCK': + case 'GTFS_FARE': + case 'GTFS_SERVICE': + isNotValid = required && !value + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'URL': + isNotValid = required && !value || value && !validator.isURL(value) + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'EMAIL': + isNotValid = required && !value || value && !validator.isEmail(value) + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'GTFS_ZONE': + isNotValid = required && (value === null || typeof value === 'undefined') + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'TIMEZONE': + isNotValid = required && (value === null || typeof value === 'undefined') + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'LANGUAGE': + isNotValid = required && (value === null || typeof value === 'undefined') + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'TIME': + case 'LATITUDE': + case 'LONGITUDE': + isNotValid = required && (value === null || typeof value === 'undefined') + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'NUMBER': + isNotValid = required && (value === null || typeof value === 'undefined') + if (isNotValid) { + return {field: name, invalid: isNotValid} + } else { + return false + } + case 'DATE': + case 'COLOR': + case 'POSITIVE_INT': + case 'POSITIVE_NUM': + case 'DAY_OF_WEEK_BOOLEAN': + case 'DROPDOWN': + // isNotValid = required && (value === null || typeof value === 'undefined') + // if (isNotValid) { + // return {field: name, invalid: isNotValid} + // } + break + case 'GTFS_ROUTE': + case 'GTFS_AGENCY': + case 'GTFS_STOP': + } +} From adbdbd492cdb3c4d782a7437016c3540f7c9448b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 17:07:53 -0500 Subject: [PATCH 189/323] fix TransferPerformance --- src/main/client/gtfs/components/GtfsMap.js | 70 ++++++++++++------- src/main/client/gtfs/components/StopMarker.js | 14 ++-- .../gtfs/components/TransferPerformance.js | 21 +++--- 3 files changed, 63 insertions(+), 42 deletions(-) diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index 83452f21d..f23f1b8b6 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -160,18 +160,35 @@ export default class GtfsMap extends Component { } } render () { + const { + width, + height, + disableScroll, + showBounds, + stops, + routes, + feeds, + renderTransferPerformance, + onStopClick, + newEntityId, + popupAction, + stop, + patterns, + onRouteClick, + pattern, + showIsochrones + } = this.props var mapStyle = { - width: this.props.width, // % or px - height: `${this.props.height}px` // only px + width: width, // % or px + height: `${height}px` // only px } - let bounds = this.getBounds() return (
    this.mapClicked(e)} onMoveEnd={(e) => this.mapMoved(e)} onLayerAdd={(e) => this.layerAddHandler(e)} @@ -182,54 +199,55 @@ export default class GtfsMap extends Component { attribution='© Mapbox © OpenStreetMap Improve this map' /> {/* feed bounds */} - {this.props.showBounds && + {showBounds && } {/* Stops from map bounds search */} - {this.props.stops && this.props.stops.length - ? this.props.stops.map((stop, index) => { - if (!stop) return null + {stops && stops.length + ? stops.map((s, index) => { + if (!s) return null return ( + stop={s} + routes={routes} + key={`marker-${s.stop_id}`} + feeds={feeds} + renderTransferPerformance={renderTransferPerformance} + onStopClick={onStopClick} + newEntityId={newEntityId} + popupAction={popupAction} /> ) }) : null } {/* Stop from GtfsSearch */} - {this.props.stop && } + {stop && } {/* Patterns from map bounds search */} - {this.props.patterns - ? this.props.patterns.map((pattern, index) => ( + {patterns + ? patterns.map((pattern, index) => ( + onRouteClick={onRouteClick} + newEntityId={newEntityId} + popupAction={popupAction} /> )) : null } {/* Pattern from GtfsSearch */} - {this.props.pattern && } + {pattern && } {/* Isochrones from map click */} - {this.props.showIsochrones && this.renderIsochrones()} + {showIsochrones && this.renderIsochrones()}
    diff --git a/src/main/client/gtfs/components/StopMarker.js b/src/main/client/gtfs/components/StopMarker.js index 9975dff22..4e15c6880 100644 --- a/src/main/client/gtfs/components/StopMarker.js +++ b/src/main/client/gtfs/components/StopMarker.js @@ -12,7 +12,7 @@ export default class StopMarker extends Component { stop: PropTypes.object } render () { - const { stop, feeds, renderTransferPerformance, onStopClick, newEntityId, popupAction } = this.props + const { stop, feeds, renderTransferPerformance, onStopClick, newEntityId, popupAction, routes } = this.props if (!stop) { return null } @@ -34,17 +34,15 @@ export default class StopMarker extends Component {

    {stop.stop_name} ({stop.stop_id})

    - {renderTransferPerformance && } - {onStopClick - ? - : null - } + )}
    diff --git a/src/main/client/gtfs/components/TransferPerformance.js b/src/main/client/gtfs/components/TransferPerformance.js index 6efc32a9f..c1f2cbe54 100644 --- a/src/main/client/gtfs/components/TransferPerformance.js +++ b/src/main/client/gtfs/components/TransferPerformance.js @@ -3,6 +3,12 @@ import { ControlLabel, FormControl } from 'react-bootstrap' import moment from 'moment' export default class TransferPerformance extends Component { + constructor (props) { + super(props) + this.state = { + index: 0 + } + } renderTransferPerformanceResult (transferPerformance) { if (!transferPerformance) { return

    No transfers found

    @@ -16,7 +22,7 @@ export default class TransferPerformance extends Component { ) } render () { - const { stop } = this.props + const { stop, routes } = this.props return stop.transferPerformance && stop.transferPerformance.length ?
    Transfer performance @@ -24,9 +30,8 @@ export default class TransferPerformance extends Component { componentClass='select' defaultValue={0} onChange={(evt) => { - let state = {} - state[stop.stop_id] = +evt.target.value - this.setState(state) + const index = +evt.target.value + this.setState({index}) }} > {stop.transferPerformance @@ -34,13 +39,13 @@ export default class TransferPerformance extends Component { // // }) .map((summary, index) => { - const fromRoute = this.props.routes.find(r => r.route_id === summary.fromRoute) - const toRoute = this.props.routes.find(r => r.route_id === summary.toRoute) - return + const fromRoute = routes.find(r => r.route_id === summary.fromRoute) + const toRoute = routes.find(r => r.route_id === summary.toRoute) + return }) } - {this.renderTransferPerformanceResult(stop.transferPerformance[this.state[stop.stop_id] || 0])} + {this.renderTransferPerformanceResult(stop.transferPerformance[this.state.index])}
    :

    No transfers found

    } From d0605a32e84cbe7348e849de9cd70db4354c4e05 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 17:08:14 -0500 Subject: [PATCH 190/323] https for esri --- src/main/client/scenario-editor/utils/reverse.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/scenario-editor/utils/reverse.js b/src/main/client/scenario-editor/utils/reverse.js index 2985bec61..37256e421 100644 --- a/src/main/client/scenario-editor/utils/reverse.js +++ b/src/main/client/scenario-editor/utils/reverse.js @@ -21,7 +21,7 @@ export async function reverseEsri (point) { returnIntersection: true, f: 'pjson' } - const url = `http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode?${qs.stringify(params)}` + const url = `https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode?${qs.stringify(params)}` const response = await fetch(url) return await response.json() } From af6a75789531c39a3d11ef9cce32c4afd09e3214 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 17:08:40 -0500 Subject: [PATCH 191/323] fix isochrone action import --- src/main/client/gtfs/containers/ActiveGtfsMap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/gtfs/containers/ActiveGtfsMap.js b/src/main/client/gtfs/containers/ActiveGtfsMap.js index 3b7bcd697..5d498b1c2 100644 --- a/src/main/client/gtfs/containers/ActiveGtfsMap.js +++ b/src/main/client/gtfs/containers/ActiveGtfsMap.js @@ -4,7 +4,7 @@ import GtfsMap from '../components/GtfsMap' import { clearGtfsElements, refreshGtfsElements } from '../actions/general' import { stopPatternFilterChange, stopRouteFilterChange, stopDateTimeFilterChange } from '../actions/stops' import { updateMapState } from '../actions/filter' -import { fetchFeedVersionIsochrones } from '../../manager/actions/feeds' +import { fetchFeedVersionIsochrones } from '../../manager/actions/versions' const mapStateToProps = (state, ownProps) => { return { From 41e371375ed9927516a752f41572fd3dadd337f5 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 15 Dec 2016 17:09:05 -0500 Subject: [PATCH 192/323] isochrone misc chores --- src/main/client/manager/reducers/projects.js | 6 ++++++ src/main/client/manager/util/index.js | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index 0567ceac9..0f87d9307 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -249,6 +249,12 @@ const projects = (state = { projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) versionIndex = state.all[projectIndex].feedSources[sourceIndex].feedVersions.findIndex(v => v.id === action.feedVersion.id) + const { fromLat, fromLon, date, fromTime, toTime } = action + action.isochrones.properties = { fromLat, fromLon, date, fromTime, toTime } + action.isochrones.features = action.isochrones.features.map(f => { + f.type = 'Feature' + return f + }) return update(state, {all: {[projectIndex]: diff --git a/src/main/client/manager/util/index.js b/src/main/client/manager/util/index.js index 75bad7f16..b14326b4f 100644 --- a/src/main/client/manager/util/index.js +++ b/src/main/client/manager/util/index.js @@ -1,3 +1,5 @@ +import shpwrite from 'shp-write' + export function findProjectByFeedSource (state, feedSourceId) { return state.projects.all ? state.projects.all.find(p => { @@ -8,3 +10,17 @@ export function findProjectByFeedSource (state, feedSourceId) { }) : null } + +const DEFAULT_OPTIONS = { + folder: 'myshapes', + types: { + point: 'mypoints', + polygon: 'mypolygons', + line: 'mylines' + } +} + +// Currently does not work for MultiPolygon isochrones +export function downloadAsShapefile (geojson, options = DEFAULT_OPTIONS) { + shpwrite.download(geojson, options) +} From 09da5d357ae24b40e0e1c49f35245f3ad0048356 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 16 Dec 2016 10:05:41 -0500 Subject: [PATCH 193/323] added take snapshot of latest button --- .../components/EditorFeedSourcePanel.js | 37 ++++++++++++++----- .../containers/ActiveEditorFeedSourcePanel.js | 12 ++++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 207310aaa..3c8ce5d2e 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -4,6 +4,7 @@ import { browserHistory } from 'react-router' import moment from 'moment' import {Icon} from '@conveyal/woonerf' +import CreateSnapshotModal from '../../editor/components/CreateSnapshotModal' import ConfirmModal from '../../common/components/ConfirmModal' import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' @@ -26,20 +27,29 @@ export default class EditorFeedSourcePanel extends Component { this.state = { expanded: false } } render () { + const { + feedSource, + createSnapshot, + loadFeedVersionForEditing + } = this.props const messages = getComponentMessages('EditorFeedSourcePanel') - const hasVersions = this.props.feedSource && this.props.feedSource.feedVersions && this.props.feedSource.feedVersions.length > 0 - const currentSnapshot = this.props.feedSource.editorSnapshots && this.props.feedSource.editorSnapshots.length - ? this.props.feedSource.editorSnapshots.find(s => s.current) + const hasVersions = feedSource && feedSource.feedVersions && feedSource.feedVersions.length > 0 + const currentSnapshot = feedSource.editorSnapshots && feedSource.editorSnapshots.length + ? feedSource.editorSnapshots.find(s => s.current) : null - const inactiveSnapshots = this.props.feedSource.editorSnapshots - ? this.props.feedSource.editorSnapshots.filter(s => !s.current) + const inactiveSnapshots = feedSource.editorSnapshots + ? feedSource.editorSnapshots.filter(s => !s.current) : [] - console.log(inactiveSnapshots) return ( + { + createSnapshot(feedSource, name, comment) + }} + /> - {this.props.feedSource.editorSnapshots && this.props.feedSource.editorSnapshots.length + {feedSource.editorSnapshots && feedSource.editorSnapshots.length ?
    Active snapshot}> {currentSnapshot @@ -68,7 +78,7 @@ export default class EditorFeedSourcePanel extends Component {

    No snapshots loaded.

    @@ -76,11 +86,11 @@ export default class EditorFeedSourcePanel extends Component { What are snapshots?}>

    Snapshots are save points you can always revert back to when editing a GTFS feed.

    A snapshot might represent a work-in-progress, future planning scenario or even different service patterns (e.g., summer schedule markup).

    diff --git a/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js b/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js index 45cc81ecf..b20bfdb82 100644 --- a/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js +++ b/src/main/client/editor/containers/ActiveEditorFeedSourcePanel.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux' -import { fetchSnapshots, restoreSnapshot, deleteSnapshot, loadFeedVersionForEditing, downloadSnapshotViaToken } from '../actions/snapshots.js' +import { fetchSnapshots, restoreSnapshot, deleteSnapshot, loadFeedVersionForEditing, downloadSnapshotViaToken, createSnapshot } from '../actions/snapshots.js' import { createFeedVersionFromSnapshot } from '../../manager/actions/versions' import EditorFeedSourcePanel from '../components/EditorFeedSourcePanel' @@ -12,13 +12,17 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch, ownProps) => { return { getSnapshots: (feedSource) => { dispatch(fetchSnapshots(feedSource)) }, + createSnapshot: (feedSource, name, comments) => { + dispatch(createSnapshot(feedSource, name, comments)) + .then(() => { + dispatch(fetchSnapshots(feedSource)) + }) + }, restoreSnapshot: (feedSource, snapshot) => { dispatch(restoreSnapshot(feedSource, snapshot)) }, deleteSnapshot: (feedSource, snapshot) => { dispatch(deleteSnapshot(feedSource, snapshot)) }, downloadSnapshot: (feedSource, snapshot) => { dispatch(downloadSnapshotViaToken(feedSource, snapshot)) }, exportSnapshotAsVersion: (feedSource, snapshot) => { dispatch(createFeedVersionFromSnapshot(feedSource, snapshot.id)) }, - loadFeedVersionForEditing: (feedVersion) => { - dispatch(loadFeedVersionForEditing(feedVersion)) - } + loadFeedVersionForEditing: (feedVersion) => { dispatch(loadFeedVersionForEditing(feedVersion)) } } } From 788b3286f673296371d3e20f886d4aba62e460a7 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 16 Dec 2016 10:06:30 -0500 Subject: [PATCH 194/323] delete token after gtfs written (so it doesn't expire before user can download file) --- .../datatools/editor/controllers/api/SnapshotController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java index 8ae3143d5..81f880fc0 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/SnapshotController.java @@ -296,8 +296,6 @@ private static Object downloadSnapshotWithToken (Request req, Response res) { } Snapshot snapshot = token.getSnapshot(); - - token.delete(); File file = null; try { @@ -308,6 +306,7 @@ private static Object downloadSnapshotWithToken (Request req, Response res) { String message = "Unable to create temp file for snapshot"; LOG.error(message); } + token.delete(); return downloadFile(file, res); } public static void register (String apiPrefix) { From 7fd473c18699c91c866a44e890875750dcc0209a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 16 Dec 2016 10:09:57 -0500 Subject: [PATCH 195/323] disable make active button for active snapshot --- src/main/client/editor/components/EditorFeedSourcePanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index 3c8ce5d2e..3fda7df74 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -140,7 +140,7 @@ class SnapshotItem extends Component {
    :

    No snapshots loaded.

    - + + + {' '}or{' '} + + ? + + : null } Date: Mon, 19 Dec 2016 12:01:53 -0500 Subject: [PATCH 200/323] fix stopTree stop insertion --- src/main/client/editor/components/GtfsEditor.js | 2 +- src/main/client/editor/components/map/StopsLayer.js | 1 - src/main/client/editor/reducers/mapState.js | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 10509473c..10de8c6e9 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -221,7 +221,7 @@ export default class GtfsEditor extends Component { hidden={subSubComponent === 'timetable'} stops={tableData.stop || []} showConfirmModal={(props) => this.showConfirmModal(props)} - drawStops={mapState.zoom > 14} // && (activeComponent === 'stop' || editSettings.addStops)} + drawStops={mapState.zoom > 14} zoomToTarget={mapState.target} sidebarExpanded={sidebarExpanded} {...this.props} /> diff --git a/src/main/client/editor/components/map/StopsLayer.js b/src/main/client/editor/components/map/StopsLayer.js index 5fc1653cd..436b11553 100644 --- a/src/main/client/editor/components/map/StopsLayer.js +++ b/src/main/client/editor/components/map/StopsLayer.js @@ -30,7 +30,6 @@ export default class StopsLayer extends Component { if (activeEntity && results.findIndex(r => r[2].id === activeEntity.id) === -1) { results.push([0, 0, activeEntity]) } - console.log(results) const outOfZoom = !drawStops // console.log(mapState.bounds, paddedBounds) return ( diff --git a/src/main/client/editor/reducers/mapState.js b/src/main/client/editor/reducers/mapState.js index 79b6736af..c06273330 100644 --- a/src/main/client/editor/reducers/mapState.js +++ b/src/main/client/editor/reducers/mapState.js @@ -2,7 +2,7 @@ import update from 'react-addons-update' import { latLngBounds } from 'leaflet' import rbush from 'rbush' -import { getEntityBounds } from '../util/gtfs' +import { getEntityBounds, stopToGtfs } from '../util/gtfs' const defaultState = { zoom: null, @@ -24,7 +24,7 @@ const mapState = (state = defaultState, action) => { return state case 'RECEIVE_STOPS': const tree = rbush(9, ['[0]', '[1]', '[0]', '[1]']) - tree.load(action.stops.map(s => ([s.stop_lon, s.stop_lat, s]))) + tree.load(action.stops.map(stopToGtfs).map(s => ([s.stop_lon, s.stop_lat, s]))) return update(state, { stopTree: {$set: tree} }) From fa9f2d8860b770f3c7c2abdb8ae93cd3d56bf1d1 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 19 Dec 2016 14:10:51 -0500 Subject: [PATCH 201/323] fix greater than 24 hr time formatting --- .../components/timetable/EditableCell.js | 18 ++++++++++++++++-- .../editor/components/timetable/Timetable.js | 5 +++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/timetable/EditableCell.js b/src/main/client/editor/components/timetable/EditableCell.js index adfb7c2cc..7568cc248 100644 --- a/src/main/client/editor/components/timetable/EditableCell.js +++ b/src/main/client/editor/components/timetable/EditableCell.js @@ -133,12 +133,26 @@ export default class EditableCell extends Component { this.cancel() } } else { + let data = this.state.data + let parts = this.state.data.split(':') + const hours = +parts[0] + let greaterThan24 = false + + // check for times greater than 24:00 + if (parts && parts[0] && hours >= 24 && hours < 34) { + parts[0] = `0${hours - 24}` + greaterThan24 = true + data = parts.join(':') + } let date = moment().startOf('day').format('YYYY-MM-DD') - let momentTime = moment(date + 'T' + this.state.data, TIMETABLE_FORMATS, true) + let momentTime = moment(date + 'T' + data, TIMETABLE_FORMATS, true) let value = momentTime.isValid() ? momentTime.diff(date, 'seconds') : null + if (greaterThan24 && momentTime.isValid()) { + value += 86400 + } // check for valid time and new value - if ((this.state.data === '' || momentTime.isValid()) && value !== this.state.data) { + if ((data === '' || momentTime.isValid()) && value !== data) { this.setState({data: value, isEditing: false}) this.props.onChange(value) this.props.onStopEditing() diff --git a/src/main/client/editor/components/timetable/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js index c480c73f9..41b4f051b 100644 --- a/src/main/client/editor/components/timetable/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -240,6 +240,11 @@ export default class Timetable extends Component { } else { if (value === 0) { return moment().startOf('day').seconds(value).format('HH:mm:ss') + } else if (value && value >= 86400) { + let text = moment().startOf('day').seconds(value).format('HH:mm:ss') + let parts = text.split(':') + parts[0] = +parts[0] + 24 + return parts.join(':') } else if (value && value > 0) { return moment().startOf('day').seconds(value).format('HH:mm:ss') } else { From 690540765724008fa8eb320d07c8d4af21b35f2d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 6 Jan 2017 10:34:14 -0500 Subject: [PATCH 202/323] check for null bounds on OSM extract fetch; print validation processing exceptions --- .../datatools/manager/models/FeedVersion.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index 06c4d74f8..818196629 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -399,7 +399,7 @@ private void saveValidationResult(File file) { FeedStore.s3Client.putObject(new PutObjectRequest( DataManager.feedBucket, keyName, file)); } catch (AmazonServiceException ase) { - LOG.error("Error uploading validation json to S3"); + LOG.error("Error uploading validation json to S3", ase); } } // save to validation directory in gtfs folder @@ -410,7 +410,7 @@ private void saveValidationResult(File file) { try { FileUtils.copyFile(file, new File(FeedStore.basePath + "/" + keyName)); } catch (IOException e) { - LOG.error("Error saving validation json to local disk"); + LOG.error("Error saving validation json to local disk", e); } } } @@ -513,8 +513,14 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { return null; } + @JsonIgnore public static File getOSMFile(Rectangle2D bounds) { - return new File(String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf", getOSMPath(), bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY())); + if (bounds != null) { + return new File(String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf", getOSMPath(), bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY())); + } + else { + return null; + } } public TransportNetwork buildTransportNetwork() { From 9429015fee97216548c75dc56af0921c4da09c3d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 6 Jan 2017 12:23:08 -0500 Subject: [PATCH 203/323] bug fix for error fetching alerts --- .gitignore | 1 + src/main/client/alerts/actions/alerts.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 321b54668..3e71639f0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ config.yml config_server.yml configurations/* !configurations/default +tmp/ diff --git a/src/main/client/alerts/actions/alerts.js b/src/main/client/alerts/actions/alerts.js index c202513e4..8fac3a4f5 100644 --- a/src/main/client/alerts/actions/alerts.js +++ b/src/main/client/alerts/actions/alerts.js @@ -91,7 +91,7 @@ export function fetchRtdAlerts () { return function (dispatch, getState) { dispatch(requestRtdAlerts()) return secureFetch(getAlertsUrl(), getState()).then((res) => { - if (res.status >= 400) { + if (!res || res.status >= 400) { dispatch(setErrorMessage('Error fetching alerts!')) return [] } From eb60ac75af2c889c9dee47fae432491c6db2eef0 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 6 Jan 2017 12:23:49 -0500 Subject: [PATCH 204/323] update comment --- .../manager/controllers/api/FeedSourceController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index 24d810fb1..5684c9d5f 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -252,8 +252,8 @@ public static boolean fetch (Request req, Response res) throws JsonProcessingExc job.run(); // WARNING: infinite 2D bounds Jackson error when returning job.result, so this method now returns true - // because we don't return the feed immediately anyways. - // job.result; + // because we don't need to return the feed immediately anyways. + // return job.result; return true; } From c20f8762bfe5b9b2a47ab2fb743f62d2674c8619 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 6 Jan 2017 12:24:19 -0500 Subject: [PATCH 205/323] fix floating footer issue --- src/main/client/common/components/ManagerPage.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/common/components/ManagerPage.js b/src/main/client/common/components/ManagerPage.js index 8435c4574..5b23b6840 100644 --- a/src/main/client/common/components/ManagerPage.js +++ b/src/main/client/common/components/ManagerPage.js @@ -71,6 +71,7 @@ export default class ManagerPage extends Component { paddingBottom: '140px', minHeight: '100%', marginBottom: '-140px', + // minHeight: '500px', position: 'relative' }} > From 89fc33febff7f8a556c1a747ed3b3cb9d4905468 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 16:21:08 -0500 Subject: [PATCH 206/323] check if entity ID exists to avoid overwriting during load --- .../manager/controllers/DumpController.java | 97 +++++++++++-------- 1 file changed, 59 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index 8a831a494..f6fe51386 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -163,52 +163,73 @@ public static boolean loadLegacy (Request req, Response res) throws Exception { } private static void loadLegacyProject (JsonNode node) { - System.out.println("load legacy project " + node.findValue("name")); - Project project = new Project(); - project.id = node.findValue("id").asText(); - project.name = node.findValue("name").asText(); - project.save(false); + String name = node.findValue("name").asText(); + String id = node.findValue("id").asText(); + if (Project.get(id) == null) { + LOG.info("load legacy project " + name); + Project project = new Project(); + project.id = id; + project.name = name; + project.save(false); + } + else { + LOG.warn("legacy project {} already exists... skipping", name); + } } private static void loadLegacyFeedSource (JsonNode node) throws Exception { - System.out.println("load legacy FeedSource " + node.findValue("name")); - FeedSource fs = new FeedSource(); - fs.id = node.findValue("id").asText(); - fs.projectId = node.findValue("feedCollectionId").asText(); - fs.name = node.findValue("name").asText(); - switch(node.findValue("retrievalMethod").asText()) { - case "FETCHED_AUTOMATICALLY": - fs.retrievalMethod = FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY; - break; - case "MANUALLY_UPLOADED": - fs.retrievalMethod = FeedSource.FeedRetrievalMethod.MANUALLY_UPLOADED; - break; - case "PRODUCED_IN_HOUSE": - fs.retrievalMethod = FeedSource.FeedRetrievalMethod.PRODUCED_IN_HOUSE; - break; - } - fs.snapshotVersion = node.findValue("snapshotVersion").asText(); - Object url = node.findValue("url").asText(); - fs.url = url != null && !url.equals("null") ? new URL(url.toString()) : null; + String name = node.findValue("name").asText(); + String id = node.findValue("id").asText(); + if (FeedSource.get(id) == null) { + LOG.info("load legacy FeedSource " + name); + FeedSource fs = new FeedSource(); + fs.id = id; + fs.projectId = node.findValue("feedCollectionId").asText(); + fs.name = name; + switch(node.findValue("retrievalMethod").asText()) { + case "FETCHED_AUTOMATICALLY": + fs.retrievalMethod = FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY; + break; + case "MANUALLY_UPLOADED": + fs.retrievalMethod = FeedSource.FeedRetrievalMethod.MANUALLY_UPLOADED; + break; + case "PRODUCED_IN_HOUSE": + fs.retrievalMethod = FeedSource.FeedRetrievalMethod.PRODUCED_IN_HOUSE; + break; + } + fs.snapshotVersion = node.findValue("snapshotVersion").asText(); + Object url = node.findValue("url").asText(); + fs.url = url != null && !url.equals("null") ? new URL(url.toString()) : null; + + //fs.lastFetched = new Date(node.findValue("lastFetched").asText()); + //System.out.println("wrote lastFetched"); - //fs.lastFetched = new Date(node.findValue("lastFetched").asText()); - //System.out.println("wrote lastFetched"); + fs.deployable = node.findValue("deployable").asBoolean(); + fs.isPublic = node.findValue("isPublic").asBoolean(); + fs.save(false); + } + else { + LOG.warn("legacy FeedSource {} already exists... skipping", name); + } - fs.deployable = node.findValue("deployable").asBoolean(); - fs.isPublic = node.findValue("isPublic").asBoolean(); - fs.save(false); } private static void loadLegacyFeedVersion (JsonNode node) throws Exception { - System.out.println("load legacy FeedVersion " + node.findValue("id")); - FeedVersion version = new FeedVersion(); - version.id = node.findValue("id").asText(); - version.version = node.findValue("version").asInt(); - version.feedSourceId = node.findValue("feedSourceId").asText(); - version.hash = node.findValue("hash").asText(); - version.updated = new Date(node.findValue("updated").asLong()); - System.out.println("updated= " + node.findValue("updated").asText()); - version.save(false); + String id = node.findValue("id").asText(); + if (FeedVersion.get(id) == null) { + LOG.info("load legacy FeedVersion " + node.findValue("id")); + FeedVersion version = new FeedVersion(); + version.id = node.findValue("id").asText(); + version.version = node.findValue("version").asInt(); + version.feedSourceId = node.findValue("feedSourceId").asText(); + version.hash = node.findValue("hash").asText(); + version.updated = new Date(node.findValue("updated").asLong()); + LOG.info("updated= " + node.findValue("updated").asText()); + version.save(false); + } + else { + LOG.warn("legacy FeedVersion {} already exists... skipping", id); + } } public static boolean validateAll (Request req, Response res) throws Exception { From e2e94d0b68f903d72db54c2d66452066e9c42aca Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 17:32:37 -0500 Subject: [PATCH 207/323] remove halt in validateAll --- .../conveyal/datatools/manager/controllers/DumpController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java index f6fe51386..355129135 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/DumpController.java @@ -247,7 +247,7 @@ public static boolean validateAll (Request req, Response res) throws Exception { version.save(); } catch (Exception e) { LOG.error("Could not validate", e); - halt(400, "Error validating feed"); +// halt(400, "Error validating feed"); } } LOG.info("Finished validation..."); From bfe31f5053e1a1cddb64759411bc6509ad31b78c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 21:28:20 -0500 Subject: [PATCH 208/323] check for feedVersions --- src/main/client/manager/components/DeploymentViewer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index c6fc1f21f..c5088eb95 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -30,7 +30,7 @@ export default class DeploymentViewer extends Component { } const deployableFeeds = this.props.feedSources ? this.props.feedSources.filter(fs => - this.props.deployment.feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 && + this.props.deployment.feedVersions && this.props.deployment.feedVersions.findIndex(v => v.feedSource.id === fs.id) === -1 && fs.deployable && fs.latestValidation ) From 36cc94f11dcaca1bfec15fe4d61a9b9be038aab2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 21:29:09 -0500 Subject: [PATCH 209/323] fix sidebar width issue --- src/main/client/gtfs/components/GtfsMap.js | 10 ++++++++-- src/main/client/gtfs/components/gtfsmapsearch.js | 2 +- src/main/client/gtfs/containers/ActiveGtfsMap.js | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index f23f1b8b6..01e892752 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -176,10 +176,16 @@ export default class GtfsMap extends Component { patterns, onRouteClick, pattern, - showIsochrones + showIsochrones, + sidebarExpanded } = this.props + let mapWidth = width + if (width.indexOf('px') !== -1) { + let diff = sidebarExpanded ? 30 : 0 + mapWidth = `${width.split('px')[0] - diff}px` + } var mapStyle = { - width: width, // % or px + width: mapWidth, // % or px height: `${height}px` // only px } return ( diff --git a/src/main/client/gtfs/components/gtfsmapsearch.js b/src/main/client/gtfs/components/gtfsmapsearch.js index 18ad9bd8e..640eaba79 100644 --- a/src/main/client/gtfs/components/gtfsmapsearch.js +++ b/src/main/client/gtfs/components/gtfsmapsearch.js @@ -95,7 +95,7 @@ export default class GtfsMapSearch extends Component { onRouteClick={this.props.onRouteClick} newEntityId={this.props.newEntityId} popupAction={this.props.popupAction} - width='555px' + width={`100%`} height={400} {...searchProps} /> diff --git a/src/main/client/gtfs/containers/ActiveGtfsMap.js b/src/main/client/gtfs/containers/ActiveGtfsMap.js index 5d498b1c2..a7773197d 100644 --- a/src/main/client/gtfs/containers/ActiveGtfsMap.js +++ b/src/main/client/gtfs/containers/ActiveGtfsMap.js @@ -12,7 +12,8 @@ const mapStateToProps = (state, ownProps) => { routes: state.gtfs.routes.data, patterns: state.gtfs.patterns.data, routing: state.routing.locationBeforeTransitions && state.routing.locationBeforeTransitions.pathname, - dateTime: state.gtfs.filter.dateTimeFilter + dateTime: state.gtfs.filter.dateTimeFilter, + sidebarExpanded: state.ui.sidebarExpanded } } From b3333291db2105c3cdae852d6e52713ef7809c2a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 21:30:46 -0500 Subject: [PATCH 210/323] fix width issue --- .../client/alerts/components/AlertEditor.js | 2 +- .../client/alerts/components/AlertPreview.js | 98 ++++++++++--------- .../client/alerts/components/AlertsViewer.js | 2 +- .../client/signs/components/SignEditor.js | 2 +- .../client/signs/components/SignsViewer.js | 2 +- 5 files changed, 57 insertions(+), 49 deletions(-) diff --git a/src/main/client/alerts/components/AlertEditor.js b/src/main/client/alerts/components/AlertEditor.js index cc464eedf..e908339ce 100644 --- a/src/main/client/alerts/components/AlertEditor.js +++ b/src/main/client/alerts/components/AlertEditor.js @@ -85,7 +85,7 @@ export default class AlertEditor extends React.Component { 0 ? `Alert ${this.props.alert.id}` : 'New Alert'} /> - + + + + + + ) return ( - - -

    {this.props.alert.title}

    -

    ID: #{this.props.alert.id} {publishedLabel} {entitiesLabel}

    - - - - - - - - - }> +

    - {moment(this.props.alert.start).format('MMM Do YYYY, h:mm:ssa')} to {moment(this.props.alert.end).format('MMM Do YYYY, h:mm:ssa')} - {publishedLabel} {this.props.alert.published ? 'Published' : 'Draft'} + {moment(alert.start).format('MMM Do YYYY, h:mm:ssa')} to {moment(alert.end).format('MMM Do YYYY, h:mm:ssa')} + {publishedLabel} {alert.published ? 'Published' : 'Draft'}

    -

    {this.props.alert.description}

    -

    URL: {this.props.alert.url}

    +

    {alert.description}

    +

    URL: {alert.url}

    {entitiesLabel} affected service(s)

    diff --git a/src/main/client/alerts/components/AlertsViewer.js b/src/main/client/alerts/components/AlertsViewer.js index cce1aaeb6..55c24ca5c 100644 --- a/src/main/client/alerts/components/AlertsViewer.js +++ b/src/main/client/alerts/components/AlertsViewer.js @@ -20,7 +20,7 @@ export default class AlertsViewer extends React.Component { - +

    diff --git a/src/main/client/signs/components/SignEditor.js b/src/main/client/signs/components/SignEditor.js index c01be60a8..6b218d49e 100644 --- a/src/main/client/signs/components/SignEditor.js +++ b/src/main/client/signs/components/SignEditor.js @@ -46,7 +46,7 @@ export default class SignEditor extends React.Component { 0 ? `eTID Config ${this.props.sign.id}` : 'New eTID Config'} /> - + {getMessage(messages, 'deploy')} : {getMessage(messages, 'noServers')} } onSelect={(evt) => { console.log(evt) - this.props.deployToTargetClicked(this.props.deployment, evt) - // setTimeout(() => this.props.getDeploymentStatus(this.props.deployment, evt), 5000) + deployToTargetClicked(deployment, evt) + // setTimeout(() => getDeploymentStatus(deployment, evt), 5000) }} > - {this.props.project.otpServers - ? this.props.project.otpServers.map(server => ( + {project.otpServers + ? project.otpServers.map(server => ( {server.name} )) : null @@ -79,11 +87,11 @@ export default class DeploymentViewer extends Component { this.props.deploymentPropertyChanged(this.props.deployment, 'name', value)} + value={deployment.name} + onChange={(value) => deploymentPropertyChanged(deployment, 'name', value)} /> - {this.props.deployment.deployedTo - ? + {deployment.deployedTo + ? : null }

    @@ -100,7 +108,7 @@ export default class DeploymentViewer extends Component { this.props.searchTextChanged(evt.target.value)} + onChange={evt => searchTextChanged(evt.target.value)} /> @@ -112,7 +120,7 @@ export default class DeploymentViewer extends Component { onSelect={(evt) => { console.log(evt) let feed = deployableFeeds.find(fs => fs.id === evt) - this.props.addFeedVersion(this.props.deployment, {id: feed.latestVersionId}) + addFeedVersion(deployment, {id: feed.latestVersionId}) }} > { @@ -146,12 +154,12 @@ export default class DeploymentViewer extends Component { return })} @@ -166,27 +174,34 @@ export default class DeploymentViewer extends Component { class FeedVersionTableRow extends Component { render () { - const fs = this.props.feedSource - const version = this.props.version - const result = this.props.version.validationResult + const { + feedSource, + version, + deployment, + project, + user, + updateVersionForFeedSource, + deleteFeedVersionClicked + } = this.props + const result = version.validationResult const na = (N/A) const hasVersionStyle = {cursor: 'pointer'} const noVersionStyle = {color: 'lightGray'} - const disabled = !this.props.user.permissions.hasFeedPermission(this.props.project.id, fs.id, 'manage-feed') + const disabled = !user.permissions.hasFeedPermission(project.id, feedSource.id, 'manage-feed') return ( - + - {fs.name} + {feedSource.name} - Version {version.version} + Version {version.version} From 8ee7325b7bb496f29e3439f87264c98e65ba2b93 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 11 Jan 2017 22:18:28 -0500 Subject: [PATCH 220/323] fix deployment creation --- src/main/client/manager/components/DeploymentViewer.js | 1 + src/main/client/manager/components/DeploymentsPanel.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index d0de4d706..f0608f17b 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -114,6 +114,7 @@ export default class DeploymentViewer extends Component { {getMessage(messages, 'addFeedSource')} : {getMessage(messages, 'allFeedsAdded')}} diff --git a/src/main/client/manager/components/DeploymentsPanel.js b/src/main/client/manager/components/DeploymentsPanel.js index 2cd3e2dbc..7a37b1025 100644 --- a/src/main/client/manager/components/DeploymentsPanel.js +++ b/src/main/client/manager/components/DeploymentsPanel.js @@ -35,7 +35,7 @@ export default class DeploymentsPanel extends Component { // }) // } render () { - const deployment = this.props.deployments && this.props.deployments.find(d => d.id === this.props.activeSubComponent) + const deployment = this.props.deployments && this.props.deployments.find(d => d.id && d.id === this.props.activeSubComponent) if (deployment) { return ( Date: Thu, 12 Jan 2017 10:08:56 -0500 Subject: [PATCH 221/323] check for empty string in addition to null --- .../manager/controllers/api/ProjectController.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 1e83ec536..a996ce2cc 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -266,19 +266,19 @@ private static void updateBuildConfig(Project proj, JsonNode buildConfig) { if(buildConfig.has("subwayAccessTime")) { JsonNode subwayAccessTime = buildConfig.get("subwayAccessTime"); // allow client to un-set option via 'null' value - proj.buildConfig.subwayAccessTime = subwayAccessTime.isNull() ? null : subwayAccessTime.asDouble(); + proj.buildConfig.subwayAccessTime = subwayAccessTime.isNull() || subwayAccessTime.asText().equals("") ? null : subwayAccessTime.asDouble(); } if(buildConfig.has("fetchElevationUS")) { JsonNode fetchElevationUS = buildConfig.get("fetchElevationUS"); - proj.buildConfig.fetchElevationUS = fetchElevationUS.isNull() ? null : fetchElevationUS.asBoolean(); + proj.buildConfig.fetchElevationUS = fetchElevationUS.isNull() || fetchElevationUS.asText().equals("") ? null : fetchElevationUS.asBoolean(); } if(buildConfig.has("stationTransfers")) { JsonNode stationTransfers = buildConfig.get("stationTransfers"); - proj.buildConfig.stationTransfers = stationTransfers.isNull() ? null : stationTransfers.asBoolean(); + proj.buildConfig.stationTransfers = stationTransfers.isNull() || stationTransfers.asText().equals("") ? null : stationTransfers.asBoolean(); } if (buildConfig.has("fares")) { JsonNode fares = buildConfig.get("fares"); - proj.buildConfig.fares = fares.isNull() ? null : fares.asText(); + proj.buildConfig.fares = fares.isNull() || fares.asText().equals("") ? null : fares.asText(); } } From 3b3aced9e01803facd379aaf9592b1b1e5df118f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 13 Jan 2017 16:55:47 -0500 Subject: [PATCH 222/323] fix bug where addRoute button not shown --- src/main/client/gtfs/components/GtfsMap.js | 19 +++++++++++++++++-- .../client/gtfs/components/PatternGeoJson.js | 1 - 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/client/gtfs/components/GtfsMap.js b/src/main/client/gtfs/components/GtfsMap.js index 01e892752..dece7ac20 100644 --- a/src/main/client/gtfs/components/GtfsMap.js +++ b/src/main/client/gtfs/components/GtfsMap.js @@ -231,7 +231,15 @@ export default class GtfsMap extends Component { : null } {/* Stop from GtfsSearch */} - {stop && } + {stop && } {/* Patterns from map bounds search */} @@ -249,7 +257,14 @@ export default class GtfsMap extends Component { : null } {/* Pattern from GtfsSearch */} - {pattern && } + {pattern && + + } {/* Isochrones from map click */} diff --git a/src/main/client/gtfs/components/PatternGeoJson.js b/src/main/client/gtfs/components/PatternGeoJson.js index 4323ebb69..428f31560 100644 --- a/src/main/client/gtfs/components/PatternGeoJson.js +++ b/src/main/client/gtfs/components/PatternGeoJson.js @@ -31,7 +31,6 @@ export default class PatternGeoJson extends GeoJson {

    {routeName}

    -

    {getRouteName(route)}

    • ID: {routeId}
    • Agency:{' '} From cbd18fcb29ee597c487a565d01f0e00b912d4b8d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 13 Jan 2017 17:33:13 -0500 Subject: [PATCH 223/323] sort gtfs stops/routes results by startsWith --- src/main/client/gtfs/components/gtfssearch.js | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/client/gtfs/components/gtfssearch.js b/src/main/client/gtfs/components/gtfssearch.js index 341627ab4..61a951b6f 100644 --- a/src/main/client/gtfs/components/gtfssearch.js +++ b/src/main/client/gtfs/components/gtfssearch.js @@ -67,7 +67,15 @@ export default class GtfsSearch extends Component { }) .then((stops) => { const stopOptions = stops !== null && stops.length > 0 - ? stops.map(stop => { + ? stops.sort((a, b) => { + const aStopName = a && a.stop_name && a.stop_name.toLowerCase() + const bStopName = b && b.stop_name && b.stop_name.toLowerCase() + if (aStopName.startsWith(input)) { + return bStopName.startsWith(input) ? aStopName.localeCompare(bStopName) : -1 + } else { + return bStopName.startsWith(input) ? 1 : aStopName.localeCompare(bStopName) + } + }).map(stop => { const agency = getFeed(this.props.feeds, stop.feed_id) return { stop, @@ -99,7 +107,16 @@ export default class GtfsSearch extends Component { }) .then((routes) => { const routeOptions = routes !== null && routes.length > 0 - ? routes.map(route => ( + ? routes.sort((a, b) => { + const aRouteName = a && getRouteName(a).toLowerCase() + const bRouteName = b && getRouteName(b).toLowerCase() + if (aRouteName.startsWith(input)) { + return bRouteName.startsWith(input) ? aRouteName.localeCompare(bRouteName) : -1 + } else { + return bRouteName.startsWith(input) ? 1 : aRouteName.localeCompare(bRouteName) + } + // return 0 + }).map(route => ( { route, value: route.route_id, From 33232d7b1308b83b0329d18c8e58c69b06152777 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 16 Jan 2017 16:35:59 -0500 Subject: [PATCH 224/323] fix(auth): fix check login bug where app logs out if token expired --- src/main/client/common/containers/App.js | 2 +- src/main/client/common/user/Auth0Manager.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/client/common/containers/App.js b/src/main/client/common/containers/App.js index a31b77a1a..222ddb58e 100644 --- a/src/main/client/common/containers/App.js +++ b/src/main/client/common/containers/App.js @@ -119,7 +119,7 @@ const mapStateToProps = (state, ownProps) => { const mapDispatchToProps = (dispatch, ownProps) => { return { checkJobStatus: () => dispatch(checkJobStatus()), - checkExistingLogin: (callback) => dispatch(checkExistingLogin()), + checkExistingLogin: (props) => dispatch(checkExistingLogin(props)), login: (options) => dispatch(login(null, null, options)) } } diff --git a/src/main/client/common/user/Auth0Manager.js b/src/main/client/common/user/Auth0Manager.js index 35ca48526..890f6028b 100644 --- a/src/main/client/common/user/Auth0Manager.js +++ b/src/main/client/common/user/Auth0Manager.js @@ -70,6 +70,8 @@ export default class Auth0Manager { if (props && props.required) { return this.loginViaLock({closable: false}) } else { + console.log(props) + console.log('bad token. logging out...') this.logout() } }) From 9361359ec9f59c47a0d04bc08d0b842bf2694640 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 25 Jan 2017 14:52:50 -0500 Subject: [PATCH 225/323] temporarily add babel-polyfill manually --- package.json | 1 + yarn.lock | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/package.json b/package.json index 6dd0d8d89..cd4550e21 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@conveyal/woonerf": "^0.2.0", "array-unique": "^0.2.1", "auth0-lock": "9", + "babel-polyfill": "^6.22.0", "body-parser": "^1.14.2", "bootstrap": "^3.3.7", "change-case": "^3.0.0", diff --git a/yarn.lock b/yarn.lock index 7624b44d0..f1b3342c9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -947,6 +947,14 @@ babel-plugin-transform-system-register@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/babel-plugin-transform-system-register/-/babel-plugin-transform-system-register-0.0.1.tgz#9dff40390c2763ac518f0b2ad7c5ea4f65a5be25" +babel-polyfill@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.22.0.tgz#1ac99ebdcc6ba4db1e2618c387b2084a82154a3b" + dependencies: + babel-runtime "^6.22.0" + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + babel-preset-env@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-0.0.6.tgz#cda63a020069098fad12272a7a447a7c5bafb3c8" @@ -1042,6 +1050,13 @@ babel-runtime@^5.6.18: dependencies: core-js "^1.0.0" +babel-runtime@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.8.0, babel-template@^6.9.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" @@ -5908,6 +5923,10 @@ regenerate@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.1.tgz#0300203a5d2fdcf89116dce84275d011f5903f33" +regenerator-runtime@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" + regenerator-runtime@^0.9.5: version "0.9.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz#403d6d40a4bdff9c330dd9392dcbb2d9a8bba1fc" From 995b67519a050ad0510f7082a3b0276354ed594b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 25 Jan 2017 14:53:08 -0500 Subject: [PATCH 226/323] include babel-polyfill --- src/main/client/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/main.js b/src/main/client/main.js index bf8493241..528f2ae57 100644 --- a/src/main/client/main.js +++ b/src/main/client/main.js @@ -1,3 +1,4 @@ +import 'babel-polyfill' import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' From 2e41173a00039249f1581a2e8c85b2c6b4347b95 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 25 Jan 2017 14:54:02 -0500 Subject: [PATCH 227/323] fixes to reflect changes in r5 --- .../manager/controllers/api/FeedVersionController.java | 6 +++++- .../manager/jobs/ReadTransportNetworkJob.java | 10 ++++------ .../conveyal/datatools/manager/models/FeedVersion.java | 4 +--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index 36a18392c..a097691dd 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -262,7 +262,11 @@ private static void buildOrReadTransportNetwork(FeedVersion version, Auth0UserPr } private static JsonNode getRouterResult(TransportNetwork transportNetwork, AnalystClusterRequest clusterRequest) { - PointSet targets = transportNetwork.getGridPointSet(); + PointSet targets; + if (transportNetwork.gridPointSet == null) { + transportNetwork.rebuildLinkedGridPointSet(); + } + targets = transportNetwork.gridPointSet; StreetMode mode = StreetMode.WALK; final LinkedPointSet linkedTargets = targets.link(transportNetwork.streetLayer, mode); RepeatedRaptorProfileRouter router = new RepeatedRaptorProfileRouter(transportNetwork, clusterRequest, linkedTargets, new TaskStatistics()); diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java index 36a271fe0..afa40b6bc 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java @@ -5,8 +5,10 @@ import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.r5.transit.TransportNetwork; +import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.util.Map; @@ -29,12 +31,8 @@ public ReadTransportNetworkJob (FeedVersion feedVersion, String owner) { @Override public void run() { System.out.println("Reading network"); - InputStream is = null; - try { - is = new FileInputStream(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + feedVersion.feedSourceId + "/" + feedVersion.id + "_network.dat"); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } + File is = null; + is = new File(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + feedVersion.feedSourceId + "/" + feedVersion.id + "_network.dat"); try { feedVersion.transportNetwork = TransportNetwork.read(is); } catch (Exception e) { diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index 818196629..cb41210e0 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -502,10 +502,8 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { } this.transportNetwork = tn; File tnFile = getTransportNetworkPath(); - OutputStream tnOut; try { - tnOut = new FileOutputStream(tnFile); - tn.write(tnOut); + tn.write(tnFile); return transportNetwork; } catch (IOException e) { e.printStackTrace(); From b517d6369de3831ec4e6ad40060d53b2932a5d88 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 25 Jan 2017 14:55:53 -0500 Subject: [PATCH 228/323] hide departure times in prod --- .../client/editor/components/timetable/TimetableHeader.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/timetable/TimetableHeader.js b/src/main/client/editor/components/timetable/TimetableHeader.js index 03f179e8a..eb63fbcf2 100644 --- a/src/main/client/editor/components/timetable/TimetableHeader.js +++ b/src/main/client/editor/components/timetable/TimetableHeader.js @@ -7,6 +7,7 @@ import HourMinuteInput from '../HourMinuteInput' import CalendarSelect from './CalendarSelect' import RouteSelect from './RouteSelect' import PatternSelect from './PatternSelect' +import {getConfigProperty} from '../../../common/util/config' export default class TimetableHeader extends Component { static propTypes = { @@ -56,14 +57,16 @@ export default class TimetableHeader extends Component { inline className='pull-right' > - {activePattern && !activePattern.useFrequency + {activePattern && !activePattern.useFrequency && getConfigProperty('application.dev') ? { toggleDepartureTimes() }} > - Hide departures + Hiding departure times will keep arrival and departure times in sync. WARNING: do not use if arrivals and departures differ.}> + Hide departures + : null } From ebda142ccbe082b3c7b6f77e1fb057b74f0a2fab Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 09:19:47 -0500 Subject: [PATCH 229/323] ci(travis): add travis --- .travis.yml | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..2a6ddf70d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,36 @@ +language: java + +# Travis doesn't (yet) support OpenJDK 8 +jdk: + - oraclejdk8 +node_js: + - '6' +before_install: + - npm i -g codecov +# Replace Travis's default Maven installation step with a no-op. +# This avoids redundantly pre-running 'mvn install -DskipTests' every time. +install: true + +# Replace Travis's default build step. +# Run all Maven phases at once up through verify, install, and deploy. +script: + - mvn clean verify + - yarn test -- --coverage + - codecov + - yarn run build + +# If sudo is disabled, CI runs on container based infrastructure (allows caching &c.) +sudo: false + +# Retain the local Maven repository to speed up builds. +cache: + directories: + - "$HOME/.m2/repository" + +# Notify us of the build status on the Slack channel +notifications: + slack: conveyal:WQxmWiu8PdmujwLw4ziW72Gc + +# Push results to codecov.io +after_success: + - bash <(curl -s https://codecov.io/bash) From 303019e4600921eb4ee44d61638d49c4d7a1cf5b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 09:24:44 -0500 Subject: [PATCH 230/323] ci(travis): install yarn --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2a6ddf70d..e5c3c8233 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ node_js: - '6' before_install: - npm i -g codecov + - npm i -g yarn # Replace Travis's default Maven installation step with a no-op. # This avoids redundantly pre-running 'mvn install -DskipTests' every time. install: true From 8b2304caf54e9d9b6afa8a98d2b00a5e7ab1837d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 09:40:29 -0500 Subject: [PATCH 231/323] ci(travis): fix node version with nvm --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e5c3c8233..eb4a3fa75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,12 @@ language: java # Travis doesn't (yet) support OpenJDK 8 jdk: - oraclejdk8 -node_js: - - '6' + +# Must use java image and switch node versions with nvm: https://github.com/travis-ci/travis-ci/issues/3827#issuecomment-98114873 +env: + - NODE_VERSION=6 before_install: + - nvm install $NODE_VERSION - npm i -g codecov - npm i -g yarn # Replace Travis's default Maven installation step with a no-op. @@ -15,6 +18,7 @@ install: true # Replace Travis's default build step. # Run all Maven phases at once up through verify, install, and deploy. script: + - nvm use $NODE_VERSION - mvn clean verify - yarn test -- --coverage - codecov From da507c1ae1394035c84273a6a7f39b2cd8afbbf2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 12:25:15 -0500 Subject: [PATCH 232/323] ci(travis): add yarn to install node_modules --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index eb4a3fa75..8311da239 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,12 +14,14 @@ before_install: # Replace Travis's default Maven installation step with a no-op. # This avoids redundantly pre-running 'mvn install -DskipTests' every time. install: true - +cache: + yarn: true # Replace Travis's default build step. # Run all Maven phases at once up through verify, install, and deploy. script: - nvm use $NODE_VERSION - mvn clean verify + - yarn - yarn test -- --coverage - codecov - yarn run build From 21f499ad3e5f6966866344db496dd9c8148b725d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 12:42:16 -0500 Subject: [PATCH 233/323] style(lint): fix linting issues in js --- src/main/client/editor/components/EditorFeedSourcePanel.js | 1 - src/main/client/editor/components/GtfsEditor.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/client/editor/components/EditorFeedSourcePanel.js b/src/main/client/editor/components/EditorFeedSourcePanel.js index cff3220a7..28b1fc384 100644 --- a/src/main/client/editor/components/EditorFeedSourcePanel.js +++ b/src/main/client/editor/components/EditorFeedSourcePanel.js @@ -1,6 +1,5 @@ import React, {Component, PropTypes} from 'react' import { Panel, Row, Col, ButtonToolbar, Button, Glyphicon, ListGroup, ListGroupItem } from 'react-bootstrap' -import { browserHistory } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' import moment from 'moment' import {Icon} from '@conveyal/woonerf' diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index 10de8c6e9..eadeef7ef 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -225,7 +225,7 @@ export default class GtfsEditor extends Component { zoomToTarget={mapState.target} sidebarExpanded={sidebarExpanded} {...this.props} /> - {!activeComponent && !hideTutorial && false + {!activeComponent && !hideTutorial && getConfigProperty('application.dev') ? From c9b514580ee3825d3e3379023409fa13a89827f1 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 13:06:44 -0500 Subject: [PATCH 234/323] fix(FeedVersionController): fixes issue with uploading feeds Previously feeds that were autofetched could not also have versions directly uploaded. This restriction has been lifted in the new generation of the tool. #12 --- .../manager/controllers/api/FeedVersionController.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index a097691dd..848fe9dac 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -97,9 +97,10 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc Auth0UserProfile userProfile = req.attribute("user"); FeedSource s = requestFeedSourceById(req, "manage"); - if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { - halt(400, "Feed is auto-fetched! Cannot upload."); - } + // Autofetched feeds are no longer restricted from directly-uploaded versions +// if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { +// halt(400, "Feed is auto-fetched! Cannot upload."); +// } FeedVersion latest = s.getLatest(); FeedVersion v = new FeedVersion(s); v.setUser(userProfile); From 21f908e95cb99e261245794852bd55048ef68bd7 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 26 Jan 2017 13:10:39 -0500 Subject: [PATCH 235/323] fix(bootstrap-table): bumped version of react-bootstrap-table to fix pagination issue fixes #11 --- package.json | 2 +- yarn.lock | 77 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index cd4550e21..12c08ab57 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "react-addons-update": "^15.4.1", "react-bootstrap": "^0.30.0-rc.2", "react-bootstrap-datetimepicker": "^0.0.22", - "react-bootstrap-table": "^2.5.2", + "react-bootstrap-table": "3.0.0-beta.7", "react-color": "^2.3.4", "react-dnd": "^2.1.4", "react-dnd-html5-backend": "^2.1.2", diff --git a/yarn.lock b/yarn.lock index f1b3342c9..5f91cd0f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,17 @@ # yarn lockfile v1 +"@allenfang/react-toastr@2.8.2": + version "2.8.2" + resolved "https://registry.yarnpkg.com/@allenfang/react-toastr/-/react-toastr-2.8.2.tgz#0bef6585189e0571dd6bdfc4ef98bc9f9c47da0c" + dependencies: + classnames "^2.2.5" + element-class "^0.2.2" + lodash "^4.16.1" + react "^0.14.0 || <15.4.0" + react-addons-update "^0.14.0 || <15.4.0" + react-dom "^0.14.0 || <15.4.0" + "@conveyal/woonerf": version "0.2.0" resolved "https://registry.yarnpkg.com/@conveyal/woonerf/-/woonerf-0.2.0.tgz#53d75d549152082b8ac7d1ab1e980b1e583c72a5" @@ -1037,12 +1048,12 @@ babel-register@^6.18.0: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.18.0.tgz#0f4177ffd98492ef13b9f823e9994a02584c9078" +babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.9.0, babel-runtime@^6.9.1: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" dependencies: core-js "^2.4.0" - regenerator-runtime "^0.9.5" + regenerator-runtime "^0.10.0" babel-runtime@^5.6.18: version "5.8.38" @@ -1050,13 +1061,6 @@ babel-runtime@^5.6.18: dependencies: core-js "^1.0.0" -babel-runtime@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.10.0" - babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.8.0, babel-template@^6.9.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" @@ -2236,7 +2240,7 @@ ejsify@0.1.0: ejs "~0.8.3" through "~2.3.4" -element-class@^0.2.2: +element-class@^0.2.0, element-class@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e" @@ -2567,6 +2571,10 @@ exec-sh@^0.2.0: dependencies: merge "^1.1.3" +exenv@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.0.tgz#3835f127abf075bfe082d0aed4484057c78e3c89" + exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" @@ -5548,7 +5556,11 @@ react-addons-perf@^15.3.2, react-addons-perf@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-shallow-compare/-/react-addons-shallow-compare-15.4.1.tgz#b68103dd4d13144cb221065f6021de1822bd435a" -"react-addons-update@^0.14.0 || ^15.0.0", react-addons-update@^15.4.1: +"react-addons-update@^0.14.0 || <15.4.0": + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.3.2.tgz#b6385c4db1e5df371825e0615b04360ed94430fe" + +react-addons-update@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react-addons-update/-/react-addons-update-15.4.1.tgz#00c07f45243aa9715e1706bbfd1f23d3d8d80bd1" @@ -5560,12 +5572,13 @@ react-bootstrap-datetimepicker@^0.0.22: classnames "^2.1.2" moment "^2.8.2" -react-bootstrap-table@^2.5.2: - version "2.5.6" - resolved "https://registry.yarnpkg.com/react-bootstrap-table/-/react-bootstrap-table-2.5.6.tgz#4a7fe78d2ab9d4b7ff86e460f13cc02637f66457" +react-bootstrap-table@3.0.0-beta.7: + version "3.0.0-beta.7" + resolved "https://registry.yarnpkg.com/react-bootstrap-table/-/react-bootstrap-table-3.0.0-beta.7.tgz#1ba4ba9b75c9c281e6d0980b8a8141d8af17478d" dependencies: + "@allenfang/react-toastr" "2.8.2" classnames "^2.1.2" - react-toastr "^2.8.0" + react-modal "^1.4.0" react-bootstrap@^0.30.0-rc.2: version "0.30.6" @@ -5608,7 +5621,11 @@ react-dnd@^2.1.4: invariant "^2.1.0" lodash "^4.2.0" -"react-dom@^0.14.0 || ^15.0.0", "react-dom@^15.0.0 || ^16.0.0", react-dom@^15.3.2, react-dom@^15.4.1: +"react-dom@^0.14.0 || <15.4.0": + version "15.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.3.2.tgz#c46b0aa5380d7b838e7a59c4a7beff2ed315531f" + +"react-dom@^15.0.0 || ^16.0.0", react-dom@^15.3.2, react-dom@^15.4.1: version "15.4.1" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-15.4.1.tgz#d54c913261aaedb17adc20410d029dcc18a1344a" dependencies: @@ -5649,6 +5666,14 @@ react-leaflet@^0.12.1: lodash "^4.0.0" warning "^3.0.0" +react-modal@^1.4.0: + version "1.6.5" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-1.6.5.tgz#f720d99bd81b1def5c2c32e0ffaa48bdaf484862" + dependencies: + element-class "^0.2.0" + exenv "1.2.0" + lodash.assign "^4.2.0" + react-overlays@^0.6.10: version "0.6.10" resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.6.10.tgz#e7e52dad47f00a0fc784eb044428c3a9e874bfa3" @@ -5712,16 +5737,6 @@ react-sidebar@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/react-sidebar/-/react-sidebar-2.2.1.tgz#a8faf6a3c62ddc562c70680d5d016fe9741b585f" -react-toastr@^2.8.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/react-toastr/-/react-toastr-2.8.1.tgz#17dfbccf37caec55e165b712e70fb35e6940205b" - dependencies: - classnames "^2.2.5" - element-class "^0.2.2" - lodash "^4.16.1" - react-addons-update "^0.14.0 || ^15.0.0" - react-dom "^0.14.0 || ^15.0.0" - react-virtualized-select@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-2.1.0.tgz#dbb05a700e198fcf0f99e59166065750f63c5685" @@ -5738,7 +5753,7 @@ react-virtualized@^8.0.5, react-virtualized@^8.5.0: classnames "^2.2.3" dom-helpers "^2.4.0" -react@^0.14.7: +"react@^0.14.0 || <15.4.0", react@^0.14.7: version "0.14.8" resolved "https://registry.yarnpkg.com/react/-/react-0.14.8.tgz#078dfa454d4745bcc54a9726311c2bf272c23684" dependencies: @@ -5927,10 +5942,6 @@ regenerator-runtime@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" -regenerator-runtime@^0.9.5: - version "0.9.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz#403d6d40a4bdff9c330dd9392dcbb2d9a8bba1fc" - regex-cache@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" From 85e9826d42c7bc0e5a7f1eecbf5a5e4a9a9ede63 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 27 Jan 2017 11:00:21 -0500 Subject: [PATCH 236/323] fix(EditorMap): added missing addStopToPattern function to AddableStopsLayer #5 --- src/main/client/editor/components/map/AddableStopsLayer.js | 7 ++++--- src/main/client/editor/components/map/EditorMap.js | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/client/editor/components/map/AddableStopsLayer.js b/src/main/client/editor/components/map/AddableStopsLayer.js index 68e591d81..57fb21f12 100644 --- a/src/main/client/editor/components/map/AddableStopsLayer.js +++ b/src/main/client/editor/components/map/AddableStopsLayer.js @@ -13,7 +13,8 @@ export default class AddableStopsLayer extends Component { stops, activePattern, editSettings, - mapState + mapState, + addStopToPattern } = this.props const { bounds } = mapState return ( @@ -60,10 +61,10 @@ export default class AddableStopsLayer extends Component { id={`add-stop-dropdown`} bsStyle='success' onSelect={(key) => { - this.addStopToPattern(activePattern, stop, key) + addStopToPattern(activePattern, stop, key) }} onClick={(e) => { - this.addStopToPattern(activePattern, stop) + addStopToPattern(activePattern, stop) }} > diff --git a/src/main/client/editor/components/map/EditorMap.js b/src/main/client/editor/components/map/EditorMap.js index d02d01def..04ae5d572 100644 --- a/src/main/client/editor/components/map/EditorMap.js +++ b/src/main/client/editor/components/map/EditorMap.js @@ -198,6 +198,7 @@ export default class EditorMap extends Component { From cc6492d5d27559331d5b09cbb8dfdd539549cf96 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 27 Jan 2017 11:32:45 -0500 Subject: [PATCH 237/323] fix(EditorMap): fix entityEdited prop for PatternStopPopup #5 --- src/main/client/editor/components/GtfsEditor.js | 1 + src/main/client/editor/components/map/EditorMap.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/client/editor/components/GtfsEditor.js b/src/main/client/editor/components/GtfsEditor.js index eadeef7ef..dc4986f80 100644 --- a/src/main/client/editor/components/GtfsEditor.js +++ b/src/main/client/editor/components/GtfsEditor.js @@ -176,6 +176,7 @@ export default class GtfsEditor extends Component { left: sidebarExpanded ? 130 : 50, bottom: 0, right: 0, + minHeight: '500px', top: 0}}> {subSubComponent === 'timetable' // && activeEntity ? Date: Fri, 27 Jan 2017 11:44:49 -0500 Subject: [PATCH 238/323] fix(EntityDetails): fix bug where component did not update on activePattern change #5 --- src/main/client/editor/components/EntityDetails.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index c220ee467..b471a223f 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -53,6 +53,7 @@ export default class EntityDetails extends Component { !shallowEqual(nextProps.mapState.target, this.props.mapState.target) || !shallowEqual(nextProps.entities, this.props.entities) || !shallowEqual(nextProps.activeEntity, this.props.activeEntity) || + !shallowEqual(nextProps.activePattern, this.props.activePattern) || !shallowEqual(nextProps.activeEntityId, this.props.activeEntityId) || !shallowEqual(nextProps.width, this.props.width) } From d8cc3b4b85f93980cbfdd58a1477759ab1a9ceb3 Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Fri, 27 Jan 2017 11:47:36 -0800 Subject: [PATCH 239/323] docs(deployment): update deployment docs --- docs/dev/deployment.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/dev/deployment.md b/docs/dev/deployment.md index b72cd669c..02e9f2372 100644 --- a/docs/dev/deployment.md +++ b/docs/dev/deployment.md @@ -95,16 +95,16 @@ To allow for the creation, deletion and editing of users you must generate a tok ## Building and Running the Application -Install the Javascript dependencies using npm: +Install the Javascript dependencies using yarn: ```bash -$ npm install +$ yarn ``` -Build the frontend using webpack: +Build the frontend using [mastarm](https://github.com/conveyal/mastarm): ```bash -$ webpack +$ npm run build ``` Package the application using Maven: From dd8f6d5c79916b58e109be267577a02ab5e3072e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 27 Jan 2017 15:27:30 -0500 Subject: [PATCH 240/323] fix(PatternStopCard): partial fix for issue with setting default dwell/travel time #5 --- .../components/pattern/PatternStopCard.js | 6 ++-- .../pattern/PatternStopContainer.js | 3 +- .../components/pattern/PatternStopsPanel.js | 36 +++++++++++-------- .../components/pattern/TripPatternList.js | 2 -- 4 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/main/client/editor/components/pattern/PatternStopCard.js b/src/main/client/editor/components/pattern/PatternStopCard.js index 41c4e9a96..2cbb48594 100644 --- a/src/main/client/editor/components/pattern/PatternStopCard.js +++ b/src/main/client/editor/components/pattern/PatternStopCard.js @@ -156,10 +156,11 @@ class PatternStopCard extends Component { > Default travel time { let patternStops = [...activePattern.patternStops] patternStops[index].defaultTravelTime = value + this.setState({defaultTravelTime: value}) updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) }} /> @@ -172,10 +173,11 @@ class PatternStopCard extends Component { > Default dwell time { let patternStops = [...activePattern.patternStops] patternStops[index].defaultDwellTime = value + this.setState({defaultDwellTime: value}) updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) }} /> diff --git a/src/main/client/editor/components/pattern/PatternStopContainer.js b/src/main/client/editor/components/pattern/PatternStopContainer.js index a80fa1052..d797ca89e 100644 --- a/src/main/client/editor/components/pattern/PatternStopContainer.js +++ b/src/main/client/editor/components/pattern/PatternStopContainer.js @@ -39,6 +39,7 @@ class PatternStopContainer extends Component { } } componentWillReceiveProps (nextProps) { + // Updates cards when pattern stops order changes (does not account for changes to default travel/dwell times) if (nextProps.activePattern.patternStops && !shallowEqual(nextProps.activePattern.patternStops, this.props.activePattern.patternStops)) { this.setState({cards: nextProps.activePattern.patternStops.map(this.addUniqueId)}) } @@ -70,7 +71,6 @@ class PatternStopContainer extends Component { index: cards.indexOf(card) } } - render () { const { connectDropTarget } = this.props const { cards } = this.state @@ -91,7 +91,6 @@ class PatternStopContainer extends Component { id={card.id} index={i} style={this.props.cardStyle} - stopIsActive={false} cumulativeTravelTime={cumulativeTravelTime} stop={stop} activePattern={this.props.activePattern} diff --git a/src/main/client/editor/components/pattern/PatternStopsPanel.js b/src/main/client/editor/components/pattern/PatternStopsPanel.js index af4bcfec5..d3a3ae93b 100644 --- a/src/main/client/editor/components/pattern/PatternStopsPanel.js +++ b/src/main/client/editor/components/pattern/PatternStopsPanel.js @@ -66,7 +66,15 @@ export default class PatternStopsPanel extends Component { } } render () { - const { activePattern } = this.props + const { + activePattern, + updateEditSetting, + editSettings, + mapState, + stops, + updateActiveEntity, + saveActiveEntity + } = this.props const cardStyle = { border: '1px dashed gray', padding: '0.5rem 0.5rem', @@ -81,17 +89,17 @@ export default class PatternStopsPanel extends Component { className='pull-right' > - {this.props.editSettings.addStops && this.props.mapState.zoom <= 14 + {editSettings.addStops && mapState.zoom <= 14 ? Zoom to view stops : null } @@ -106,19 +114,19 @@ export default class PatternStopsPanel extends Component {
    + updateActiveEntity={updateActiveEntity} + saveActiveEntity={saveActiveEntity} + editSettings={editSettings} + updateEditSetting={updateEditSetting} /> {/* Add stop selector */} - {this.props.editSettings.addStops + {editSettings.addStops ?
    @@ -126,7 +134,7 @@ export default class PatternStopsPanel extends Component { bsSize='small' bsStyle='default' block - onClick={() => this.props.updateEditSetting('addStops', !this.props.editSettings.addStops)} + onClick={() => updateEditSetting('addStops', !editSettings.addStops)} > Cancel @@ -135,7 +143,7 @@ export default class PatternStopsPanel extends Component { :

    this.props.updateEditSetting('addStops', !this.props.editSettings.addStops)} + onClick={() => updateEditSetting('addStops', !editSettings.addStops)} className='small' > Add stop diff --git a/src/main/client/editor/components/pattern/TripPatternList.js b/src/main/client/editor/components/pattern/TripPatternList.js index 777b4c890..644f63457 100644 --- a/src/main/client/editor/components/pattern/TripPatternList.js +++ b/src/main/client/editor/components/pattern/TripPatternList.js @@ -33,8 +33,6 @@ export default class TripPatternList extends Component { subEntityId: PropTypes.string, currentPattern: PropTypes.object } - componentWillReceiveProps (nextProps) { - } shouldComponentUpdate (nextProps) { return true } From 8ba7c4336ab9dc4c28924066969afb6a53f69894 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 30 Jan 2017 14:23:32 -0500 Subject: [PATCH 241/323] docs(deployment): update docs for mastarm --- docs/dev/deployment.md | 11 +++++++---- docs/dev/development.md | 10 ++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/dev/deployment.md b/docs/dev/deployment.md index 02e9f2372..37c6d0abf 100644 --- a/docs/dev/deployment.md +++ b/docs/dev/deployment.md @@ -2,7 +2,7 @@ ## Prerequisites -The application features a Spark-powered Java backend and a Javascript frontend written with React and Redux. To install and deploy the application, you will need Java 8, Maven, Node/npm, and Webpack. +The application features a Spark-powered Java backend and a Javascript frontend written with React and Redux. To install and deploy the application, you will need Java 8, Maven, Node/npm, yarn, and [mastarm](https://github.com/conveyal/mastarm). User athentication is done via [Auth0](http://auth0.com). You will need an Auth0 account and application to use the Data Manager. @@ -101,10 +101,11 @@ Install the Javascript dependencies using yarn: $ yarn ``` -Build the frontend using [mastarm](https://github.com/conveyal/mastarm): +Build and deploy the frontend to s3 using npm script (which calls [mastarm](https://github.com/conveyal/mastarm)): ```bash -$ npm run build +$ npm run deploy -- s3://$S3_BUCKET_NAME/dist +>>>>>>> Stashed changes ``` Package the application using Maven: @@ -119,7 +120,9 @@ Deploy the application with Java: $ java -jar target/datatools.jar ``` -The application should now be accessible at `http://localhost:9000` (or whatever port you specified in `config.yml`). + +The application back-end should now be running at `http://localhost:9000` (or whatever port you specified in `config.yml`). +The front-end assets are pointed to by the back end at whatever s3 bucket name is specified in `config.yml` at `application.assets_bucket`. ## Configuring Modules diff --git a/docs/dev/development.md b/docs/dev/development.md index 98c36686a..873204298 100644 --- a/docs/dev/development.md +++ b/docs/dev/development.md @@ -1,11 +1,13 @@ # Development -## Using `combine-serve` +## mastarm -Spark does not hot-reload static web files, i.e. the application frontend. To make life easier when doing frontend development, we recommend using [combine-serve](https://github.com/conveyal/combine-serve) to serve both the backend and frontend as a unified service. Used in conjunction with `webpack --watch`, this will eliminate the need to constantly rebuild/reload the frontend for testing. +We use Conveyal's front-end JS tool-belt [`mastarm`](https://github.com/conveyal/mastarm) to build, run, and lint while developing. -For example, if running the Java backend on port 9000 (typically via an IDE such as IntelliJ), and you want to serve the combined application on port 9001 for development purposes, use: +To kick off a development server at [http://localhost:9966](http://localhost:9966): ``` -combine-serve --serve / src/main/resources/public/ --proxy / http://localhost:9000 --port 9001 +npm start ``` + +This will use `mastarm` to run a browserify server at the above port along with a proxy for the back-end API, which is assumed to be running on http://localhost:4000. \ No newline at end of file From e9aa5943624fced0f6b6a08babd13297ac79a08f Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Mon, 30 Jan 2017 22:05:33 -0800 Subject: [PATCH 242/323] test(client): add first client-side test --- .travis.yml | 3 +- package.json | 7 +- src/main/client/gtfs/util/__tests__/stats.js | 9 + yarn.lock | 2408 +++++++++--------- 4 files changed, 1168 insertions(+), 1259 deletions(-) create mode 100644 src/main/client/gtfs/util/__tests__/stats.js diff --git a/.travis.yml b/.travis.yml index 8311da239..b013c8bc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,8 @@ script: - nvm use $NODE_VERSION - mvn clean verify - yarn - - yarn test -- --coverage + - yarn run lint + - yarn run cover-client - codecov - yarn run build diff --git a/package.json b/package.json index 12c08ab57..a2bced37a 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,13 @@ "license": "MIT", "scripts": { "build": "mastarm build --env production", + "cover-client": "npm run test-client -- --coverage --coverage-paths \"src/main/client/**/*.js\"", "deploy": "mastarm build --minify --env production && aws s3 cp --recursive --acl public-read dist", + "lint": "mastarm lint \"src/main/client/**/*.js\"", "prestart": "yarn", "start": "mastarm build --serve --proxy http://localhost:4000/api", - "test": "mastarm lint \"src/main/client/**/*.js\"" + "test": "npm run lint && npm run test-client", + "test-client": "mastarm test" }, "dependencies": { "@conveyal/woonerf": "^0.2.0", @@ -89,7 +92,7 @@ "validator": "^5.5.0" }, "devDependencies": { - "mastarm": "^3.1.0" + "mastarm": "^3.3.0" }, "standard": { "parser": "babel-eslint" diff --git a/src/main/client/gtfs/util/__tests__/stats.js b/src/main/client/gtfs/util/__tests__/stats.js new file mode 100644 index 000000000..c0d4ad088 --- /dev/null +++ b/src/main/client/gtfs/util/__tests__/stats.js @@ -0,0 +1,9 @@ +/* globals describe, expect, it */ + +import * as stats from '../stats' + +describe('gtfs > util > stats', () => { + it('formatSpeed should work', () => { + expect(stats.formatSpeed(123)).toEqual(275) + }) +}) diff --git a/yarn.lock b/yarn.lock index 5f91cd0f4..b93a6d7b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37,8 +37,8 @@ Base64@~0.1.3: resolved "https://registry.yarnpkg.com/Base64/-/Base64-0.1.4.tgz#e9f6c6bef567fd635ea4162ab14dd329e74aa6de" JSONStream@^1.0.3: - version "1.2.1" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.2.1.tgz#32aa5790e799481083b49b4b7fa94e23bae69bf9" + version "1.3.0" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.0.tgz#680ab9ac6572a8a1a207e0b38721db1c77b215e5" dependencies: jsonparse "^1.2.0" through ">=2.2.7 <3" @@ -47,7 +47,7 @@ abab@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.3.tgz#b81de5f7274ec4e756d797cd834f303642724e5d" -abbrev@1, abbrev@1.0.x: +abbrev@1: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -76,8 +76,8 @@ acorn@^3.0.4, acorn@^3.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" acorn@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.3.tgz#1a3e850b428e73ba6b09d1cc527f5aaad4d03ef1" + version "4.0.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.4.tgz#17a8d6a7a6c4ef538b814ec9abac2779293bf30a" add-dom-event-listener@1.x: version "1.0.1" @@ -86,12 +86,12 @@ add-dom-event-listener@1.x: object-assign "4.x" ajv-keywords@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.1.1.tgz#02550bc605a3e576041565628af972e06c549d50" + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" ajv@^4.7.0: - version "4.8.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.8.2.tgz#65486936ca36fea39a1504332a78bebd5d447bdc" + version "4.11.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.2.tgz#f166c3c11cbc6cb9dcc102a5bcfe5b72c95287e6" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -136,6 +136,10 @@ ansicolors@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" +any-promise@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-0.1.0.tgz#830b680aa7e56f33451d4b049f3bd8044498ee27" + anymatch@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" @@ -143,9 +147,11 @@ anymatch@^1.3.0: arrify "^1.0.0" micromatch "^2.1.5" -append-transform@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.3.0.tgz#d6933ce4a85f09445d9ccc4cc119051b7381a813" +append-transform@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" + dependencies: + default-require-extensions "^1.0.0" aproba@^1.0.3: version "1.0.4" @@ -181,10 +187,6 @@ arr-flatten@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" @@ -242,8 +244,8 @@ asap@^2.0.3, asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" asn1.js@^4.0.0: - version "4.8.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.8.1.tgz#3949b7f5fd1e8bedc13be3abebf477f93490c810" + version "4.9.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40" dependencies: bn.js "^4.0.0" inherits "^2.0.1" @@ -261,9 +263,9 @@ assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" -assert@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.3.0.tgz#03939a622582a812cc202320a0b9a56c9b815849" +assert@^1.4.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" dependencies: util "0.10.3" @@ -281,10 +283,16 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" -async@1.x, async@^1.4.0, async@^1.4.2: +async@^1.4.0, async@^1.4.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" +async@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.1.4.tgz#2d2160c7788032e4dd6cbe2502f1f9a2c8f6cde4" + dependencies: + lodash "^4.14.0" + async@~0.2.6: version "0.2.10" resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" @@ -374,19 +382,19 @@ autolinker@~0.15.0: resolved "https://registry.yarnpkg.com/autolinker/-/autolinker-0.15.3.tgz#342417d8f2f3461b14cf09088d5edf8791dc9832" autoprefixer@^6.0.2: - version "6.5.1" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.5.1.tgz#ae759a5221e709f3da17c2d656230e67c43cbb75" + version "6.7.1" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.1.tgz#d14d0842f6ef90741cfb2b1e8152a04e83b39ed2" dependencies: - browserslist "~1.4.0" - caniuse-db "^1.0.30000554" + browserslist "^1.7.1" + caniuse-db "^1.0.30000617" normalize-range "^0.1.2" num2fraction "^1.2.2" - postcss "^5.2.4" + postcss "^5.2.11" postcss-value-parser "^3.2.3" aws-sdk@^2.4.2: - version "2.6.13" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.6.13.tgz#14860439ce40497e4b08cfd363d76777967dbaef" + version "2.9.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.9.0.tgz#f258dcc295b1e7eca49d3624abfbf5f7d644172c" dependencies: buffer "4.9.1" crypto-browserify "1.0.9" @@ -394,6 +402,7 @@ aws-sdk@^2.4.2: querystring "0.2.0" sax "1.1.5" url "0.10.3" + uuid "3.0.0" xml2js "0.4.15" xmlbuilder "2.6.2" @@ -413,7 +422,15 @@ babel-code-frame@^6.16.0: esutils "^2.0.2" js-tokens "^2.0.0" -babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.10.4, babel-core@^6.11.4, babel-core@^6.18.0, babel-core@^6.9.0: +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.10.4, babel-core@^6.18.0: version "6.18.2" resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.18.2.tgz#d8bb14dd6986fa4f3566a26ceda3964fa0e04e5b" dependencies: @@ -438,12 +455,13 @@ babel-core@^6.0.0, babel-core@^6.0.14, babel-core@^6.10.4, babel-core@^6.11.4, b source-map "^0.5.0" babel-eslint@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.0.tgz#d506a5174ba224e25a2d17e128e2ba8987139ddc" + version "7.1.1" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-7.1.1.tgz#8a6a884f085aa7060af69cfc77341c2f99370fb2" dependencies: + babel-code-frame "^6.16.0" babel-traverse "^6.15.0" babel-types "^6.15.0" - babylon "^6.11.2" + babylon "^6.13.0" lodash.pickby "^4.6.0" babel-generator@^6.18.0: @@ -458,125 +476,125 @@ babel-generator@^6.18.0: lodash "^4.2.0" source-map "^0.5.0" -babel-helper-bindify-decorators@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.18.0.tgz#fc00c573676a6e702fffa00019580892ec8780a5" +babel-helper-bindify-decorators@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.22.0.tgz#d7f5bc261275941ac62acfc4e20dacfb8a3fe952" dependencies: - babel-runtime "^6.0.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-builder-binary-assignment-operator-visitor@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.18.0.tgz#8ae814989f7a53682152e3401a04fabd0bb333a6" +babel-helper-builder-binary-assignment-operator-visitor@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.22.0.tgz#29df56be144d81bdeac08262bfa41d2c5e91cdcd" dependencies: - babel-helper-explode-assignable-expression "^6.18.0" - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-helper-explode-assignable-expression "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-builder-react-jsx@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.18.0.tgz#ab02f19a2eb7ace936dd87fa55896d02be59bf71" +babel-helper-builder-react-jsx@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.22.0.tgz#aafb31913e47761fd4d0b6987756a144a65fca0d" dependencies: - babel-runtime "^6.9.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" esutils "^2.0.0" lodash "^4.2.0" -babel-helper-call-delegate@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.18.0.tgz#05b14aafa430884b034097ef29e9f067ea4133bd" +babel-helper-call-delegate@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" dependencies: - babel-helper-hoist-variables "^6.18.0" - babel-runtime "^6.0.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-define-map@^6.18.0, babel-helper-define-map@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.18.0.tgz#8d6c85dc7fbb4c19be3de40474d18e97c3676ec2" +babel-helper-define-map@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.22.0.tgz#9544e9502b2d6dfe7d00ff60e82bd5a7a89e95b7" dependencies: - babel-helper-function-name "^6.18.0" - babel-runtime "^6.9.0" - babel-types "^6.18.0" + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" lodash "^4.2.0" -babel-helper-explode-assignable-expression@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.18.0.tgz#14b8e8c2d03ad735d4b20f1840b24cd1f65239fe" +babel-helper-explode-assignable-expression@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.22.0.tgz#c97bf76eed3e0bae4048121f2b9dae1a4e7d0478" dependencies: - babel-runtime "^6.0.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-explode-class@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.18.0.tgz#c44f76f4fa23b9c5d607cbac5d4115e7a76f62cb" +babel-helper-explode-class@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.22.0.tgz#646304924aa6388a516843ba7f1855ef8dfeb69b" dependencies: - babel-helper-bindify-decorators "^6.18.0" - babel-runtime "^6.0.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-bindify-decorators "^6.22.0" + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-function-name@^6.18.0, babel-helper-function-name@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.18.0.tgz#68ec71aeba1f3e28b2a6f0730190b754a9bf30e6" +babel-helper-function-name@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.22.0.tgz#51f1bdc4bb89b15f57a9b249f33d742816dcbefc" dependencies: - babel-helper-get-function-arity "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-get-function-arity@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.18.0.tgz#a5b19695fd3f9cdfc328398b47dafcd7094f9f24" +babel-helper-get-function-arity@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-hoist-variables@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.18.0.tgz#a835b5ab8b46d6de9babefae4d98ea41e866b82a" +babel-helper-hoist-variables@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-optimise-call-expression@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.18.0.tgz#9261d0299ee1a4f08a6dd28b7b7c777348fd8f0f" +babel-helper-optimise-call-expression@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.22.0.tgz#f8d5d4b40a6e2605a6a7f9d537b581bea3756d15" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" -babel-helper-regex@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.18.0.tgz#ae0ebfd77de86cb2f1af258e2cc20b5fe893ecc6" +babel-helper-regex@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz#79f532be1647b1f0ee3474b5f5c3da58001d247d" dependencies: - babel-runtime "^6.9.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" lodash "^4.2.0" -babel-helper-remap-async-to-generator@^6.16.0, babel-helper-remap-async-to-generator@^6.16.2: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.18.0.tgz#336cdf3cab650bb191b02fc16a3708e7be7f9ce5" +babel-helper-remap-async-to-generator@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz#2186ae73278ed03b8b15ced089609da981053383" dependencies: - babel-helper-function-name "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.16.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" -babel-helper-replace-supers@^6.18.0, babel-helper-replace-supers@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.18.0.tgz#28ec69877be4144dbd64f4cc3a337e89f29a924e" +babel-helper-replace-supers@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.22.0.tgz#1fcee2270657548908c34db16bcc345f9850cf42" dependencies: - babel-helper-optimise-call-expression "^6.18.0" - babel-messages "^6.8.0" - babel-runtime "^6.0.0" - babel-template "^6.16.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-optimise-call-expression "^6.22.0" + babel-messages "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" babel-helpers@^6.16.0: version "6.16.0" @@ -593,6 +611,28 @@ babel-jest@^16.0.0: babel-plugin-istanbul "^2.0.0" babel-preset-jest "^16.0.0" +babel-jest@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-17.0.2.tgz#8d51e0d03759713c331f108eb0b2eaa4c6efff74" + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^2.0.0" + babel-preset-jest "^17.0.2" + +babel-jest@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-18.0.0.tgz#17ebba8cb3285c906d859e8707e4e79795fb65e3" + dependencies: + babel-core "^6.0.0" + babel-plugin-istanbul "^3.0.0" + babel-preset-jest "^18.0.0" + +babel-messages@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.22.0.tgz#36066a214f1217e4ed4164867669ecb39e3ea575" + dependencies: + babel-runtime "^6.22.0" + babel-messages@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.8.0.tgz#bf504736ca967e6d65ef0adb5a2a5f947c8e0eb9" @@ -604,10 +644,10 @@ babel-plugin-add-module-exports@^0.2.1: resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz#9ae9a1f4a8dc67f0cdec4f4aeda1e43a5ff65e25" babel-plugin-check-es2015-constants@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz#dbf024c32ed37bfda8dee1e76da02386a8d26fe7" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-istanbul@^2.0.0: version "2.0.3" @@ -618,10 +658,27 @@ babel-plugin-istanbul@^2.0.0: object-assign "^4.1.0" test-exclude "^2.1.1" +babel-plugin-istanbul@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-3.1.2.tgz#11d5abde18425ec24b5d648c7e0b5d25cd354a22" + dependencies: + find-up "^1.1.2" + istanbul-lib-instrument "^1.4.2" + object-assign "^4.1.0" + test-exclude "^3.3.0" + babel-plugin-jest-hoist@^16.0.0: version "16.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-16.0.0.tgz#b58ca3f770982a7e7c25b5614b2e57e9dafc6e76" +babel-plugin-jest-hoist@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-17.0.2.tgz#213488ce825990acd4c30f887dca09fffeb45235" + +babel-plugin-jest-hoist@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-18.0.0.tgz#4150e70ecab560e6e7344adc849498072d34e12a" + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -658,305 +715,281 @@ babel-plugin-syntax-object-rest-spread@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" -babel-plugin-syntax-trailing-function-commas@^6.13.0, babel-plugin-syntax-trailing-function-commas@^6.3.13: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.13.0.tgz#2b84b7d53dd744f94ff1fad7669406274b23f541" +babel-plugin-syntax-trailing-function-commas@^6.13.0, babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" -babel-plugin-transform-async-generator-functions@^6.17.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.17.0.tgz#d0b5a2b2f0940f2b245fa20a00519ed7bc6cae54" +babel-plugin-transform-async-generator-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.22.0.tgz#a720a98153a7596f204099cd5409f4b3c05bab46" dependencies: - babel-helper-remap-async-to-generator "^6.16.2" + babel-helper-remap-async-to-generator "^6.22.0" babel-plugin-syntax-async-generators "^6.5.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-async-to-generator@^6.16.0, babel-plugin-transform-async-to-generator@^6.8.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.16.0.tgz#19ec36cb1486b59f9f468adfa42ce13908ca2999" +babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.8.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz#194b6938ec195ad36efc4c33a971acf00d8cd35e" dependencies: - babel-helper-remap-async-to-generator "^6.16.0" + babel-helper-remap-async-to-generator "^6.22.0" babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.0.0" - -babel-plugin-transform-cjs-system-require@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-cjs-system-require/-/babel-plugin-transform-cjs-system-require-0.1.1.tgz#ffef26d31bc270e82bdbbd437db2777e85162a29" - -babel-plugin-transform-cjs-system-wrapper@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-cjs-system-wrapper/-/babel-plugin-transform-cjs-system-wrapper-0.2.1.tgz#e855078877b56d4d1b92b9f91b37f599db0200e3" - dependencies: - babel-plugin-transform-cjs-system-require "^0.1.1" - babel-template "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-class-properties@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.18.0.tgz#bc1266a39d4c8726e0bd7b15c56235177e6ede57" +babel-plugin-transform-class-properties@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.22.0.tgz#aa78f8134495c7de06c097118ba061844e1dc1d8" dependencies: - babel-helper-function-name "^6.18.0" + babel-helper-function-name "^6.22.0" babel-plugin-syntax-class-properties "^6.8.0" - babel-runtime "^6.9.1" + babel-runtime "^6.22.0" + babel-template "^6.22.0" -babel-plugin-transform-decorators@^6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.13.0.tgz#82d65c1470ae83e2d13eebecb0a1c2476d62da9d" +babel-plugin-transform-decorators@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.22.0.tgz#c03635b27a23b23b7224f49232c237a73988d27c" dependencies: - babel-helper-define-map "^6.8.0" - babel-helper-explode-class "^6.8.0" + babel-helper-explode-class "^6.22.0" babel-plugin-syntax-decorators "^6.13.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" - babel-types "^6.13.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-arrow-functions@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.8.0.tgz#5b63afc3181bdc9a8c4d481b5a4f3f7d7fef3d9d" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-block-scoped-functions@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.8.0.tgz#ed95d629c4b5a71ae29682b998f70d9833eb366d" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-block-scoping@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.18.0.tgz#3bfdcfec318d46df22525cdea88f1978813653af" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.22.0.tgz#00d6e3a0bebdcfe7536b9d653b44a9141e63e47e" dependencies: - babel-runtime "^6.9.0" - babel-template "^6.15.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" lodash "^4.2.0" babel-plugin-transform-es2015-classes@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.18.0.tgz#ffe7a17321bf83e494dcda0ae3fc72df48ffd1d9" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.22.0.tgz#54d44998fd823d9dca15292324161c331c1b6f14" dependencies: - babel-helper-define-map "^6.18.0" - babel-helper-function-name "^6.18.0" - babel-helper-optimise-call-expression "^6.18.0" - babel-helper-replace-supers "^6.18.0" - babel-messages "^6.8.0" - babel-runtime "^6.9.0" - babel-template "^6.14.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-define-map "^6.22.0" + babel-helper-function-name "^6.22.0" + babel-helper-optimise-call-expression "^6.22.0" + babel-helper-replace-supers "^6.22.0" + babel-messages "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-computed-properties@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.8.0.tgz#f51010fd61b3bd7b6b60a5fdfd307bb7a5279870" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz#7c383e9629bba4820c11b0425bdd6290f7f057e7" dependencies: - babel-helper-define-map "^6.8.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" babel-plugin-transform-es2015-destructuring@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.18.0.tgz#a08fb89415ab82058649558bedb7bf8dafa76ba5" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.22.0.tgz#8e0af2f885a0b2cf999d47c4c1dd23ce88cfa4c6" dependencies: - babel-runtime "^6.9.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-duplicate-keys@^6.6.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.8.0.tgz#fd8f7f7171fc108cc1c70c3164b9f15a81c25f7d" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz#672397031c21610d72dd2bbb0ba9fb6277e1c36b" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.8.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-for-of@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.18.0.tgz#4c517504db64bf8cfc119a6b8f177211f2028a70" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.22.0.tgz#180467ad63aeea592a1caeee4bf1c8b3e2616265" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-function-name@^6.3.13: - version "6.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.9.0.tgz#8c135b17dbd064e5bba56ec511baaee2fca82719" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" dependencies: - babel-helper-function-name "^6.8.0" - babel-runtime "^6.9.0" - babel-types "^6.9.0" + babel-helper-function-name "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-literals@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.8.0.tgz#50aa2e5c7958fc2ab25d74ec117e0cc98f046468" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-es2015-modules-amd@^6.18.0, babel-plugin-transform-es2015-modules-amd@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.18.0.tgz#49a054cbb762bdf9ae2d8a807076cfade6141e40" +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.8.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz#bf69cd34889a41c33d90dfb740e0091ccff52f21" dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" + babel-plugin-transform-es2015-modules-commonjs "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" -babel-plugin-transform-es2015-modules-commonjs@^6.18.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.18.0.tgz#c15ae5bb11b32a0abdcc98a5837baa4ee8d67bcc" +babel-plugin-transform-es2015-modules-commonjs@^6.22.0, babel-plugin-transform-es2015-modules-commonjs@^6.6.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.22.0.tgz#6ca04e22b8e214fb50169730657e7a07dc941145" dependencies: - babel-plugin-transform-strict-mode "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.16.0" - babel-types "^6.18.0" + babel-plugin-transform-strict-mode "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-types "^6.22.0" -babel-plugin-transform-es2015-modules-systemjs@^6.12.0, babel-plugin-transform-es2015-modules-systemjs@^6.6.5: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.18.0.tgz#f09294707163edae4d3b3e8bfacecd01d920b7ad" +babel-plugin-transform-es2015-modules-systemjs@^6.12.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.22.0.tgz#810cd0cd025a08383b84236b92c6e31f88e644ad" dependencies: - babel-helper-hoist-variables "^6.18.0" - babel-runtime "^6.11.6" - babel-template "^6.14.0" + babel-helper-hoist-variables "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" babel-plugin-transform-es2015-modules-umd@^6.12.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.18.0.tgz#23351770ece5c1f8e83ed67cb1d7992884491e50" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.22.0.tgz#60d0ba3bd23258719c64391d9bf492d648dc0fae" dependencies: - babel-plugin-transform-es2015-modules-amd "^6.18.0" - babel-runtime "^6.0.0" - babel-template "^6.8.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" babel-plugin-transform-es2015-object-super@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.8.0.tgz#1b858740a5a4400887c23dcff6f4d56eea4a24c5" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz#daa60e114a042ea769dd53fe528fc82311eb98fc" dependencies: - babel-helper-replace-supers "^6.8.0" - babel-runtime "^6.0.0" + babel-helper-replace-supers "^6.22.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-parameters@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.18.0.tgz#9b2cfe238c549f1635ba27fc1daa858be70608b1" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.22.0.tgz#57076069232019094f27da8c68bb7162fe208dbb" dependencies: - babel-helper-call-delegate "^6.18.0" - babel-helper-get-function-arity "^6.18.0" - babel-runtime "^6.9.0" - babel-template "^6.16.0" - babel-traverse "^6.18.0" - babel-types "^6.18.0" + babel-helper-call-delegate "^6.22.0" + babel-helper-get-function-arity "^6.22.0" + babel-runtime "^6.22.0" + babel-template "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-shorthand-properties@^6.3.13: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.18.0.tgz#e2ede3b7df47bf980151926534d1dd0cbea58f43" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz#8ba776e0affaa60bff21e921403b8a652a2ff723" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-spread@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.8.0.tgz#0217f737e3b821fa5a669f187c6ed59205f05e9c" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-sticky-regex@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.8.0.tgz#e73d300a440a35d5c64f5c2a344dc236e3df47be" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz#ab316829e866ee3f4b9eb96939757d19a5bc4593" dependencies: - babel-helper-regex "^6.8.0" - babel-runtime "^6.0.0" - babel-types "^6.8.0" + babel-helper-regex "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" babel-plugin-transform-es2015-template-literals@^6.6.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.8.0.tgz#86eb876d0a2c635da4ec048b4f7de9dfc897e66b" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-typeof-symbol@^6.6.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.18.0.tgz#0b14c48629c90ff47a0650077f6aa699bee35798" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.22.0.tgz#87faf2336d3b6a97f68c4d906b0cd0edeae676e1" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-es2015-unicode-regex@^6.3.13: - version "6.11.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.11.0.tgz#6298ceabaad88d50a3f4f392d8de997260f6ef2c" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz#8d9cc27e7ee1decfe65454fb986452a04a613d20" dependencies: - babel-helper-regex "^6.8.0" - babel-runtime "^6.0.0" + babel-helper-regex "^6.22.0" + babel-runtime "^6.22.0" regexpu-core "^2.0.0" -babel-plugin-transform-exponentiation-operator@^6.3.13, babel-plugin-transform-exponentiation-operator@^6.8.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.8.0.tgz#db25742e9339eade676ca9acec46f955599a68a4" +babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.8.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.22.0.tgz#d57c8335281918e54ef053118ce6eb108468084d" dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.8.0" + babel-helper-builder-binary-assignment-operator-visitor "^6.22.0" babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-flow-strip-types@^6.3.13: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.18.0.tgz#4d3e642158661e9b40db457c004a30817fa32592" +babel-plugin-transform-flow-strip-types@^6.18.0, babel-plugin-transform-flow-strip-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" dependencies: babel-plugin-syntax-flow "^6.18.0" - babel-runtime "^6.0.0" - -babel-plugin-transform-global-system-wrapper@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-global-system-wrapper/-/babel-plugin-transform-global-system-wrapper-0.0.1.tgz#afb469cec0e04689b9fe7e8b1fd280fc94a6d8f2" - dependencies: - babel-template "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-object-rest-spread@^6.16.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.16.0.tgz#db441d56fffc1999052fdebe2e2f25ebd28e36a9" +babel-plugin-transform-object-rest-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.22.0.tgz#1d419b55e68d2e4f64a5ff3373bd67d73c8e83bc" dependencies: babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-react-display-name@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.8.0.tgz#f7a084977383d728bdbdc2835bba0159577f660e" +babel-plugin-transform-react-display-name@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.22.0.tgz#077197520fa8562b8d3da4c3c4b0b1bdd7853f26" dependencies: - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" -babel-plugin-transform-react-jsx-self@^6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.11.0.tgz#605c9450c1429f97a930f7e1dfe3f0d9d0dbd0f4" +babel-plugin-transform-react-jsx-self@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" dependencies: babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-react-jsx-source@^6.3.13: - version "6.9.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.9.0.tgz#af684a05c2067a86e0957d4f343295ccf5dccf00" +babel-plugin-transform-react-jsx-source@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" dependencies: babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-react-jsx@^6.3.13: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.8.0.tgz#94759942f70af18c617189aa7f3593f1644a71ab" +babel-plugin-transform-react-jsx@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.22.0.tgz#48556b7dd4c3fe97d1c943bcd54fc3f2561c1817" dependencies: - babel-helper-builder-react-jsx "^6.8.0" + babel-helper-builder-react-jsx "^6.22.0" babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.0.0" + babel-runtime "^6.22.0" babel-plugin-transform-regenerator@^6.6.0: - version "6.16.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.16.1.tgz#a75de6b048a14154aae14b0122756c5bed392f59" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz#65740593a319c44522157538d690b84094617ea6" dependencies: - babel-runtime "^6.9.0" - babel-types "^6.16.0" - private "~0.1.5" + regenerator-transform "0.9.8" babel-plugin-transform-runtime@^6.9.0: - version "6.15.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.15.0.tgz#3d75b4d949ad81af157570273846fb59aeb0d57c" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.22.0.tgz#10968d760bbf6517243081eec778e10fa828551c" dependencies: - babel-runtime "^6.9.0" + babel-runtime "^6.22.0" -babel-plugin-transform-strict-mode@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.18.0.tgz#df7cf2991fe046f44163dcd110d5ca43bc652b9d" +babel-plugin-transform-strict-mode@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" dependencies: - babel-runtime "^6.0.0" - babel-types "^6.18.0" - -babel-plugin-transform-system-register@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-system-register/-/babel-plugin-transform-system-register-0.0.1.tgz#9dff40390c2763ac518f0b2ad7c5ea4f65a5be25" + babel-runtime "^6.22.0" + babel-types "^6.22.0" babel-polyfill@^6.22.0: version "6.22.0" @@ -966,9 +999,9 @@ babel-polyfill@^6.22.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-preset-env@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-0.0.6.tgz#cda63a020069098fad12272a7a447a7c5bafb3c8" +babel-preset-env@^1.1.0: + version "1.1.8" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.1.8.tgz#c46734c6233c3f87d177513773db3cf3c1758aaa" dependencies: babel-plugin-check-es2015-constants "^6.3.13" babel-plugin-syntax-trailing-function-commas "^6.13.0" @@ -1005,36 +1038,48 @@ babel-preset-jest@^16.0.0: dependencies: babel-plugin-jest-hoist "^16.0.0" +babel-preset-jest@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-17.0.2.tgz#141e935debe164aaa0364c220d31ccb2176493b2" + dependencies: + babel-plugin-jest-hoist "^17.0.2" + +babel-preset-jest@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-18.0.0.tgz#84faf8ca3ec65aba7d5e3f59bbaed935ab24049e" + dependencies: + babel-plugin-jest-hoist "^18.0.0" + babel-preset-react@^6.5.0: - version "6.16.0" - resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.16.0.tgz#aa117d60de0928607e343c4828906e4661824316" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.22.0.tgz#7bc97e2d73eec4b980fb6b4e4e0884e81ccdc165" dependencies: babel-plugin-syntax-flow "^6.3.13" babel-plugin-syntax-jsx "^6.3.13" - babel-plugin-transform-flow-strip-types "^6.3.13" - babel-plugin-transform-react-display-name "^6.3.13" - babel-plugin-transform-react-jsx "^6.3.13" - babel-plugin-transform-react-jsx-self "^6.11.0" - babel-plugin-transform-react-jsx-source "^6.3.13" + babel-plugin-transform-flow-strip-types "^6.22.0" + babel-plugin-transform-react-display-name "^6.22.0" + babel-plugin-transform-react-jsx "^6.22.0" + babel-plugin-transform-react-jsx-self "^6.22.0" + babel-plugin-transform-react-jsx-source "^6.22.0" babel-preset-stage-2@^6.17.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.18.0.tgz#9eb7bf9a8e91c68260d5ba7500493caaada4b5b5" + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.22.0.tgz#ccd565f19c245cade394b21216df704a73b27c07" dependencies: babel-plugin-syntax-dynamic-import "^6.18.0" - babel-plugin-transform-class-properties "^6.18.0" - babel-plugin-transform-decorators "^6.13.0" - babel-preset-stage-3 "^6.17.0" + babel-plugin-transform-class-properties "^6.22.0" + babel-plugin-transform-decorators "^6.22.0" + babel-preset-stage-3 "^6.22.0" -babel-preset-stage-3@^6.17.0: - version "6.17.0" - resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.17.0.tgz#b6638e46db6e91e3f889013d8ce143917c685e39" +babel-preset-stage-3@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.22.0.tgz#a4e92bbace7456fafdf651d7a7657ee0bbca9c2e" dependencies: - babel-plugin-syntax-trailing-function-commas "^6.3.13" - babel-plugin-transform-async-generator-functions "^6.17.0" - babel-plugin-transform-async-to-generator "^6.16.0" - babel-plugin-transform-exponentiation-operator "^6.3.13" - babel-plugin-transform-object-rest-spread "^6.16.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-generator-functions "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-object-rest-spread "^6.22.0" babel-register@^6.18.0: version "6.18.0" @@ -1061,7 +1106,7 @@ babel-runtime@^5.6.18: dependencies: core-js "^1.0.0" -babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-template@^6.8.0, babel-template@^6.9.0: +babel-template@^6.16.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.16.0.tgz#e149dd1a9f03a35f817ddbc4d0481988e7ebc8ca" dependencies: @@ -1071,6 +1116,16 @@ babel-template@^6.14.0, babel-template@^6.15.0, babel-template@^6.16.0, babel-te babylon "^6.11.0" lodash "^4.2.0" +babel-template@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.22.0.tgz#403d110905a4626b317a2a1fcb8f3b73204b2edb" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.22.0" + babel-types "^6.22.0" + babylon "^6.11.0" + lodash "^4.2.0" + babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.18.0.tgz#5aeaa980baed2a07c8c47329cd90c3b90c80f05e" @@ -1085,7 +1140,21 @@ babel-traverse@^6.15.0, babel-traverse@^6.16.0, babel-traverse@^6.18.0: invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.13.0, babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0, babel-types@^6.8.0, babel-types@^6.9.0: +babel-traverse@^6.22.0: + version "6.22.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.22.1.tgz#3b95cd6b7427d6f1f757704908f2fc9748a5f59f" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.22.0" + babel-runtime "^6.22.0" + babel-types "^6.22.0" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.18.0.tgz#1f7d5a73474c59eb9151b2417bbff4e4fce7c3f8" dependencies: @@ -1094,6 +1163,15 @@ babel-types@^6.13.0, babel-types@^6.15.0, babel-types@^6.16.0, babel-types@^6.18 lodash "^4.2.0" to-fast-properties "^1.0.1" +babel-types@^6.19.0, babel-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.22.0.tgz#2a447e8d0ea25d2512409e4175479fd78cc8b1db" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + babelify@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/babelify/-/babelify-7.3.0.tgz#aa56aede7067fd7bd549666ee16dc285087e88e5" @@ -1101,10 +1179,14 @@ babelify@^7.3.0: babel-core "^6.0.14" object-assign "^4.0.0" -babylon@^6.11.0, babylon@^6.11.2, babylon@^6.13.0: +babylon@^6.11.0, babylon@^6.13.0: version "6.13.1" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.13.1.tgz#adca350e088f0467647157652bafead6ddb8dfdb" +babylon@^6.15.0: + version "6.15.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.15.0.tgz#ba65cfa1a80e1759b0e89fb562e27dccae70348e" + balanced-match@0.1.0, balanced-match@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" @@ -1136,14 +1218,8 @@ bean@~1.0.4: resolved "https://registry.yarnpkg.com/bean/-/bean-1.0.15.tgz#b4a9fff82618b071471676c8b190868048c34775" binary-extensions@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.7.0.tgz#6c1610db163abfb34edfe42fa423343a1e01185d" - -bl@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" - dependencies: - readable-stream "~2.0.5" + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" block-stream@*: version "0.0.9" @@ -1151,10 +1227,6 @@ block-stream@*: dependencies: inherits "~2.0.0" -bluebird@^3.0.5, bluebird@^3.3.4: - version "3.4.6" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.6.tgz#01da8d821d87813d158967e743d5fe6c62cf8c0f" - blueimp-md5@2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.3.1.tgz#992a6737733b9da1edd641550dc3acab2e9cfc5a" @@ -1252,8 +1324,8 @@ brorand@^1.0.1: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.6.tgz#4028706b915f91f7b349a2e0bf3c376039d216e5" browser-pack@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.1.tgz#779887c792eaa1f64a46a22c8f1051cdcd96755f" + version "6.0.2" + resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531" dependencies: JSONStream "^1.0.3" combine-source-map "~0.7.1" @@ -1327,16 +1399,68 @@ browserify-zlib@~0.1.2: dependencies: pako "~0.2.0" -browserify@^13.0.0, browserify@^13.0.1: - version "13.1.1" - resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.1.1.tgz#72a2310e2f706ed87db929cf0ee73a5e195d9bb0" +browserify@^13.0.1: + version "13.3.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-13.3.0.tgz#b5a9c9020243f0c70e4675bec8223bc627e415ce" + dependencies: + JSONStream "^1.0.3" + assert "^1.4.0" + browser-pack "^6.0.1" + browser-resolve "^1.11.0" + browserify-zlib "~0.1.2" + buffer "^4.1.0" + cached-path-relative "^1.0.0" + concat-stream "~1.5.1" + console-browserify "^1.1.0" + constants-browserify "~1.0.0" + crypto-browserify "^3.0.0" + defined "^1.0.0" + deps-sort "^2.0.0" + domain-browser "~1.1.0" + duplexer2 "~0.1.2" + events "~1.1.0" + glob "^7.1.0" + has "^1.0.0" + htmlescape "^1.1.0" + https-browserify "~0.0.0" + inherits "~2.0.1" + insert-module-globals "^7.0.0" + labeled-stream-splicer "^2.0.0" + module-deps "^4.0.8" + os-browserify "~0.1.1" + parents "^1.0.1" + path-browserify "~0.0.0" + process "~0.11.0" + punycode "^1.3.2" + querystring-es3 "~0.2.0" + read-only-stream "^2.0.0" + readable-stream "^2.0.2" + resolve "^1.1.4" + shasum "^1.0.0" + shell-quote "^1.6.1" + stream-browserify "^2.0.0" + stream-http "^2.0.0" + string_decoder "~0.10.0" + subarg "^1.0.0" + syntax-error "^1.1.1" + through2 "^2.0.0" + timers-browserify "^1.0.1" + tty-browserify "~0.0.0" + url "~0.11.0" + util "~0.10.1" + vm-browserify "~0.0.1" + xtend "^4.0.0" + +browserify@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/browserify/-/browserify-14.0.0.tgz#67e6cfe7acb2fb1a1908e8a763452306de0bcf38" dependencies: JSONStream "^1.0.3" - assert "~1.3.0" + assert "^1.4.0" browser-pack "^6.0.1" browser-resolve "^1.11.0" browserify-zlib "~0.1.2" - buffer "^4.1.0" + buffer "^5.0.2" cached-path-relative "^1.0.0" concat-stream "~1.5.1" console-browserify "^1.1.0" @@ -1347,7 +1471,7 @@ browserify@^13.0.0, browserify@^13.0.1: domain-browser "~1.1.0" duplexer2 "~0.1.2" events "~1.1.0" - glob "^5.0.15" + glob "^7.1.0" has "^1.0.0" htmlescape "^1.1.0" https-browserify "~0.0.0" @@ -1365,7 +1489,7 @@ browserify@^13.0.0, browserify@^13.0.1: readable-stream "^2.0.2" resolve "^1.1.4" shasum "^1.0.0" - shell-quote "^1.4.3" + shell-quote "^1.6.1" stream-browserify "^2.0.0" stream-http "^2.0.0" string_decoder "~0.10.0" @@ -1379,26 +1503,28 @@ browserify@^13.0.0, browserify@^13.0.1: vm-browserify "~0.0.1" xtend "^4.0.0" -browserslist@^1.0.0, browserslist@^1.0.1, browserslist@^1.4.0, browserslist@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.4.0.tgz#9cfdcf5384d9158f5b70da2aa00b30e8ff019049" +browserslist@^1.0.0, browserslist@^1.0.1, browserslist@^1.4.0, browserslist@^1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.1.tgz#cc9bd193979a2a4b09fdb3df6003fefe48ccefe1" dependencies: - caniuse-db "^1.0.30000539" + caniuse-db "^1.0.30000617" + electron-to-chromium "^1.2.1" -bser@^1.0.2: +bser@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169" dependencies: node-int64 "^0.4.0" budo@^9.0.0: - version "9.2.1" - resolved "https://registry.yarnpkg.com/budo/-/budo-9.2.1.tgz#c36c6c5097bd5ad43720cb3ae9d269870b4735ed" + version "9.4.7" + resolved "https://registry.yarnpkg.com/budo/-/budo-9.4.7.tgz#a6cdcf2572c22ed1331ae91f34a07f265b3dd20b" dependencies: bole "^2.0.0" browserify "^13.0.1" chokidar "^1.0.1" connect-pushstate "^1.0.0" + escape-html "^1.0.3" events "^1.0.2" garnish "^5.0.0" get-ports "^1.0.2" @@ -1407,6 +1533,8 @@ budo@^9.0.0: internal-ip "^1.0.1" micromatch "^2.2.0" minimist "^1.1.0" + on-finished "^2.3.0" + on-headers "^1.0.1" once "^1.3.2" opn "^3.0.2" pem "^1.8.3" @@ -1427,10 +1555,6 @@ buffer-equal@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" -buffer-peek-stream@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-peek-stream/-/buffer-peek-stream-1.0.1.tgz#53b47570a1347787c5bad4ca2ca3021f9d8b3cfd" - buffer-shims@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" @@ -1447,13 +1571,20 @@ buffer@4.9.1, buffer@^4.1.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.2.tgz#41d0407ff76782e9ec19f52f88e237ce6bb0de6d" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -builtin-status-codes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-2.0.0.tgz#6f22003baacf003ccd287afe6872151fddc58579" +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" bytes@2.2.0: version "2.2.0" @@ -1467,6 +1598,12 @@ cached-path-relative@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.0.tgz#d1094c577fbd9a8b8bd43c96af6188aa205d05f4" +cachedir@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.1.1.tgz#e1363075ea206a12767d92bb711c8a2f76a10f62" + dependencies: + os-homedir "^1.0.1" + caller-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" @@ -1517,9 +1654,9 @@ caniuse-api@^1.3.2: lodash.uniq "^4.3.0" shelljs "^0.7.0" -caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000539, caniuse-db@^1.0.30000554: - version "1.0.30000572" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000572.tgz#81d0aa6b7de2d785c8dcab135502983276cc707d" +caniuse-db@^1.0.30000346, caniuse-db@^1.0.30000617: + version "1.0.30000617" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000617.tgz#9b7fd81f58a35526315c83e60cb5f076f0beb392" cardinal@^1.0.0: version "1.0.0" @@ -1607,7 +1744,7 @@ cipher-base@^1.0.0, cipher-base@^1.0.1: dependencies: inherits "^2.0.1" -circular-json@^0.3.0: +circular-json@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d" @@ -1663,35 +1800,23 @@ co@^4.6.0: resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" code-point-at@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.0.1.tgz#1104cd34f9b5b45d3eba88f1babc1924e1ce35fb" - dependencies: - number-is-nan "^1.0.0" + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -color-convert@0.5.x, color-convert@^0.5.3: +color-convert@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" color-convert@^1.3.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.6.0.tgz#7592755faf53938a05b1ea8e5374cab77d6dd190" + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a" dependencies: color-name "^1.1.1" -color-name@1.0.x, color-name@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.0.1.tgz#6b34b2b29b7716013972b0b9d5bedcfbb6718df8" - -color-name@^1.1.1: +color-name@^1.0.0, color-name@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" -color-string@0.2.x: - version "0.2.4" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.2.4.tgz#221ff64234f71aaa3e13bc8c7e8c95f3cdd8f81a" - dependencies: - color-name "1.0.x" - color-string@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" @@ -1705,7 +1830,7 @@ color@^0.10.1: color-convert "^0.5.3" color-string "^0.3.0" -color@^0.11.0: +color@^0.11.0, color@^0.11.3, color@^0.11.4: version "0.11.4" resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" dependencies: @@ -1713,20 +1838,6 @@ color@^0.11.0: color-convert "^1.3.0" color-string "^0.3.0" -color@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/color/-/color-0.7.3.tgz#ab3ae4bc6cb8cfadb5d749c40f34aea088104f89" - dependencies: - color-convert "0.5.x" - color-string "0.2.x" - -color@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/color/-/color-0.9.0.tgz#91146d460cdd5543ea1fa20aa0b597337509b64d" - dependencies: - color-convert "^0.5.3" - color-string "^0.3.0" - colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -1746,29 +1857,30 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.9.x, commander@^2.5.0, commander@^2.9.0: +commander@^2.5.0, commander@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" dependencies: graceful-readlink ">= 1.0.0" commitizen@^2.8.2: - version "2.8.6" - resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-2.8.6.tgz#578483abbf5b67368d1ccdb9d9d978c74972011b" + version "2.9.5" + resolved "https://registry.yarnpkg.com/commitizen/-/commitizen-2.9.5.tgz#f9605c8c1170eef86331676b5b5f12ab595bf498" dependencies: + cachedir "^1.1.0" chalk "1.1.3" cz-conventional-changelog "1.2.0" dedent "0.6.0" detect-indent "4.0.0" - find-node-modules "1.0.3" + find-node-modules "1.0.4" find-root "1.0.0" - glob "7.0.5" - home-or-tmp "2.0.0" - inquirer "1.1.2" - lodash "4.15.0" + fs-extra "^1.0.0" + glob "7.1.1" + inquirer "1.2.3" + lodash "4.17.2" minimist "1.2.0" path-exists "2.1.0" - shelljs "0.5.3" + shelljs "0.7.5" strip-json-comments "2.0.1" commoner@^0.10.1: @@ -1799,13 +1911,13 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.1, 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" +concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" dependencies: - inherits "~2.0.1" - readable-stream "~2.0.0" - typedarray "~0.0.5" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" concat-stream@~1.4.5: version "1.4.10" @@ -1815,9 +1927,17 @@ concat-stream@~1.4.5: readable-stream "~1.1.9" typedarray "~0.0.5" +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" + dependencies: + inherits "~2.0.1" + readable-stream "~2.0.0" + typedarray "~0.0.5" + connect-pushstate@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/connect-pushstate/-/connect-pushstate-1.0.0.tgz#cdc82173076f01b90bb084983a1f4493d4a28d04" + version "1.1.0" + resolved "https://registry.yarnpkg.com/connect-pushstate/-/connect-pushstate-1.1.0.tgz#bcab224271c439604a0fb0f614c0a5f563e88e24" console-browserify@^1.1.0: version "1.1.0" @@ -1860,7 +1980,7 @@ 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" -core-js@^1.0.0, core-js@^1.2.6: +core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -1946,8 +2066,8 @@ css-color-function@^1.2.0: rgb "~0.1.0" cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" "cssstyle@>= 0.2.36 < 0.3.0": version "0.2.37" @@ -1982,12 +2102,6 @@ cz-conventional-changelog@1.2.0, cz-conventional-changelog@^1.1.6: right-pad "^1.0.1" word-wrap "^1.0.3" -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - d@^0.1.1, d@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" @@ -2000,10 +2114,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-0.0.4.tgz#46e13ab9da8e309745c8d01ce547213ebdb2fe3f" - date-now@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/date-now/-/date-now-1.0.1.tgz#bb7d086438debe4182a485fb3df3fbfb99d6153c" @@ -2068,13 +2178,19 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +default-require-extensions@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8" + dependencies: + strip-bom "^2.0.0" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" -deglob@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/deglob/-/deglob-2.0.0.tgz#dd087aa2971a0b1feeea66483c2c82685c73be86" +deglob@^2.0.0, deglob@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/deglob/-/deglob-2.1.0.tgz#4d44abe16ef32c779b4972bd141a80325029a14a" dependencies: find-root "^1.0.0" glob "^7.0.5" @@ -2147,8 +2263,8 @@ detective@^4.0.0, detective@^4.3.1: defined "^1.0.0" diff@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.0.1.tgz#a52d90cc08956994be00877bff97110062582c35" + version "3.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" diffie-hellman@^5.0.0: version "5.0.2" @@ -2240,6 +2356,10 @@ ejsify@0.1.0: ejs "~0.8.3" through "~2.3.4" +electron-to-chromium@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.2.1.tgz#63ac7579a1c5bedb296c8607621f2efc9a54b968" + element-class@^0.2.0, element-class@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/element-class/-/element-class-0.2.2.tgz#9d3bbd0767f9013ef8e1c8ebe722c1402a60050e" @@ -2263,22 +2383,19 @@ encoding@^0.1.11: dependencies: iconv-lite "~0.4.13" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.1.0.tgz#e9353258baa9108965efc41cb0ef8ade2f3cfb07" - dependencies: - once "~1.3.0" - -envify@^3.0.0, envify@^3.4.1: +envify@^3.0.0: version "3.4.1" resolved "https://registry.yarnpkg.com/envify/-/envify-3.4.1.tgz#d7122329e8df1688ba771b12501917c9ce5cbce8" dependencies: jstransform "^11.0.3" through "~2.3.4" -err-code@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.1.tgz#739d71b6851f24d050ea18c79a5b722420771d59" +envify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/envify/-/envify-4.0.0.tgz#f791343e3d11cc29cce41150300a8af61c66cab0" + dependencies: + esprima "~3.1.0" + through "~2.3.4" "errno@>=0.1.1 <0.2.0-0": version "0.1.4" @@ -2296,7 +2413,7 @@ errorify@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/errorify/-/errorify-0.3.1.tgz#53e0aaeeb18adc3e55f9f1eb4e2d95929f41b79b" -es5-ext@^0.10.12, es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@^0.10.9, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: +es5-ext@^0.10.7, es5-ext@^0.10.8, es5-ext@~0.10.11, es5-ext@~0.10.2, es5-ext@~0.10.7: version "0.10.12" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.12.tgz#aa84641d4db76b62abba5e45fd805ecbab140047" dependencies: @@ -2343,13 +2460,6 @@ es6-symbol@3, es6-symbol@~3.1, es6-symbol@~3.1.0: d "~0.1.1" es5-ext "~0.10.11" -es6-template-strings@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es6-template-strings/-/es6-template-strings-2.0.1.tgz#b166c6a62562f478bb7775f6ca96103a599b4b2c" - dependencies: - es5-ext "^0.10.12" - esniff "^1.1" - es6-weak-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.1.tgz#0d2bbd8827eb5fb4ba8f97fbfea50d43db21ea81" @@ -2359,7 +2469,7 @@ es6-weak-map@^2.0.1: es6-iterator "2" es6-symbol "3" -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -2367,7 +2477,7 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^ version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" -escodegen@1.8.x, escodegen@^1.6.1: +escodegen@^1.6.1: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" dependencies: @@ -2414,25 +2524,26 @@ eslint-config-standard@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-6.2.1.tgz#d3a68aafc7191639e7ee441e7348739026354292" -eslint-plugin-promise@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.3.0.tgz#20a1ef58b4243ffdaef82ee9360a02353a7cca89" +eslint-plugin-promise@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.4.0.tgz#6ba9048c2df57be77d036e0c68918bc9b4fc4195" -eslint-plugin-react@~6.4.1: - version "6.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.4.1.tgz#7d1aade747db15892f71eee1fea4addf97bcfa2b" +eslint-plugin-react@~6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-6.7.1.tgz#1af96aea545856825157d97c1b50d5a8fb64a5a7" dependencies: doctrine "^1.2.2" - jsx-ast-utils "^1.3.1" + jsx-ast-utils "^1.3.3" eslint-plugin-standard@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz#3589699ff9c917f2c25f76a916687f641c369ff3" -eslint@~3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.8.1.tgz#7d02db44cd5aaf4fa7aa489e1f083baa454342ba" +eslint@~3.10.2: + version "3.10.2" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.10.2.tgz#c9a10e8bf6e9d65651204778c503341f1eac3ce7" dependencies: + babel-code-frame "^6.16.0" chalk "^1.1.3" concat-stream "^1.4.6" debug "^2.1.1" @@ -2444,7 +2555,7 @@ eslint@~3.8.1: file-entry-cache "^2.0.0" glob "^7.0.3" globals "^9.2.0" - ignore "^3.1.5" + ignore "^3.2.0" imurmurhash "^0.1.4" inquirer "^0.12.0" is-my-json-valid "^2.10.0" @@ -2460,20 +2571,13 @@ eslint@~3.8.1: pluralize "^1.2.1" progress "^1.1.8" require-uncached "^1.0.2" - shelljs "^0.6.0" + shelljs "^0.7.5" strip-bom "^3.0.0" strip-json-comments "~1.0.1" table "^3.7.8" text-table "~0.2.0" user-home "^2.0.0" -esniff@^1.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/esniff/-/esniff-1.1.0.tgz#c66849229f91464dede2e0d40201ed6abf65f2ac" - dependencies: - d "1" - es5-ext "^0.10.12" - espree@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/espree/-/espree-3.3.2.tgz#dbf3fadeb4ecb4d4778303e50103b3d36c88b89c" @@ -2489,7 +2593,7 @@ esprima-fb@~15001.1001.0-dev-harmony-fb: version "15001.1001.0-dev-harmony-fb" resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" -esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: +esprima@^2.6.0, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -2505,6 +2609,10 @@ esprima@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.0.0.tgz#53cf247acda77313e551c3aa2e73342d3fb4f7d9" +esprima@~3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + esrecurse@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220" @@ -2599,7 +2707,7 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expand-tilde@^1.2.0, expand-tilde@^1.2.1, expand-tilde@^1.2.2: +expand-tilde@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" dependencies: @@ -2613,7 +2721,7 @@ extend@^3.0.0, extend@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" -external-editor@^1.0.1: +external-editor@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-1.1.1.tgz#12d7b0db850f7ff7e7081baf4005700060c4600b" dependencies: @@ -2645,8 +2753,8 @@ fast-bind@^1.0.0: resolved "https://registry.yarnpkg.com/fast-bind/-/fast-bind-1.0.0.tgz#7fa9652cb3325f5cd1e252d6cb4f160de1a76e75" fast-levenshtein@~2.0.4: - version "2.0.5" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.5.tgz#bd33145744519ab1c36c3ee9f31f08e9079b67f2" + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" faye-websocket@~0.10.0: version "0.10.0" @@ -2655,10 +2763,10 @@ faye-websocket@~0.10.0: websocket-driver ">=0.5.1" fb-watchman@^1.8.0, fb-watchman@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.0.tgz#6f268f1f347a6b3c875d1e89da7e1ed79adfc0ec" + version "1.9.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-1.9.2.tgz#a24cf47827f82d38fb59a69ad70b76e3b6ae7383" dependencies: - bser "^1.0.2" + bser "1.0.2" fbjs@0.1.0-alpha.10: version "0.1.0-alpha.10" @@ -2718,12 +2826,12 @@ filename-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" -fileset@0.2.x: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fileset/-/fileset-0.2.1.tgz#588ef8973c6623b2a76df465105696b96aac8067" +fileset@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0" dependencies: - glob "5.x" - minimatch "2.x" + glob "^7.0.3" + minimatch "^3.0.3" fill-range@^2.1.0: version "2.2.3" @@ -2735,11 +2843,11 @@ fill-range@^2.1.0: repeat-element "^1.1.2" repeat-string "^1.5.2" -find-node-modules@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-1.0.3.tgz#36117ea45c13d5d8352f82ba791c2b835d730a14" +find-node-modules@1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/find-node-modules/-/find-node-modules-1.0.4.tgz#b6deb3cccb699c87037677bcede2c5f5862b2550" dependencies: - findup-sync "^0.2.1" + findup-sync "0.4.2" merge "^1.2.0" find-root@1.0.0, find-root@^1.0.0: @@ -2753,42 +2861,20 @@ find-up@^1.0.0, find-up@^1.1.2: path-exists "^2.0.0" pinkie-promise "^2.0.0" -findup-sync@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.2.1.tgz#e0a90a450075c49466ee513732057514b81e878c" - dependencies: - glob "~4.3.0" - -findup-sync@^0.4.2: - version "0.4.3" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" +findup-sync@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.2.tgz#a8117d0f73124f5a4546839579fe52d7129fb5e5" dependencies: detect-file "^0.1.0" is-glob "^2.0.1" micromatch "^2.3.7" resolve-dir "^0.1.0" -fined@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.0.2.tgz#5b28424b760d7598960b7ef8480dff8ad3660e97" - dependencies: - expand-tilde "^1.2.1" - lodash.assignwith "^4.0.7" - lodash.isempty "^4.2.1" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.pick "^4.2.1" - parse-filepath "^1.0.1" - -flagged-respawn@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-0.3.2.tgz#ff191eddcd7088a675b2610fffc976be9b8074b5" - flat-cache@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.1.tgz#6c837d6225a7de5659323740b36d5361f71691ff" + version "1.2.2" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96" dependencies: - circular-json "^0.3.0" + circular-json "^0.3.1" del "^2.0.2" graceful-fs "^4.1.2" write "^0.2.1" @@ -2846,15 +2932,23 @@ from2@^2.0.3: fs-exists-sync@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + resolved "http://registry.npmjs.org/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" + +fs-extra@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0: - version "1.0.14" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.14.tgz#558e8cc38643d8ef40fe45158486d0d25758eee4" + version "1.0.17" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.0.17.tgz#8537f3f12272678765b4fd6528c0f1f66f8f4558" dependencies: nan "^2.3.0" node-pre-gyp "^0.6.29" @@ -2895,18 +2989,18 @@ garnish@^5.0.0: stdout-stream "^1.4.0" url-trim "^1.0.0" -gauge@~2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" +gauge@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.2.tgz#15cecc31b02d05345a5d6b0e171cdb3ad2307774" dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" - has-color "^0.1.7" has-unicode "^2.0.0" object-assign "^4.1.0" signal-exit "^3.0.0" string-width "^1.0.1" strip-ansi "^3.0.1" + supports-color "^0.2.0" wide-align "^1.1.0" generate-function@^2.0.0: @@ -2956,19 +3050,9 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@5.0.x, glob@5.x, glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: - version "7.0.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" +glob@7.1.1, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -2977,9 +3061,9 @@ glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^6.0.1: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" dependencies: inflight "^1.0.4" inherits "2" @@ -2987,15 +3071,6 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@~4.3.0: - version "4.3.5" - resolved "https://registry.yarnpkg.com/glob/-/glob-4.3.5.tgz#80fbb08ca540f238acce5d11d1e9bc41e75173d3" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "^2.0.1" - once "^1.3.0" - global-modules@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" @@ -3004,13 +3079,13 @@ global-modules@^0.2.3: is-windows "^0.2.0" global-prefix@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.4.tgz#05158db1cde2dd491b455e290eb3ab8bfc45c6e1" + version "0.1.5" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" dependencies: + homedir-polyfill "^1.0.0" ini "^1.3.4" is-windows "^0.2.0" - osenv "^0.1.3" - which "^1.2.10" + which "^1.2.12" globals@^9.0.0, globals@^9.2.0: version "9.12.0" @@ -3027,7 +3102,7 @@ globby@^5.0.0: pify "^2.0.0" pinkie-promise "^2.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.9" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.9.tgz#baacba37d19d11f9d146d3578bc99958c3787e29" @@ -3043,9 +3118,9 @@ growly@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" -handlebars@^4.0.1, handlebars@^4.0.3: - version "4.0.5" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.5.tgz#92c6ed6bb164110c50d4d8d0fbddc70806c6f8e7" +handlebars@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -3074,10 +3149,6 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" -has-color@^0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" - has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" @@ -3144,13 +3215,19 @@ hoist-non-react-statics@^1.0.3, hoist-non-react-statics@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" -home-or-tmp@2.0.0, home-or-tmp@^2.0.0: +home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.1" +homedir-polyfill@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" + dependencies: + parse-passwd "^1.0.0" + hosted-git-info@^2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" @@ -3180,9 +3257,17 @@ http-errors@~1.5.0: setprototypeof "1.0.1" statuses ">= 1.3.0 < 2" +http-errors@~1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.5.1.tgz#788c0d2c1de2c81b9e6e8c01843b6b97eb920750" + dependencies: + inherits "2.0.3" + setprototypeof "1.0.2" + statuses ">= 1.3.1 < 2" + http-proxy@^1.14.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31" + version "1.16.2" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" dependencies: eventemitter3 "1.x.x" requires-port "1.x.x" @@ -3207,9 +3292,9 @@ ieee754@^1.1.4: version "1.1.8" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" -ignore@^3.0.9, ignore@^3.1.5: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.0.tgz#8d88f03c3002a0ac52114db25d2c673b0bf1e435" +ignore@^3.0.9, ignore@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410" immediate@~3.0.5: version "3.0.6" @@ -3248,17 +3333,21 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.1, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@2.0.1, inherits@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" +inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" inject-lr-script@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/inject-lr-script/-/inject-lr-script-2.0.0.tgz#99487bbd224ac9c18f69c65a5c6dd6fa4191e903" + version "2.1.0" + resolved "https://registry.yarnpkg.com/inject-lr-script/-/inject-lr-script-2.1.0.tgz#e61b5e84c118733906cbea01ec3d746698a39f65" dependencies: resp-modifier "^6.0.0" @@ -3268,15 +3357,15 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" -inquirer@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.1.2.tgz#ac3ba5f06b8e7291abd9f22912c03f09cfe2dd1f" +inquirer@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918" dependencies: ansi-escapes "^1.1.0" chalk "^1.0.0" cli-cursor "^1.0.1" cli-width "^2.0.0" - external-editor "^1.0.1" + external-editor "^1.1.0" figures "^1.3.5" lodash "^4.3.0" mute-stream "0.0.6" @@ -3338,13 +3427,6 @@ invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" -is-absolute@^0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" - dependencies: - is-relative "^0.2.1" - is-windows "^0.2.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3464,12 +3546,6 @@ is-property@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" -is-relative@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" - dependencies: - is-unc-path "^0.1.1" - is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" @@ -3484,12 +3560,6 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -is-unc-path@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.1.tgz#ab2533d77ad733561124c3dc0f5cd8b90054c86b" - dependencies: - unc-path-regex "^0.1.0" - is-upper-case@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" @@ -3504,10 +3574,6 @@ is-windows@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" -is@~0.2.6: - version "0.2.7" - resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" - isarray@0.0.1, isarray@~0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -3520,6 +3586,10 @@ isexe@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0" +isnumeric@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isnumeric/-/isnumeric-0.2.0.tgz#a2347ba360de19e33d0ffd590fddf7755cbf2e64" + iso-639-1@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-1.2.4.tgz#b0cef68adf904839c7d3e90bea249e79cc9498ee" @@ -3543,34 +3613,33 @@ isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-api@^1.0.0-aplha.10: - version "1.0.0-aplha.10" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.0.0-aplha.10.tgz#902edf5cf5404e0eba7e00ef46408488a0d3e337" +istanbul-api@^1.1.0-alpha.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.1.tgz#d36e2f1560d1a43ce304c4ff7338182de61c8f73" dependencies: - async "1.x" - clone "^1.0.2" - fileset "0.2.x" - istanbul-lib-coverage "^1.0.0-alpha" - istanbul-lib-hook "^1.0.0-alpha" - istanbul-lib-instrument "^1.0.0-alpha" - istanbul-lib-report "^1.0.0-alpha" - istanbul-lib-source-maps "^1.0.0-alpha" - istanbul-reports "^1.0.0-alpha" - js-yaml "3.x" - mkdirp "0.5.x" - once "1.x" + async "^2.1.4" + fileset "^2.0.2" + istanbul-lib-coverage "^1.0.0" + istanbul-lib-hook "^1.0.0" + istanbul-lib-instrument "^1.3.0" + istanbul-lib-report "^1.0.0-alpha.3" + istanbul-lib-source-maps "^1.1.0" + istanbul-reports "^1.0.0" + js-yaml "^3.7.0" + mkdirp "^0.5.1" + once "^1.4.0" istanbul-lib-coverage@^1.0.0, istanbul-lib-coverage@^1.0.0-alpha, istanbul-lib-coverage@^1.0.0-alpha.0: version "1.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.0.tgz#c3f9b6d226da12424064cce87fce0fb57fdfa7a2" -istanbul-lib-hook@^1.0.0-alpha: - version "1.0.0-alpha.4" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0-alpha.4.tgz#8c5bb9f6fbd8526e0ae6cf639af28266906b938f" +istanbul-lib-hook@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.0.tgz#fc5367ee27f59268e8f060b0c7aaf051d9c425c5" dependencies: - append-transform "^0.3.0" + append-transform "^0.4.0" -istanbul-lib-instrument@^1.0.0-alpha, istanbul-lib-instrument@^1.1.1, istanbul-lib-instrument@^1.1.4: +istanbul-lib-instrument@^1.1.1, istanbul-lib-instrument@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.2.0.tgz#73d5d108ab7568c373fdcb7d01c1d42d565bc8c4" dependencies: @@ -3582,7 +3651,19 @@ istanbul-lib-instrument@^1.0.0-alpha, istanbul-lib-instrument@^1.1.1, istanbul-l istanbul-lib-coverage "^1.0.0" semver "^5.3.0" -istanbul-lib-report@^1.0.0-alpha: +istanbul-lib-instrument@^1.3.0, istanbul-lib-instrument@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.4.2.tgz#0e2fdfac93c1dabf2e31578637dc78a19089f43e" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.13.0" + istanbul-lib-coverage "^1.0.0" + semver "^5.3.0" + +istanbul-lib-report@^1.0.0-alpha.3: version "1.0.0-alpha.3" resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.0.0-alpha.3.tgz#32d5f6ec7f33ca3a602209e278b2e6ff143498af" dependencies: @@ -3593,78 +3674,53 @@ istanbul-lib-report@^1.0.0-alpha: rimraf "^2.4.3" supports-color "^3.1.2" -istanbul-lib-source-maps@^1.0.0-alpha: - version "1.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.0.2.tgz#9e91b0e5ae6ed203f67c69a34e6e98b10bb69a49" +istanbul-lib-source-maps@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.1.0.tgz#9d429218f35b823560ea300a96ff0c3bbdab785f" dependencies: istanbul-lib-coverage "^1.0.0-alpha.0" mkdirp "^0.5.1" rimraf "^2.4.4" source-map "^0.5.3" -istanbul-reports@^1.0.0-alpha: - version "1.0.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.0.tgz#24b4eb2b1d29d50f103b369bd422f6e640aa0777" +istanbul-reports@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.0.1.tgz#9a17176bc4a6cbebdae52b2f15961d52fa623fbc" dependencies: handlebars "^4.0.3" -istanbul@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" - dependencies: - abbrev "1.0.x" - async "1.x" - escodegen "1.8.x" - esprima "2.7.x" - glob "^5.0.15" - handlebars "^4.0.1" - js-yaml "3.x" - mkdirp "0.5.x" - nopt "3.x" - once "1.x" - resolve "1.1.x" - supports-color "^3.1.0" - which "^1.1.1" - wordwrap "^1.0.0" - -jasmine-check@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/jasmine-check/-/jasmine-check-0.1.5.tgz#dbad7eec56261c4b3d175ada55fe59b09ac9e415" - dependencies: - testcheck "^0.1.0" - jdataview@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/jdataview/-/jdataview-2.5.0.tgz#3081b3fea651f9317ec6bd4feb2ddc98aa41d595" -jest-changed-files@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-16.0.0.tgz#7931deff4424182b8173d80e06800d7363b19c45" +jest-changed-files@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-17.0.2.tgz#f5657758736996f590a51b87e5c9369d904ba7b7" -jest-cli@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-16.0.2.tgz#d439b28affa7189aa3d046d2af931f7ebb9af69d" +jest-cli@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-18.1.0.tgz#5ead36ecad420817c2c9baa2aa7574f63257b3d6" dependencies: ansi-escapes "^1.4.0" callsites "^2.0.0" chalk "^1.1.1" graceful-fs "^4.1.6" is-ci "^1.0.9" - istanbul-api "^1.0.0-aplha.10" + istanbul-api "^1.1.0-alpha.1" istanbul-lib-coverage "^1.0.0" istanbul-lib-instrument "^1.1.1" - jest-changed-files "^16.0.0" - jest-config "^16.0.2" - jest-environment-jsdom "^16.0.2" - jest-file-exists "^15.0.0" - jest-haste-map "^16.0.2" - jest-jasmine2 "^16.0.2" - jest-mock "^16.0.2" - jest-resolve "^16.0.2" - jest-resolve-dependencies "^16.0.2" - jest-runtime "^16.0.2" - jest-snapshot "^16.0.2" - jest-util "^16.0.2" + jest-changed-files "^17.0.2" + jest-config "^18.1.0" + jest-environment-jsdom "^18.1.0" + jest-file-exists "^17.0.0" + jest-haste-map "^18.1.0" + jest-jasmine2 "^18.1.0" + jest-mock "^18.0.0" + jest-resolve "^18.1.0" + jest-resolve-dependencies "^18.1.0" + jest-runtime "^18.1.0" + jest-snapshot "^18.1.0" + jest-util "^18.1.0" json-stable-stringify "^1.0.0" node-notifier "^4.6.1" sane "~1.4.1" @@ -3672,151 +3728,152 @@ jest-cli@^16.0.2: throat "^3.0.0" which "^1.1.1" worker-farm "^1.3.1" - yargs "^5.0.0" + yargs "^6.3.0" -jest-config@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-16.0.2.tgz#8e82a9c08846f23dc7fd42b5c0a1f596c385772a" +jest-config@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-18.1.0.tgz#6111740a6d48aab86ff5a9e6ab0b98bd993b6ff4" dependencies: chalk "^1.1.1" - istanbul "^0.4.5" - jest-environment-jsdom "^16.0.2" - jest-environment-node "^16.0.2" - jest-jasmine2 "^16.0.2" - jest-mock "^16.0.2" - jest-resolve "^16.0.2" - jest-util "^16.0.2" + jest-environment-jsdom "^18.1.0" + jest-environment-node "^18.1.0" + jest-jasmine2 "^18.1.0" + jest-mock "^18.0.0" + jest-resolve "^18.1.0" + jest-util "^18.1.0" json-stable-stringify "^1.0.0" -jest-diff@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-16.0.0.tgz#4a5d13b1e36c5b8020d5d9e69639e486a675ce14" +jest-diff@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-18.1.0.tgz#4ff79e74dd988c139195b365dc65d87f606f4803" dependencies: chalk "^1.1.3" diff "^3.0.0" - jest-matcher-utils "^16.0.0" - pretty-format "~4.2.1" + jest-matcher-utils "^18.1.0" + pretty-format "^18.1.0" -jest-environment-jsdom@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-16.0.2.tgz#548d883b68f8ed0bd6466d8703986296724c1ef7" +jest-environment-jsdom@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-18.1.0.tgz#18b42f0c4ea2bae9f36cab3639b1e8f8c384e24e" dependencies: - jest-mock "^16.0.2" - jest-util "^16.0.2" - jsdom "^9.8.0" + jest-mock "^18.0.0" + jest-util "^18.1.0" + jsdom "^9.9.1" -jest-environment-node@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-16.0.2.tgz#eb7b3a4a9c63b728ce023828d4b5661aad8c7a08" +jest-environment-node@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-18.1.0.tgz#4d6797572c8dda99acf5fae696eb62945547c779" dependencies: - jest-mock "^16.0.2" - jest-util "^16.0.2" + jest-mock "^18.0.0" + jest-util "^18.1.0" -jest-file-exists@^15.0.0: - version "15.0.0" - resolved "https://registry.yarnpkg.com/jest-file-exists/-/jest-file-exists-15.0.0.tgz#b7fefdd3f4b227cb686bb156ecc7661ee6935a88" +jest-file-exists@^17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/jest-file-exists/-/jest-file-exists-17.0.0.tgz#7f63eb73a1c43a13f461be261768b45af2cdd169" -jest-haste-map@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-16.0.2.tgz#4562915b25171ae2d0d75118c992f0e97536a2ed" +jest-haste-map@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-18.1.0.tgz#06839c74b770a40c1a106968851df8d281c08375" dependencies: fb-watchman "^1.9.0" graceful-fs "^4.1.6" - multimatch "^2.1.0" + micromatch "^2.3.11" + sane "~1.4.1" worker-farm "^1.3.1" -jest-jasmine2@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-16.0.2.tgz#c91ae170d127aae22180dbfe181d77655a5da8c3" +jest-jasmine2@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-18.1.0.tgz#094e104c2c189708766c77263bb2aecb5860a80b" dependencies: graceful-fs "^4.1.6" - jasmine-check "^0.1.4" - jest-matchers "^16.0.2" - jest-snapshot "^16.0.2" - jest-util "^16.0.2" + jest-matcher-utils "^18.1.0" + jest-matchers "^18.1.0" + jest-snapshot "^18.1.0" + jest-util "^18.1.0" -jest-matcher-utils@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-16.0.0.tgz#705af3ff85944bec1c25bc813f427aff8642b0cd" +jest-matcher-utils@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-18.1.0.tgz#1ac4651955ee2a60cef1e7fcc98cdfd773c0f932" dependencies: chalk "^1.1.3" - pretty-format "~4.2.1" + pretty-format "^18.1.0" -jest-matchers@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-16.0.2.tgz#c078c28cfe05b9b1f295f9ab27b5991f1095bbbf" +jest-matchers@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-matchers/-/jest-matchers-18.1.0.tgz#0341484bf87a1fd0bac0a4d2c899e2b77a3f1ead" dependencies: - jest-diff "^16.0.0" - jest-matcher-utils "^16.0.0" - jest-util "^16.0.2" + jest-diff "^18.1.0" + jest-matcher-utils "^18.1.0" + jest-util "^18.1.0" + pretty-format "^18.1.0" -jest-mock@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-16.0.2.tgz#97b533343295d0082e9474a73ac4eb474d1636fe" +jest-mock@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-18.0.0.tgz#5c248846ea33fa558b526f5312ab4a6765e489b3" -jest-resolve-dependencies@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-16.0.2.tgz#b204166d50141469d10667dc216239c0be865729" +jest-resolve-dependencies@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-18.1.0.tgz#8134fb5caf59c9ed842fe0152ab01c52711f1bbb" dependencies: - jest-file-exists "^15.0.0" - jest-resolve "^16.0.2" + jest-file-exists "^17.0.0" + jest-resolve "^18.1.0" -jest-resolve@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-16.0.2.tgz#46b92b9c2a44aa7ddd9a6b73dc234e9503e8c609" +jest-resolve@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-18.1.0.tgz#6800accb536658c906cd5e29de412b1ab9ac249b" dependencies: browser-resolve "^1.11.2" - jest-file-exists "^15.0.0" - jest-haste-map "^16.0.2" - resolve "^1.1.6" + jest-file-exists "^17.0.0" + jest-haste-map "^18.1.0" + resolve "^1.2.0" -jest-runtime@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-16.0.2.tgz#a741e8d55a7b5f011bbe17a22c673a83d278a45d" +jest-runtime@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-18.1.0.tgz#3abfd687175b21fc3b85a2b8064399e997859922" dependencies: - babel-core "^6.11.4" - babel-jest "^16.0.0" - babel-plugin-istanbul "^2.0.0" + babel-core "^6.0.0" + babel-jest "^18.0.0" + babel-plugin-istanbul "^3.0.0" chalk "^1.1.3" graceful-fs "^4.1.6" - jest-config "^16.0.2" - jest-file-exists "^15.0.0" - jest-haste-map "^16.0.2" - jest-mock "^16.0.2" - jest-resolve "^16.0.2" - jest-snapshot "^16.0.2" - jest-util "^16.0.2" + jest-config "^18.1.0" + jest-file-exists "^17.0.0" + jest-haste-map "^18.1.0" + jest-mock "^18.0.0" + jest-resolve "^18.1.0" + jest-snapshot "^18.1.0" + jest-util "^18.1.0" json-stable-stringify "^1.0.0" - multimatch "^2.1.0" - yargs "^5.0.0" + micromatch "^2.3.11" + yargs "^6.3.0" -jest-snapshot@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-16.0.2.tgz#f137a4176d661bd4058910850191d1816bebdaae" +jest-snapshot@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-18.1.0.tgz#55b96d2ee639c9bce76f87f2a3fd40b71c7a5916" dependencies: - jest-diff "^16.0.0" - jest-file-exists "^15.0.0" - jest-matcher-utils "^16.0.0" - jest-util "^16.0.2" + jest-diff "^18.1.0" + jest-file-exists "^17.0.0" + jest-matcher-utils "^18.1.0" + jest-util "^18.1.0" natural-compare "^1.4.0" - pretty-format "~4.2.1" + pretty-format "^18.1.0" -jest-util@^16.0.2: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-16.0.2.tgz#db5123358278e7a34a6d9f837409d649a0db5d54" +jest-util@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-18.1.0.tgz#3a99c32114ab17f84be094382527006e6d4bfc6a" dependencies: chalk "^1.1.1" diff "^3.0.0" graceful-fs "^4.1.6" - jest-file-exists "^15.0.0" - jest-mock "^16.0.2" + jest-file-exists "^17.0.0" + jest-mock "^18.0.0" mkdirp "^0.5.1" -jest@^16.0.0: - version "16.0.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-16.0.2.tgz#4a2f7f3527465168a0bafe0c3d55055188253f3a" +jest@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-18.1.0.tgz#bcebf1e203dee5c2ad2091c805300a343d9e6c7d" dependencies: - jest-cli "^16.0.2" + jest-cli "^18.1.0" jmespath@0.15.0: version "0.15.0" @@ -3836,9 +3893,13 @@ js-tokens@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-2.0.0.tgz#79903f5563ee778cc1162e6dcf1a0027c97f9cb5" -js-yaml@3.x, js-yaml@^3.5.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +js-yaml@^3.5.1, js-yaml@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" dependencies: argparse "^1.0.7" esprima "^2.6.0" @@ -3847,9 +3908,9 @@ jsbn@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd" -jsdom@^9.8.0: - version "9.8.3" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.8.3.tgz#fde29c109c32a1131e0b6c65914e64198f97c370" +jsdom@^9.9.1: + version "9.9.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.9.1.tgz#84f3972ad394ab963233af8725211bce4d01bfd5" dependencies: abab "^1.0.0" acorn "^2.4.0" @@ -3861,7 +3922,7 @@ jsdom@^9.8.0: escodegen "^1.6.1" html-encoding-sniffer "^1.0.1" iconv-lite "^0.4.13" - nwmatcher ">= 1.3.7 < 2.0.0" + nwmatcher ">= 1.3.9 < 2.0.0" parse5 "^1.5.1" request "^2.55.0" sax "^1.1.4" @@ -3869,7 +3930,7 @@ jsdom@^9.8.0: tough-cookie "^2.3.1" webidl-conversions "^3.0.1" whatwg-encoding "^1.0.1" - whatwg-url "^3.0.0" + whatwg-url "^4.1.0" xml-name-validator ">= 2.0.1 < 3.0.0" jsesc@^1.3.0: @@ -3908,6 +3969,12 @@ json5@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.0.tgz#9b20715b026cbe3778fd769edccd822d8332a5b2" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + 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" @@ -3925,80 +3992,13 @@ jsonp@~0.0.4: debug "*" jsonparse@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.2.0.tgz#5c0c5685107160e72fe7489bddea0b44c2bc67bd" + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.0.tgz#85fc245b1d9259acc6941960b905adf64e7de0e8" jsonpointer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.0.tgz#6661e161d2fc445f19f98430231343722e1fcbd5" -jspm-github@^0.14.11: - version "0.14.11" - resolved "https://registry.yarnpkg.com/jspm-github/-/jspm-github-0.14.11.tgz#5093b3a79289d63ff6e3982f3b527878ac808d5c" - dependencies: - bluebird "^3.0.5" - expand-tilde "^1.2.0" - graceful-fs "^4.1.3" - mkdirp "^0.5.1" - netrc "^0.1.3" - request "^2.74.0" - rimraf "^2.5.4" - semver "^5.0.1" - tar-fs "^1.13.0" - which "^1.0.9" - -jspm-npm@^0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/jspm-npm/-/jspm-npm-0.30.0.tgz#3f3ab3c33f1ae7070a240c607c6dd0814affdc39" - dependencies: - bluebird "^3.0.5" - buffer-peek-stream "^1.0.1" - graceful-fs "^4.1.3" - mkdirp "^0.5.1" - readdirp "^2.0.0" - request "^2.58.0" - rmdir "^1.1.0" - semver "^5.0.1" - systemjs-builder "^0.15.33" - tar-fs "^1.13.0" - traceur "0.0.105" - which "^1.1.1" - -jspm-registry@^0.4.1: - version "0.4.2" - resolved "https://registry.yarnpkg.com/jspm-registry/-/jspm-registry-0.4.2.tgz#a78ec3f5935bd3c9363da10b94b9c93e5b555e7d" - dependencies: - graceful-fs "^4.1.3" - rimraf "^2.3.2" - rsvp "^3.0.18" - semver "^4.3.3" - -jspm@^0.17.0-beta.13: - version "0.17.0-beta.31" - resolved "https://registry.yarnpkg.com/jspm/-/jspm-0.17.0-beta.31.tgz#34cfd27a0f8d915e788356fd57f2c613595c4ae7" - dependencies: - bluebird "^3.0.5" - chalk "^1.1.1" - core-js "^1.2.6" - glob "^6.0.1" - graceful-fs "^4.1.2" - jspm-github "^0.14.11" - jspm-npm "^0.30.0" - jspm-registry "^0.4.1" - liftoff "^2.2.0" - minimatch "^3.0.0" - mkdirp "~0.5.1" - ncp "^2.0.0" - proper-lockfile "^1.1.2" - request "^2.67.0" - rimraf "^2.4.4" - sane "^1.3.3" - semver "^5.1.0" - systemjs "0.19.40" - systemjs-builder "0.15.33" - traceur "0.0.105" - uglify-js "^2.6.1" - jsprim@^1.2.2: version "1.3.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" @@ -4017,9 +4017,9 @@ jstransform@^11.0.3: object-assign "^2.0.0" source-map "^0.4.2" -jsx-ast-utils@^1.3.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.3.tgz#ccfdbe0320ba03f7a1fc4e67ceaf7e2cc0169721" +jsx-ast-utils@^1.3.3: + version "1.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.3.5.tgz#9ba6297198d9f754594d62e59496ffb923778dd4" dependencies: acorn-jsx "^3.0.1" object-assign "^4.1.0" @@ -4054,6 +4054,12 @@ kind-of@^3.0.2: dependencies: is-buffer "^1.0.2" +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + 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" @@ -4103,20 +4109,6 @@ lie@^3.0.1, lie@~3.1.0: dependencies: immediate "~3.0.5" -liftoff@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.3.0.tgz#a98f2ff67183d8ba7cfaca10548bd7ff0550b385" - dependencies: - extend "^3.0.0" - findup-sync "^0.4.2" - fined "^1.0.1" - flagged-respawn "^0.3.2" - lodash.isplainobject "^4.0.4" - lodash.isstring "^4.0.1" - lodash.mapvalues "^4.4.0" - rechoir "^0.6.2" - resolve "^1.1.7" - livereload-js@^2.2.0: version "2.2.2" resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.2.2.tgz#6c87257e648ab475bc24ea257457edcc1f8d0bc2" @@ -4181,14 +4173,10 @@ lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" -lodash.assign@^4.1.0, lodash.assign@^4.2.0: +lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" -lodash.assignwith@^4.0.7: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignwith/-/lodash.assignwith-4.2.0.tgz#127a97f02adc41751a954d24b0de17e100e038eb" - lodash.clonedeep@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-3.0.2.tgz#a0a1e40d82a5ea89ff5b147b8444ed63d92827db" @@ -4208,10 +4196,6 @@ lodash.isarray@^3.0.0: version "3.0.4" resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" -lodash.isempty@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" - lodash.isequal@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.4.0.tgz#6295768e98e14dc15ce8d362ef6340db82852031" @@ -4220,14 +4204,6 @@ lodash.isobject@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" -lodash.isplainobject@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - lodash.keys@^3.0.0, lodash.keys@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" @@ -4240,10 +4216,6 @@ lodash.map@^4.5.1: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" -lodash.mapvalues@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz#1bafa5005de9dd6f4f26668c30ca37230cc9689c" - lodash.memoize@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -4256,10 +4228,6 @@ lodash.merge@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - lodash.pickby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" @@ -4281,11 +4249,11 @@ lodash.uniq@^4.3.0, lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@4.15.0: - version "4.15.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.15.0.tgz#3162391d8f0140aa22cf8f6b3c34d6b7f63d3aa9" +lodash@4.17.2, lodash@^4.1.0, lodash@^4.14.0, lodash@^4.3.0: + version "4.17.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.2.tgz#34a3055babe04ce42467b607d700072c7ff6bf42" -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.1.0, lodash@^4.13.1, lodash@^4.16.1, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.0.1, lodash@^4.13.1, lodash@^4.16.1, lodash@^4.2.0, lodash@^4.2.1: version "4.16.6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.6.tgz#d22c9ac660288f3843e16ba7d2b5d06cca27d777" @@ -4340,10 +4308,6 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -map-cache@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - map-limit@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/map-limit/-/map-limit-0.0.1.tgz#eb7961031c0f0e8d001bf2d56fab685d58822f38" @@ -4368,17 +4332,18 @@ marked@^0.3.6: version "0.3.6" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" -mastarm: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mastarm/-/mastarm-2.0.0.tgz#4688e28616849c9b4438dd002e8b2c426bafed7f" +mastarm@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/mastarm/-/mastarm-3.3.0.tgz#51918979fe1f3434fc6acf673628c81bc869925a" dependencies: aws-sdk "^2.4.2" babel-core "^6.10.4" babel-eslint "^7.0.0" - babel-jest "^16.0.0" + babel-jest "^17.0.2" babel-plugin-add-module-exports "^0.2.1" + babel-plugin-transform-flow-strip-types "^6.18.0" babel-plugin-transform-runtime "^6.9.0" - babel-preset-env "^0.0.6" + babel-preset-env "^1.1.0" babel-preset-react "^6.5.0" babel-preset-stage-2 "^6.17.0" babelify "^7.3.0" @@ -4390,26 +4355,26 @@ mastarm: commitizen "^2.8.2" concat-stream "^1.5.1" cz-conventional-changelog "^1.1.6" - envify "^3.4.1" + envify "^4.0.0" errorify "^0.3.1" exorcist "^0.4.0" http-proxy "^1.14.0" isomorphic-fetch "^2.2.1" - jest "^16.0.0" + jest "^18.1.0" lodash.uniq "^4.5.0" mime "^1.3.4" mkdirp "^0.5.1" postcss "^5.0.21" postcss-cssnext "^2.6.0" - postcss-import "^8.1.2" - postcss-reporter "^1.3.3" + postcss-import "^9.0.0" + postcss-reporter "^3.0.0" postcss-safe-parser "^2.0.0" rimraf "^2.5.4" standard "^8.3.0" standard-engine "^5.0.0" through2 "^2.0.1" uglifyify "^3.0.2" - uuid "^2.0.2" + uuid "^3.0.0" watchify "^3.7.0" yamljs "^0.2.8" @@ -4418,8 +4383,8 @@ material-colors@^1.0.0: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.0.tgz#956f0c87660882333cff7b7557efb7b03592820e" math-expression-evaluator@^1.2.14: - version "1.2.14" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.14.tgz#39511771ed9602405fba9affff17eb4d2a3843ab" + version "1.2.15" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.15.tgz#38dc5f0194c5bf5ff1c690ad4c4b64df71ac0187" dependencies: lodash.indexof "^4.0.5" @@ -4493,18 +4458,12 @@ minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2: +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" dependencies: brace-expansion "^1.0.0" -minimatch@2.x, minimatch@^2.0.1: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - dependencies: - brace-expansion "^1.0.0" - minimist@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.5.tgz#d7aa327bcecf518f9106ac6b8f003fa3bcea8566" @@ -4517,7 +4476,7 @@ minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2 version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -4568,14 +4527,9 @@ ms@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" -multimatch@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" mute-stream@0.0.5: version "0.0.5" @@ -4586,8 +4540,8 @@ mute-stream@0.0.6: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" nan@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" + version "2.5.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" natural-compare@^1.4.0: version "1.4.0" @@ -4597,14 +4551,6 @@ nave@~0.5.1: version "0.5.3" resolved "https://registry.yarnpkg.com/nave/-/nave-0.5.3.tgz#5acec72375856e5c76c83bd21a68d713eb5f1ba4" -ncp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" - -netrc@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/netrc/-/netrc-0.1.4.tgz#6be94fcaca8d77ade0a9670dc460914c94472444" - no-case@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.0.tgz#ca2825ccb76b18e6f79d573dcfbf1eace33dd164" @@ -4612,8 +4558,8 @@ no-case@^2.2.0: lower-case "^1.1.1" node-emoji@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.4.1.tgz#c9fa0cf91094335bcb967a6f42b2305c15af2ebc" + version "1.5.1" + resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.5.1.tgz#fd918e412769bf8c448051238233840b2aff16a1" dependencies: string.prototype.codepointat "^0.2.0" @@ -4641,14 +4587,14 @@ node-notifier@^4.6.1: which "^1.0.5" node-pre-gyp@^0.6.29: - version "0.6.31" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.31.tgz#d8a00ddaa301a940615dbcc8caad4024d58f6017" + version "0.6.32" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.32.tgz#fc452b376e7319b3d255f5f34853ef6fd8fe1fd5" dependencies: mkdirp "~0.5.1" nopt "~3.0.6" - npmlog "^4.0.0" + npmlog "^4.0.1" rc "~1.1.6" - request "^2.75.0" + request "^2.79.0" rimraf "~2.5.4" semver "~5.3.0" tar "~2.2.1" @@ -4658,20 +4604,7 @@ node-uuid@~1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.7.tgz#6da5a17668c4b3dd59623bda11cf7fa4c1f60a6f" -node.extend@1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.0.8.tgz#bab04379f7383f4587990c9df07b6a7f65db772b" - dependencies: - is "~0.2.6" - object-keys "~0.4.0" - -node.flow@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/node.flow/-/node.flow-1.2.3.tgz#e1c44a82aeca8d78b458a77fb3dc642f2eba2649" - dependencies: - node.extend "1.0.8" - -nopt@3.x, nopt@~3.0.6: +nopt@~3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" dependencies: @@ -4694,13 +4627,13 @@ normalize-range@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" -npmlog@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.0.tgz#e094503961c70c1774eb76692080e8d578a9f88f" +npmlog@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" - gauge "~2.6.0" + gauge "~2.7.1" set-blocking "~2.0.0" num2fraction@^1.2.2: @@ -4715,7 +4648,7 @@ numeral@^1.5.3: version "1.5.3" resolved "https://registry.yarnpkg.com/numeral/-/numeral-1.5.3.tgz#a4c3eba68239580509f818267c77243bce43ff62" -"nwmatcher@>= 1.3.7 < 2.0.0": +"nwmatcher@>= 1.3.9 < 2.0.0": version "1.3.9" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.3.9.tgz#8bab486ff7fa3dfd086656bbe8b17116d3692d2a" @@ -4754,13 +4687,17 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -on-finished@~2.3.0: +on-finished@^2.3.0, on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" dependencies: ee-first "1.1.1" -once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.2: +on-headers@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" + +once@^1.3.0, once@^1.3.2, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: @@ -4822,17 +4759,10 @@ os-shim@^0.1.2: version "0.1.3" resolved "https://registry.yarnpkg.com/os-shim/-/os-shim-0.1.3.tgz#6b62c3791cf7909ea35ed46e17658bb417cb3917" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -osenv@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.3.tgz#83cf05c6d6458fc4d5ac6362ea325d92f2754217" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - outpipe@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/outpipe/-/outpipe-1.1.1.tgz#50cf8616365e87e031e29a5ec9339a3da4725fa2" @@ -4887,14 +4817,6 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" -parse-filepath@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.1.tgz#159d6155d43904d16c10ef698911da1e91969b73" - dependencies: - is-absolute "^0.2.3" - map-cache "^0.2.0" - path-root "^0.1.1" - parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -4914,6 +4836,10 @@ parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + parse5@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" @@ -4977,16 +4903,6 @@ path-platform@~0.11.15: version "0.11.15" resolved "https://registry.yarnpkg.com/path-platform/-/path-platform-0.11.15.tgz#e864217f74c36850f0852b78dc7bf7d4a5721bf2" -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - dependencies: - path-root-regex "^0.1.0" - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -5002,8 +4918,8 @@ pbkdf2@^3.0.3: create-hmac "^1.1.2" pem@^1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/pem/-/pem-1.8.3.tgz#e78fc65469698c7e85e4d62dd1018926ed89f3bd" + version "1.9.4" + resolved "https://registry.yarnpkg.com/pem/-/pem-1.9.4.tgz#63e89c49c17629610e978e87514e5cdbf498374f" dependencies: os-tmpdir "^1.0.1" which "^1.2.4" @@ -5038,13 +4954,6 @@ pkg-config@^1.0.1, pkg-config@^1.1.0: find-root "^1.0.0" xtend "^4.0.1" -pkg-resolve@^0.1.7: - version "0.1.14" - resolved "https://registry.yarnpkg.com/pkg-resolve/-/pkg-resolve-0.1.14.tgz#329b2e76ccbb372e22e6a3a41cb30ab0457836ba" - dependencies: - jspm "^0.17.0-beta.13" - resolve "^1.1.7" - pleeease-filters@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pleeease-filters/-/pleeease-filters-3.0.0.tgz#35a4d4c2086413eabc2ce17aaa2ec29054e3075c" @@ -5096,10 +5005,10 @@ postcss-color-function@^2.0.0: postcss-value-parser "^3.3.0" postcss-color-gray@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-3.0.0.tgz#dd65f7ad7bec4b63b458a5de88d6dd49c86cbf7b" + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-3.0.1.tgz#74432ede66dd83b1d1363565c68b376e18ff6770" dependencies: - color "^0.7.3" + color "^0.11.3" postcss "^5.0.4" postcss-message-helpers "^2.0.0" reduce-function-call "^1.0.1" @@ -5112,22 +5021,37 @@ postcss-color-hex-alpha@^2.0.0: postcss "^5.0.4" postcss-message-helpers "^2.0.0" +postcss-color-hsl@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-color-hsl/-/postcss-color-hsl-1.0.5.tgz#f53bb1c348310ce307ad89e3181a864738b5e687" + dependencies: + postcss "^5.2.0" + postcss-value-parser "^3.3.0" + units-css "^0.4.0" + postcss-color-hwb@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-hwb/-/postcss-color-hwb-2.0.0.tgz#2c0d30a9563158f41eb3f6bddfdf1dae9cadae37" + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-hwb/-/postcss-color-hwb-2.0.1.tgz#d63afaf9b70cb595f900a29c9fe57bf2a32fabec" dependencies: - color "^0.10.1" + color "^0.11.4" postcss "^5.0.4" postcss-message-helpers "^2.0.0" reduce-function-call "^1.0.1" postcss-color-rebeccapurple@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-2.0.0.tgz#5a88225aeb924a1e3e8e2ea066c5cf4fe39d818a" + version "2.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-2.0.1.tgz#74c6444e7cbb7d85613b5f7286df7a491608451c" dependencies: - color "^0.9.0" + color "^0.11.4" postcss "^5.0.4" +postcss-color-rgb@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/postcss-color-rgb/-/postcss-color-rgb-1.1.4.tgz#f29243e22e8e8c13434474092372d4ce605be8bc" + dependencies: + postcss "^5.2.0" + postcss-value-parser "^3.3.0" + postcss-color-rgba-fallback@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-color-rgba-fallback/-/postcss-color-rgba-fallback-2.2.0.tgz#6d29491be5990a93173d47e7c76f5810b09402ba" @@ -5137,8 +5061,8 @@ postcss-color-rgba-fallback@^2.0.0: rgb-hex "^1.0.0" postcss-cssnext@^2.6.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-2.8.0.tgz#fbaef792185967457812f66355d0c24707bb8cf3" + version "2.9.0" + resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-2.9.0.tgz#064df2a8c21fd2ebb88825df372cf20fca882868" dependencies: autoprefixer "^6.0.2" caniuse-api "^1.3.2" @@ -5152,8 +5076,10 @@ postcss-cssnext@^2.6.0: postcss-color-function "^2.0.0" postcss-color-gray "^3.0.0" postcss-color-hex-alpha "^2.0.0" + postcss-color-hsl "^1.0.5" postcss-color-hwb "^2.0.0" postcss-color-rebeccapurple "^2.0.0" + postcss-color-rgb "^1.1.4" postcss-color-rgba-fallback "^2.0.0" postcss-custom-media "^5.0.0" postcss-custom-properties "^5.0.0" @@ -5195,27 +5121,27 @@ postcss-font-variant@^2.0.0: dependencies: postcss "^5.0.4" -postcss-import@^8.1.2: - version "8.1.2" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-8.1.2.tgz#f1dbcce590c93b536a121ffedcb63f1b751749f9" +postcss-import@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-9.1.0.tgz#95fe9876a1e79af49fbdc3589f01fe5aa7cc1e80" dependencies: object-assign "^4.0.1" - pkg-resolve "^0.1.7" postcss "^5.0.14" postcss-value-parser "^3.2.3" + promise-each "^2.2.0" read-cache "^1.0.0" resolve "^1.1.7" postcss-initial@^1.3.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-1.5.2.tgz#61eb5ad871e7991aadfb3f497b16fe61aea92ffb" + version "1.5.3" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-1.5.3.tgz#20c3e91c96822ddb1bed49508db96d56bac377d0" dependencies: lodash.template "^4.2.4" postcss "^5.0.19" postcss-media-minmax@^2.1.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-2.1.2.tgz#444c5cf8926ab5e4fd8a2509e9297e751649cdf8" + resolved "http://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-2.1.2.tgz#444c5cf8926ab5e4fd8a2509e9297e751649cdf8" dependencies: postcss "^5.0.4" @@ -5248,9 +5174,9 @@ postcss-replace-overflow-wrap@^1.0.0: dependencies: postcss "^5.0.16" -postcss-reporter@^1.3.3: - version "1.4.1" - resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-1.4.1.tgz#c136f0a5b161915f379dd3765c61075f7e7b9af2" +postcss-reporter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-3.0.0.tgz#09ea0f37a444c5693878606e09b018ebeff7cf8f" dependencies: chalk "^1.0.0" lodash "^4.1.0" @@ -5286,8 +5212,8 @@ postcss-selector-parser@^1.1.4: uniq "^1.0.1" postcss-selector-parser@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.1.tgz#fdbf696103b12b0a64060e5610507f410491f7c8" + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.2.tgz#3d70f5adda130da51c7c0c2fc023f56b1374fe08" dependencies: flatten "^1.0.2" indexes-of "^1.0.1" @@ -5297,14 +5223,14 @@ postcss-value-parser@^3.0.2, postcss-value-parser@^3.2.3, postcss-value-parser@^ version "3.3.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" -postcss@^5.0.0, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.21, postcss@^5.0.3, postcss@^5.0.4, postcss@^5.1.1, postcss@^5.2.0, postcss@^5.2.4: - version "5.2.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.5.tgz#ec428c27dffc7fac65961340a9b022fa4af5f056" +postcss@^5.0.0, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.19, postcss@^5.0.2, postcss@^5.0.21, postcss@^5.0.3, postcss@^5.0.4, postcss@^5.1.1, postcss@^5.2.0, postcss@^5.2.11: + version "5.2.11" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.11.tgz#ff29bcd6d2efb98bfe08a022055ec599bbe7b761" dependencies: chalk "^1.1.3" js-base64 "^2.1.9" source-map "^0.5.6" - supports-color "^3.1.2" + supports-color "^3.2.3" prelude-ls@~1.1.2: version "1.1.2" @@ -5318,9 +5244,11 @@ prettier-bytes@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/prettier-bytes/-/prettier-bytes-1.0.3.tgz#932b31c23efddb36fc66a82dcef362af3122982f" -pretty-format@~4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-4.2.2.tgz#f80bf8d98a6f4d20997a51d18bf331f2ad789a64" +pretty-format@^18.1.0: + version "18.1.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-18.1.0.tgz#fb65a86f7a7f9194963eee91865c1bcf1039e284" + dependencies: + ansi-styles "^2.2.1" pretty-ms@^2.1.0: version "2.1.0" @@ -5352,21 +5280,18 @@ proj4@^2.1.4: dependencies: mgrs "~0.0.2" +promise-each@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/promise-each/-/promise-each-2.2.0.tgz#3353174eff2694481037e04e01f77aa0fb6d1b60" + dependencies: + any-promise "^0.1.0" + promise@^7.0.3, promise@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" dependencies: asap "~2.0.3" -proper-lockfile@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-1.2.0.tgz#ceff5dd89d3e5f10fb75e1e8e76bc75801a59c34" - dependencies: - err-code "^1.0.0" - extend "^3.0.0" - graceful-fs "^4.1.2" - retry "^0.10.0" - prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" @@ -5381,13 +5306,6 @@ public-encrypt@^4.0.0: parse-asn1 "^5.0.0" randombytes "^2.0.1" -pump@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.1.tgz#f1f1409fb9bd1085bbdb576b43b84ec4b5eadc1a" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -5813,9 +5731,9 @@ read-pkg@^1.0.0: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.1.0, readable-stream@~2.1.4: - version "2.1.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" +readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" dependencies: buffer-shims "^1.0.0" core-util-is "~1.0.0" @@ -5825,7 +5743,16 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2. string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@^2.0.2, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6: +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~2.0.0, readable-stream@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: @@ -5836,14 +5763,17 @@ readable-stream@^2.0.2, readable-stream@~2.0.0, readable-stream@~2.0.5, readable string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" +readable-stream@~2.1.4: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" dependencies: + buffer-shims "^1.0.0" core-util-is "~1.0.0" inherits "~2.0.1" - isarray "0.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" string_decoder "~0.10.x" + util-deprecate "~1.0.1" readdirp@^2.0.0: version "2.1.0" @@ -5899,10 +5829,10 @@ reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7: reduce-function-call "^1.0.1" reduce-function-call@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.1.tgz#fa02e126e695824263cab91d3a5b0fdc1dd27a9a" + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" dependencies: - balanced-match "~0.1.0" + balanced-match "^0.4.2" reduce-reducers@^0.1.0, reduce-reducers@^0.1.2: version "0.1.2" @@ -5935,13 +5865,21 @@ redux@^3.2.0, redux@^3.3.1, redux@^3.6.0: symbol-observable "^1.0.2" regenerate@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.1.tgz#0300203a5d2fdcf89116dce84275d011f5903f33" + version "1.3.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" regenerator-runtime@^0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.1.tgz#257f41961ce44558b18f7814af48c17559f9faeb" +regenerator-transform@0.9.8: + version "0.9.8" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + regex-cache@^0.4.2: version "0.4.3" resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" @@ -5988,7 +5926,32 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.55.0, request@^2.58.0, request@^2.67.0, request@^2.74.0, request@^2.75.0: +request@^2.55.0, request@^2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +request@^2.67.0: version "2.76.0" resolved "https://registry.yarnpkg.com/request/-/request-2.76.0.tgz#be44505afef70360a0436955106be3945d95560e" dependencies: @@ -6022,8 +5985,8 @@ require-main-filename@^1.0.1: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" require-uncached@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.2.tgz#67dad3b733089e77030124678a459589faf6a7ec" + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" dependencies: caller-path "^0.1.0" resolve-from "^1.0.0" @@ -6051,10 +6014,14 @@ resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" -resolve@1.1.7, resolve@1.1.x, resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7: +resolve@1.1.7, resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" +resolve@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.2.0.tgz#9589c3f2f6149d1417a40becc1663db6ec6bc26c" + resp-modifier@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f" @@ -6069,10 +6036,6 @@ restore-cursor@^1.0.1: exit-hook "^1.0.0" onetime "^1.0.0" -retry@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.0.tgz#649e15ca408422d98318161935e7f7d652d435dd" - rgb-hex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-1.0.0.tgz#bfaf8cd9cd9164b5a26d71eb4f15a0965324b3c1" @@ -6095,7 +6058,7 @@ right-pad@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/right-pad/-/right-pad-1.0.1.tgz#8ca08c2cbb5b55e74dafa96bf7fd1a27d568c8d0" -rimraf@2, rimraf@^2.2.8, rimraf@^2.3.2, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4: +rimraf@2, rimraf@^2.2.8, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@~2.5.1, rimraf@~2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" dependencies: @@ -6105,22 +6068,6 @@ ripemd160@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" -rmdir@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rmdir/-/rmdir-1.2.0.tgz#4fe0357cb06168c258e73e968093dc4e8a0f3253" - dependencies: - node.flow "1.2.3" - -rollup@^0.36.3: - version "0.36.3" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-0.36.3.tgz#c89ac479828924ff8f69c1d44541cb4ea2fc11fc" - dependencies: - source-map-support "^0.4.0" - -rsvp@^3.0.13, rsvp@^3.0.18: - version "3.3.3" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.3.3.tgz#34633caaf8bc66ceff4be3c2e1dffd032538a813" - run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -6128,11 +6075,10 @@ run-async@^0.1.0: once "^1.3.0" run-async@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.2.0.tgz#8783abd83c7bb86f41ee0602fc82404b3bd6e8b9" + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" dependencies: is-promise "^2.1.0" - pinkie-promise "^2.0.0" run-parallel@^1.1.2: version "1.1.6" @@ -6146,7 +6092,7 @@ rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" -sane@^1.3.3, sane@~1.4.1: +sane@~1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/sane/-/sane-1.4.1.tgz#88f763d74040f5f0c256b6163db399bf110ac715" dependencies: @@ -6174,17 +6120,13 @@ selectn@^1.0.20, selectn@^1.0.5: debug "^2.2.0" dotsplit.js "^1.0.3" -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" -semver@^4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - -send@0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.14.1.tgz#a954984325392f51532a7760760e459598c89f7a" +send@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.14.2.tgz#39b0438b3f510be5dc6f667a11f71689368cdeef" dependencies: debug "~2.2.0" depd "~1.1.0" @@ -6193,12 +6135,12 @@ send@0.14.1: escape-html "~1.0.3" etag "~1.7.0" fresh "0.3.0" - http-errors "~1.5.0" + http-errors "~1.5.1" mime "1.3.4" - ms "0.7.1" + ms "0.7.2" on-finished "~2.3.0" range-parser "~1.2.0" - statuses "~1.3.0" + statuses "~1.3.1" sentence-case@^2.1.0: version "2.1.0" @@ -6208,13 +6150,13 @@ sentence-case@^2.1.0: upper-case-first "^1.1.2" serve-static@^1.10.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.1.tgz#d6cce7693505f733c759de57befc1af76c0f0805" + version "1.11.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.2.tgz#2cf9889bd4435a320cc36895c9aa57bd662e6ac7" dependencies: encodeurl "~1.0.1" escape-html "~1.0.3" parseurl "~1.3.1" - send "0.14.1" + send "0.14.2" set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" @@ -6228,9 +6170,13 @@ setprototypeof@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.1.tgz#52009b27888c4dc48f591949c0a8275834c1ca7e" +setprototypeof@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.2.tgz#81a552141ec104b88e89ce383103ad5c66564d08" + sha.js@^2.3.6, sha.js@~2.4.4: - version "2.4.5" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.5.tgz#27d171efcc82a118b99639ff581660242b506e7c" + version "2.4.8" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f" dependencies: inherits "^2.0.1" @@ -6251,7 +6197,7 @@ shasum@^1.0.0: json-stable-stringify "~0.0.0" sha.js "~2.4.4" -shell-quote@^1.4.2, shell-quote@^1.4.3: +shell-quote@^1.4.2, shell-quote@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" dependencies: @@ -6260,15 +6206,7 @@ shell-quote@^1.4.2, shell-quote@^1.4.3: array-reduce "~0.0.0" jsonify "~0.0.0" -shelljs@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.5.3.tgz#c54982b996c76ef0c1e6b59fbdc5825f5b713113" - -shelljs@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.6.1.tgz#ec6211bed1920442088fe0f70b2837232ed2c8a8" - -shelljs@^0.7.0: +shelljs@0.7.5, shelljs@^0.7.0, shelljs@^0.7.5: version "0.7.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.5.tgz#2eef7a50a21e1ccf37da00df767ec69e30ad0675" dependencies: @@ -6298,8 +6236,8 @@ shpjs@^3.3.2: proj4 "^2.1.4" signal-exit@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.1.tgz#5a4c884992b63a7acd9badb7894c3ee9cfccad81" + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" simple-html-index@^1.4.0: version "1.5.0" @@ -6331,24 +6269,12 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -source-map-support@^0.4.0, source-map-support@^0.4.2: +source-map-support@^0.4.2: version "0.4.6" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.6.tgz#32552aa64b458392a85eab3b0b5ee61527167aeb" dependencies: source-map "^0.5.3" -source-map-support@~0.2.8: - version "0.2.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" - dependencies: - source-map "0.1.32" - -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" - dependencies: - amdefine ">=0.0.4" - "source-map@>= 0.1.2", source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1, source-map@~0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" @@ -6421,9 +6347,20 @@ stacked@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stacked/-/stacked-1.1.1.tgz#2c7fa38cc7e37a3411a77cd8e792de448f9f6975" -standard-engine@^5.0.0, standard-engine@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-5.1.1.tgz#cb775eae1c50cfa8e76ab25456dd122af7f34788" +standard-engine@^5.0.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-5.3.0.tgz#fa254d7e068d92de8019d9945d420286d1ce04c9" + dependencies: + deglob "^2.1.0" + find-root "^1.0.0" + get-stdin "^5.0.1" + home-or-tmp "^2.0.0" + minimist "^1.1.0" + pkg-config "^1.0.1" + +standard-engine@~5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/standard-engine/-/standard-engine-5.2.0.tgz#400660ae5acce8afd4db60ff2214a9190ad790a3" dependencies: deglob "^2.0.0" find-root "^1.0.0" @@ -6433,16 +6370,16 @@ standard-engine@^5.0.0, standard-engine@~5.1.0: pkg-config "^1.0.1" standard@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/standard/-/standard-8.5.0.tgz#df78a505da59382287b92a86b55ae02df3b54a31" + version "8.6.0" + resolved "https://registry.yarnpkg.com/standard/-/standard-8.6.0.tgz#635132be7bfb567c2921005f30f9e350e4752aad" dependencies: - eslint "~3.8.1" + eslint "~3.10.2" eslint-config-standard "6.2.1" eslint-config-standard-jsx "3.2.0" - eslint-plugin-promise "~3.3.0" - eslint-plugin-react "~6.4.1" + eslint-plugin-promise "~3.4.0" + eslint-plugin-react "~6.7.1" eslint-plugin-standard "~2.0.1" - standard-engine "~5.1.0" + standard-engine "~5.2.0" static-eval@~0.2.0: version "0.2.4" @@ -6466,7 +6403,11 @@ static-module@^1.1.0: static-eval "~0.2.0" through2 "~0.4.1" -statuses@1, "statuses@>= 1.3.0 < 2", statuses@~1.3.0: +statuses@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" + +"statuses@>= 1.3.0 < 2": version "1.3.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.0.tgz#8e55758cb20e7682c1f4fce8dcab30bf01d1e07a" @@ -6491,10 +6432,10 @@ stream-combiner2@^1.1.1: readable-stream "^2.0.2" stream-http@^2.0.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.4.1.tgz#8ee5689ae69169e8eb8edd6aeb2ca08ab47e8f59" + version "2.6.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3" dependencies: - builtin-status-codes "^2.0.0" + builtin-status-codes "^3.0.0" inherits "^2.0.1" readable-stream "^2.1.0" to-arraybuffer "^1.0.0" @@ -6596,9 +6537,9 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" +supports-color@^3.1.2, supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: has-flag "^1.0.0" @@ -6614,8 +6555,8 @@ symbol-observable@^1.0.2: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" "symbol-tree@>= 3.1.0 < 4.0.0": - version "3.1.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.1.4.tgz#02b279348d337debc39694c5c95f882d448a312a" + version "3.2.1" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.1.tgz#8549dd1d01fa9f893c18cc9ab0b106b4d9b168cb" syntax-error@^1.1.1: version "1.1.6" @@ -6623,32 +6564,6 @@ syntax-error@^1.1.1: dependencies: acorn "^2.7.0" -systemjs-builder@0.15.33, systemjs-builder@^0.15.33: - version "0.15.33" - resolved "https://registry.yarnpkg.com/systemjs-builder/-/systemjs-builder-0.15.33.tgz#7bd4d045769a67b52f9596141ba21cd94b49910c" - dependencies: - babel-core "^6.9.0" - babel-plugin-transform-cjs-system-wrapper "^0.2.1" - babel-plugin-transform-es2015-modules-systemjs "^6.6.5" - babel-plugin-transform-global-system-wrapper "0.0.1" - babel-plugin-transform-system-register "0.0.1" - bluebird "^3.3.4" - data-uri-to-buffer "0.0.4" - es6-template-strings "^2.0.0" - glob "^7.0.3" - mkdirp "^0.5.1" - rollup "^0.36.3" - source-map "^0.5.3" - systemjs "^0.19.39" - traceur "0.0.105" - uglify-js "^2.6.1" - -systemjs@0.19.40, systemjs@^0.19.39: - version "0.19.40" - resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-0.19.40.tgz#158f64a9f4ef541a7fda6b40e527ee46b6c54cd0" - dependencies: - when "^3.7.5" - table@^3.7.8: version "3.8.3" resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" @@ -6660,14 +6575,6 @@ table@^3.7.8: slice-ansi "0.0.4" string-width "^2.0.0" -tar-fs@^1.13.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.14.0.tgz#f99cc074bf33bed21cd921a21720797bb18e6c96" - dependencies: - mkdirp "^0.5.0" - pump "^1.0.0" - tar-stream "^1.1.2" - tar-pack@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" @@ -6681,15 +6588,6 @@ tar-pack@~3.3.0: tar "~2.2.1" uid-number "~0.0.6" -tar-stream@^1.1.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.2.tgz#fbc6c6e83c1a19d4cb48c7d96171fc248effc7bf" - dependencies: - bl "^1.0.0" - end-of-stream "^1.0.0" - readable-stream "^2.0.0" - xtend "^4.0.0" - tar@~2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" @@ -6715,9 +6613,15 @@ test-exclude@^2.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" -testcheck@^0.1.0: - version "0.1.4" - resolved "https://registry.yarnpkg.com/testcheck/-/testcheck-0.1.4.tgz#90056edd48d11997702616ce6716f197d8190164" +test-exclude@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-3.3.0.tgz#7a17ca1239988c98367b0621456dbb7d4bc38977" + dependencies: + arrify "^1.0.1" + micromatch "^2.3.11" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" text-table@~0.2.0: version "0.2.0" @@ -6812,16 +6716,6 @@ tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" -traceur@0.0.105: - version "0.0.105" - resolved "https://registry.yarnpkg.com/traceur/-/traceur-0.0.105.tgz#5cf9dee83d6b77861c3d6c44d53859aed7ab0479" - dependencies: - commander "2.9.x" - glob "5.0.x" - rsvp "^3.0.13" - semver "^4.3.3" - source-map-support "~0.2.8" - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -6946,7 +6840,7 @@ type-is@~1.6.10, type-is@~1.6.13: media-typer "0.3.0" mime-types "~2.1.11" -typedarray@~0.0.5: +typedarray@^0.0.6, typedarray@~0.0.5: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -6954,9 +6848,9 @@ ua-parser-js@^0.7.9: version "0.7.10" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f" -uglify-js@2.x.x, uglify-js@^2.6, uglify-js@^2.6.1: - version "2.7.4" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.4.tgz#a295a0de12b6a650c031c40deb0dc40b14568bd2" +uglify-js@2.x.x, uglify-js@^2.6: + version "2.7.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.7.5.tgz#4612c0c7baaee2ba7c487de4904ae122079f2ca8" dependencies: async "~0.2.6" source-map "~0.5.1" @@ -6985,10 +6879,6 @@ umd@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/umd/-/umd-3.0.1.tgz#8ae556e11011f63c2596708a8837259f01b3d60e" -unc-path-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - uncontrollable@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/uncontrollable/-/uncontrollable-4.0.3.tgz#06ec76cb9e02914756085d9cea0354fc746b09b4" @@ -7015,6 +6905,13 @@ uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" +units-css@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07" + dependencies: + isnumeric "^0.2.0" + viewport-dimensions "^0.2.0" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -7063,9 +6960,13 @@ util@0.10.3, util@~0.10.1: dependencies: inherits "2.0.1" -uuid@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" +uuid@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" validate-npm-package-license@^3.0.1: version "3.0.1" @@ -7088,6 +6989,10 @@ verror@1.3.6: dependencies: extsprintf "1.0.2" +viewport-dimensions@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c" + vm-browserify@~0.0.1: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" @@ -7128,11 +7033,11 @@ watchify-middleware@^1.6.0: watchify "^3.3.1" watchify@^3.3.1, watchify@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.7.0.tgz#ee2f2c5c8c37312303f998b818b2b3450eefe648" + version "3.9.0" + resolved "https://registry.yarnpkg.com/watchify/-/watchify-3.9.0.tgz#f075fd2e8a86acde84cedba6e5c2a0bedd523d9e" dependencies: anymatch "^1.3.0" - browserify "^13.0.0" + browserify "^14.0.0" chokidar "^1.0.0" defined "^1.0.0" outpipe "^1.1.0" @@ -7167,24 +7072,20 @@ whatwg-fetch@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-0.9.0.tgz#0e3684c6cb9995b43efc9df03e4c365d95fd9cc0" -whatwg-url@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-3.0.0.tgz#b9033c50c7ce763e91d78777ce825a6d7f56dac5" +whatwg-url@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.3.0.tgz#92aaee21f4f2a642074357d70ef8500a7cbb171a" dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" -when@^3.7.5: - version "3.7.7" - resolved "https://registry.yarnpkg.com/when/-/when-3.7.7.tgz#aba03fc3bb736d6c88b091d013d8a8e590d84718" - which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" -which@^1.0.5, which@^1.0.9, which@^1.1.1, which@^1.2.10, which@^1.2.4: - version "1.2.11" - resolved "https://registry.yarnpkg.com/which/-/which-1.2.11.tgz#c8b2eeea6b8c1659fa7c1dd4fdaabe9533dc5e8b" +which@^1.0.5, which@^1.1.1, which@^1.2.12, which@^1.2.4: + version "1.2.12" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.12.tgz#de67b5e450269f194909ef23ece4ebe416fa1192" dependencies: isexe "^1.1.1" @@ -7202,19 +7103,15 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - word-wrap@^1.0.3: - version "1.1.0" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.1.0.tgz#356153d61d10610d600785c5d701288e0ae764a6" + version "1.2.1" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.1.tgz#248f459b465d179a17bc407c854d3151d07e45d8" wordwrap@0.0.2, wordwrap@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" -wordwrap@^1.0.0, wordwrap@~1.0.0: +wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -7226,10 +7123,11 @@ worker-farm@^1.3.1: xtend ">=4.0.0 <4.1.0-0" wrap-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.0.0.tgz#7d30f8f873f9a5bbc3a64dabc8d177e071ae426f" + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" dependencies: string-width "^1.0.1" + strip-ansi "^3.0.1" wrappy@1: version "1.0.2" @@ -7279,21 +7177,20 @@ yamljs@^0.2.8: argparse "^1.0.7" glob "^7.0.5" -yargs-parser@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-3.2.0.tgz#5081355d19d9d0c8c5d81ada908cb4e6d186664f" +yargs-parser@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" dependencies: camelcase "^3.0.0" - lodash.assign "^4.1.0" -yargs@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e" +yargs@^6.3.0: + version "6.6.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" dependencies: + camelcase "^3.0.0" cliui "^3.2.0" decamelize "^1.1.1" get-caller-file "^1.0.1" - lodash.assign "^4.2.0" os-locale "^1.4.0" read-pkg-up "^1.0.1" require-directory "^2.1.1" @@ -7301,9 +7198,8 @@ yargs@^5.0.0: set-blocking "^2.0.0" string-width "^1.0.2" which-module "^1.0.0" - window-size "^0.2.0" y18n "^3.2.1" - yargs-parser "^3.2.0" + yargs-parser "^4.2.0" yargs@~3.10.0: version "3.10.0" From eef8992c39bab01f3aa2d55c55a2192839b8ead3 Mon Sep 17 00:00:00 2001 From: Evan Siroky Date: Mon, 30 Jan 2017 22:36:51 -0800 Subject: [PATCH 243/323] style(client): fix linting issues --- .../client/editor/components/EditorInput.js | 11 +- .../editor/components/map/ControlPoint.js | 2 +- .../client/editor/components/map/EditorMap.js | 2 +- .../editor/components/timetable/Timetable.js | 3 +- .../gtfsplus/components/GtfsPlusTable.js | 11 +- src/main/client/gtfsplus/reducers/gtfsplus.js | 50 ++--- src/main/client/manager/reducers/projects.js | 199 +++++++++++------- 7 files changed, 163 insertions(+), 115 deletions(-) diff --git a/src/main/client/editor/components/EditorInput.js b/src/main/client/editor/components/EditorInput.js index 094e4565c..95870df80 100644 --- a/src/main/client/editor/components/EditorInput.js +++ b/src/main/client/editor/components/EditorInput.js @@ -443,11 +443,12 @@ export default class EditorInput extends Component { case 'GTFS_ROUTE': const routeEntity = getGtfsEntity('route', currentValue) const routeValue = routeEntity - ? { 'value': routeEntity.route_id, - 'label': routeEntity.route_short_name - ? `${routeEntity.route_short_name} - ${routeEntity.route_long_name}` - : routeEntity.route_long_name - } + ? { + 'value': routeEntity.route_id, + 'label': routeEntity.route_short_name + ? `${routeEntity.route_short_name} - ${routeEntity.route_long_name}` + : routeEntity.route_long_name + } : '' return ( - ) + ) } } diff --git a/src/main/client/editor/components/map/EditorMap.js b/src/main/client/editor/components/map/EditorMap.js index a9976211e..e0905335a 100644 --- a/src/main/client/editor/components/map/EditorMap.js +++ b/src/main/client/editor/components/map/EditorMap.js @@ -213,7 +213,7 @@ export default class EditorMap extends Component { updateActiveEntity={this.props.updateActiveEntity} setActiveEntity={this.props.setActiveEntity} feedSource={this.props.feedSource} /> - ) + ) default: return null } diff --git a/src/main/client/editor/components/timetable/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js index 41b4f051b..332798303 100644 --- a/src/main/client/editor/components/timetable/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -392,7 +392,8 @@ export default class Timetable extends Component { width={width - scrollbarSize() - columnWidth} />

    - ) }} + ) + }}
    diff --git a/src/main/client/gtfsplus/components/GtfsPlusTable.js b/src/main/client/gtfsplus/components/GtfsPlusTable.js index 1079e3e45..17f7dc00e 100644 --- a/src/main/client/gtfsplus/components/GtfsPlusTable.js +++ b/src/main/client/gtfsplus/components/GtfsPlusTable.js @@ -84,11 +84,12 @@ export default class GtfsPlusTable extends Component { const routeEntity = this.props.getGtfsEntity('route', currentValue) const routeValue = routeEntity - ? { 'value': routeEntity.route_id, - 'label': routeEntity.route_short_name - ? `${routeEntity.route_short_name} - ${routeEntity.route_long_name}` - : routeEntity.route_long_name - } + ? { + 'value': routeEntity.route_id, + 'label': routeEntity.route_short_name + ? `${routeEntity.route_short_name} - ${routeEntity.route_long_name}` + : routeEntity.route_long_name + } : '' return ( diff --git a/src/main/client/gtfsplus/reducers/gtfsplus.js b/src/main/client/gtfsplus/reducers/gtfsplus.js index bb6b4e813..46b5043a8 100644 --- a/src/main/client/gtfsplus/reducers/gtfsplus.js +++ b/src/main/client/gtfsplus/reducers/gtfsplus.js @@ -66,38 +66,38 @@ const gtfsplus = (state = { ) } // otherwise, add it to the exising table - return update(state, - {tableData: - {[action.tableId]: - {$push: [action.rowData]} + return update(state, { + tableData: { + [action.tableId]: { + $push: [action.rowData] } } - ) + }) case 'UPDATE_GTFSPLUS_FIELD': - return update(state, - {tableData: - {[action.tableId]: - {[action.rowIndex]: - {[action.fieldName]: - {$set: action.newValue} + return update(state, { + tableData: { + [action.tableId]: { + [action.rowIndex]: { + [action.fieldName]: { + $set: action.newValue } } } } - ) + }) case 'DELETE_GTFSPLUS_ROW': const table = state.tableData[action.tableId] const newTable = [ ...table.slice(0, action.rowIndex), ...table.slice(action.rowIndex + 1) ] - return update(state, - {tableData: - {[action.tableId]: - {$set: newTable} + return update(state, { + tableData: { + [action.tableId]: { + $set: newTable } } - ) + }) case 'RECEIVE_GTFS_PLUS_ENTITIES': const getType = function (entity) { if (entity.hasOwnProperty('route_id')) return 'route' @@ -110,11 +110,11 @@ const gtfsplus = (state = { newLookupEntries[key] = entity } - return update(state, - {gtfsEntityLookup: - {$merge: newLookupEntries} + return update(state, { + gtfsEntityLookup: { + $merge: newLookupEntries } - ) + }) case 'RECEIVE_GTFSPLUS_VALIDATION': const validationTable = {} for (let issue of action.validationIssues) { @@ -123,11 +123,11 @@ const gtfsplus = (state = { } validationTable[issue.tableId].push(issue) } - return update(state, - {validation: - {$set: validationTable} + return update(state, { + validation: { + $set: validationTable } - ) + }) default: return state diff --git a/src/main/client/manager/reducers/projects.js b/src/main/client/manager/reducers/projects.js index 0f87d9307..07b5bba45 100644 --- a/src/main/client/manager/reducers/projects.js +++ b/src/main/client/manager/reducers/projects.js @@ -158,15 +158,16 @@ const projects = (state = { ...existingSources.slice(sourceIndex + 1) ] } - return update(state, - { - all: - {[projectIndex]: - {$merge: {feedSources: updatedSources}} - }, - isFetching: { $set: false } - } - ) + return update(state, { + all: { + [projectIndex]: { + $merge: { + feedSources: updatedSources + } + } + }, + isFetching: { $set: false } + }) case 'RECEIVE_FEEDVERSIONS': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) @@ -176,17 +177,19 @@ const projects = (state = { if (a.version > b.version) return 1 return 0 } - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {$merge: {feedVersions: action.feedVersions.sort(versionSort)}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + $merge: { + feedVersions: action.feedVersions.sort(versionSort) + } } } } } - ) + }) case 'RECEIVE_FEEDVERSION': projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) @@ -209,26 +212,36 @@ const projects = (state = { ...existingVersions.slice(versionIndex + 1) ] } - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {$merge: {feedVersions: updatedVersions}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + $merge: { + feedVersions: updatedVersions + } } } } } - ) + }) case 'PUBLISHED_FEEDVERSION': projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedVersion.feedSource.id) versionIndex = state.all[projectIndex].feedSources[sourceIndex].feedVersions.findIndex(v => v.id === action.feedVersion.id) - return update(state, - {all: {[projectIndex]: {feedSources: {[sourceIndex]: {publishedVersionId: { - $set: action.feedVersion.id - }}}}}} - ) + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + publishedVersionId: { + $set: action.feedVersion.id + } + } + } + } + } + }) case 'RECEIVE_VALIDATION_RESULT': projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedVersion.feedSource.id) @@ -240,11 +253,23 @@ const projects = (state = { // } // result[error.file].push(error) // }) - return update(state, - {all: {[projectIndex]: {feedSources: {[sourceIndex]: {feedVersions: { - [versionIndex]: {$merge: {validationResult: action.validationResult}} - }}}}}} - ) + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + feedVersions: { + [versionIndex]: { + $merge: { + validationResult: action.validationResult + } + } + } + } + } + } + } + }) case 'RECEIVE_FEEDVERSION_ISOCHRONES': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) @@ -255,38 +280,50 @@ const projects = (state = { f.type = 'Feature' return f }) - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {feedVersions: - {[versionIndex]: - {$merge: {isochrones: action.isochrones}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + feedVersions: { + [versionIndex]: { + $merge: { + isochrones: action.isochrones + } } } } } } } - ) + }) case 'RECEIVE_DEPLOYMENTS': projectIndex = state.all.findIndex(p => p.id === action.projectId) if (state.active && action.projectId === state.active.id) { return update(state, { - active: {$merge: {deployments: action.deployments}}, + active: { + $merge: { + deployments: action.deployments + } + }, all: { - [projectIndex]: {$merge: {deployments: action.deployments}} + [projectIndex]: { + $merge: { + deployments: action.deployments + } + } } }) } else { // if projectId does not match active project - return update(state, - { - all: { - [projectIndex]: {$merge: {deployments: action.deployments}} + return update(state, { + all: { + [projectIndex]: { + $merge: { + deployments: action.deployments + } } } - ) + }) } case 'RECEIVE_DEPLOYMENT': projectIndex = state.all.findIndex(p => p.id === action.deployment.project.id) @@ -305,62 +342,70 @@ const projects = (state = { ...existingDeployments.slice(sourceIndex + 1) ] } - return update(state, - {all: - {[projectIndex]: - {$merge: {deployments: updatedDeployments}} + return update(state, { + all: { + [projectIndex]: { + $merge: { + deployments: updatedDeployments + } } } - ) + }) case 'RECEIVE_NOTES_FOR_FEEDSOURCE': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {$merge: {notes: action.notes}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + $merge: { + notes: action.notes + } } } } } - ) + }) case 'RECEIVE_NOTES_FOR_FEEDVERSION': projectIndex = state.all.findIndex(p => p.id === action.feedVersion.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedVersion.feedSource.id) versionIndex = state.all[projectIndex].feedSources[sourceIndex].feedVersions.findIndex(v => v.id === action.feedVersion.id) - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {feedVersions: - {[versionIndex]: - {$merge: {notes: action.notes}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + feedVersions: { + [versionIndex]: { + $merge: { + notes: action.notes + } } } } } } } - ) + }) case 'RECEIVE_GTFSEDITOR_SNAPSHOTS': projectIndex = state.all.findIndex(p => p.id === action.feedSource.projectId) sourceIndex = state.all[projectIndex].feedSources.findIndex(s => s.id === action.feedSource.id) - return update(state, - {all: - {[projectIndex]: - {feedSources: - {[sourceIndex]: - {$merge: {editorSnapshots: action.snapshots}} + return update(state, { + all: { + [projectIndex]: { + feedSources: { + [sourceIndex]: { + $merge: { + editorSnapshots: action.snapshots + } } } } } - ) + }) default: return state From 2fca4e1e3d919199a882067100ed195337451b24 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 31 Jan 2017 12:59:32 -0500 Subject: [PATCH 244/323] fix(TripPatternController): **actually** delete trips for trip pattern when userFreqency value chang fixes #19 --- src/main/client/admin/reducers/users.js | 34 ++++ src/main/client/alerts/reducers/active.js | 157 +++++++++++++++ src/main/client/signs/reducers/active.js | 183 ++++++++++++++++++ .../api/TripPatternController.java | 13 +- .../api/OrganizationController.java | 129 ++++++++++++ .../manager/models/Organization.java | 81 ++++++++ 6 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 src/main/client/admin/reducers/users.js create mode 100644 src/main/client/alerts/reducers/active.js create mode 100644 src/main/client/signs/reducers/active.js create mode 100644 src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java create mode 100644 src/main/java/com/conveyal/datatools/manager/models/Organization.java diff --git a/src/main/client/admin/reducers/users.js b/src/main/client/admin/reducers/users.js new file mode 100644 index 000000000..5d5d4e8e7 --- /dev/null +++ b/src/main/client/admin/reducers/users.js @@ -0,0 +1,34 @@ +import update from 'react-addons-update' + +const users = (state = { + isFetching: false, + data: null, + userCount: 0, + page: 0, + perPage: 10, + userQueryString: null +}, action) => { + switch (action.type) { + case 'REQUESTING_USERS': + return update(state, {isFetching: { $set: true }}) + case 'RECEIVE_USERS': + return update(state, { + isFetching: { $set: false }, + data: { $set: action.users }, + userCount: { $set: action.totalUserCount } + }) + case 'CREATED_USER': + if (state.data) { + return update(state, {data: { $push: [action.profile] }}) + } + break + case 'SET_USER_PAGE': + return update(state, {page: { $set: action.page }}) + case 'SET_USER_QUERY_STRING': + return update(state, {userQueryString: { $set: action.queryString }}) + default: + return state + } +} + +export default users diff --git a/src/main/client/alerts/reducers/active.js b/src/main/client/alerts/reducers/active.js new file mode 100644 index 000000000..f1d7e977a --- /dev/null +++ b/src/main/client/alerts/reducers/active.js @@ -0,0 +1,157 @@ +import update from 'react-addons-update' + +const active = (state = null, action) => { + let entities, foundIndex, updatedEntity + switch (action.type) { + case 'UPDATE_ACTIVE_ALERT_ALERT': + case 'CREATE_ALERT': + case 'EDIT_ALERT': + return action.alert + case 'SET_ACTIVE_ALERT_TITLE': + return update(state, {title: {$set: action.title}}) + case 'SET_ACTIVE_ALERT_DESCRIPTION': + return update(state, {description: {$set: action.description}}) + case 'SET_ACTIVE_ALERT_URL': + return update(state, {url: {$set: action.url}}) + case 'SET_ACTIVE_ALERT_CAUSE': + return update(state, {cause: {$set: action.cause}}) + case 'SET_ACTIVE_ALERT_EFFECT': + return update(state, {effect: {$set: action.effect}}) + case 'SET_ACTIVE_ALERT_START': + return update(state, {start: {$set: parseInt(action.start)}}) + case 'SET_ACTIVE_ALERT_END': + return update(state, {end: {$set: parseInt(action.end)}}) + case 'SET_ACTIVE_ALERT_PUBLISHED': + return update(state, {published: {$set: action.published}}) + case 'RECEIVED_ALERT_GTFS_ENTITIES': + // TODO: update GTFS entities for active alert + if (state !== null && state.affectedEntities !== null) { + for (var i = 0; i < action.gtfsObjects.length; i++) { + let ent = action.gtfsObjects[i] + if (typeof ent.gtfs !== 'undefined' && ent.AlertId === state.id) { + // let alert = action.gtfsAlerts.find(a => a.id === ent.entity.AlertId) + updatedEntity = state.affectedEntities.find(e => e.id === ent.entity.Id) + updatedEntity[ent.type] = ent.gtfs + // entities.push(selectedEnt) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + } + } + return update(state, {affectedEntities: {$set: entities}}) + } + return state + case 'ADD_ACTIVE_ALERT_AFFECTED_ENTITY': + entities = [...state.affectedEntities, action.entity] + return update(state, {affectedEntities: {$set: entities}}) + case 'UPDATE_ACTIVE_ALERT_ENTITY': + console.log('update entity', action.entity, action.field, action.value) + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + switch (action.field) { + case 'TYPE': + updatedEntity = update(action.entity, { + type: {$set: action.value}, + stop: {$set: null}, + route: {$set: null}, + stop_id: {$set: null}, + route_id: {$set: null} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'AGENCY': + updatedEntity = update(action.entity, {agency: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'MODE': + updatedEntity = update(action.entity, {mode: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'STOP': + let stopId = action.value !== null ? action.value.stop_id : null + console.log(action.entity) + // set route to null if stop is updated for type stop + if (action.entity.type === 'STOP') { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency}, + route: {$set: null}, + route_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + console.log(updatedEntity) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'ROUTE': + let routeId = action.value !== null ? action.value.route_id : null + // set route to null if stop is updated for type stop + if (action.entity.type === 'ROUTE') { + updatedEntity = update(action.entity, { + route: {$set: action.value}, + route_id: {$set: routeId}, + agency: {$set: action.agency}, + stop: {$set: null}, + stop_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + route: {$set: action.value}, + route_id: {$set: routeId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + } + return state + case 'DELETE_ACTIVE_ALERT_AFFECTED_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + return state + + default: + return state + } +} + +export default active diff --git a/src/main/client/signs/reducers/active.js b/src/main/client/signs/reducers/active.js new file mode 100644 index 000000000..c3b2a783e --- /dev/null +++ b/src/main/client/signs/reducers/active.js @@ -0,0 +1,183 @@ +import update from 'react-addons-update' + +const active = (state = null, action) => { + let entities, foundIndex, displayIndex + switch (action.type) { + case 'UPDATE_ACTIVE_SIGN': + return update(state, {$set: action.sign}) + case 'CREATE_SIGN': + case 'EDIT_SIGN': + return action.sign + + case 'SET_ACTIVE_SIGN_TITLE': + return update(state, {title: {$set: action.title}}) + case 'SET_ACTIVE_SIGN_DESCRIPTION': + return update(state, {description: {$set: action.description}}) + case 'SET_ACTIVE_SIGN_URL': + return update(state, {url: {$set: action.url}}) + case 'SET_ACTIVE_SIGN_CAUSE': + return update(state, {cause: {$set: action.cause}}) + case 'SET_ACTIVE_SIGN_EFFECT': + return update(state, {effect: {$set: action.effect}}) + case 'SET_ACTIVE_SIGN_START': + return update(state, {start: {$set: parseInt(action.start)}}) + case 'SET_ACTIVE_SIGN_END': + return update(state, {end: {$set: parseInt(action.end)}}) + case 'SET_ACTIVE_SIGN_PUBLISHED': + return update(state, {published: {$set: action.published}}) + case 'RECEIVED_RTD_DISPLAYS': + if (state !== null) { + let displayMap = {} + for (var i = 0; i < action.rtdDisplays.length; i++) { + let d = action.rtdDisplays[i] + if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) { + continue + } + if (d.DraftDisplayConfigurationId) { + if (displayMap[d.DraftDisplayConfigurationId] && displayMap[d.DraftDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { + displayMap[d.DraftDisplayConfigurationId].push(d) + } else if (!displayMap[d.DraftDisplayConfigurationId]) { + displayMap[d.DraftDisplayConfigurationId] = [] + displayMap[d.DraftDisplayConfigurationId].push(d) + } + } + if (d.PublishedDisplayConfigurationId) { + if (displayMap[d.PublishedDisplayConfigurationId] && displayMap[d.PublishedDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { + displayMap[d.PublishedDisplayConfigurationId].push(d) + } else if (!displayMap[d.PublishedDisplayConfigurationId]) { + displayMap[d.PublishedDisplayConfigurationId] = [] + displayMap[d.PublishedDisplayConfigurationId].push(d) + } + } + } + return update(state, {displays: {$set: displayMap[state.id]}}) + } + return state + case 'RECEIVED_SIGN_GTFS_ENTITIES': + // TODO: update GTFS entities for active sign + if (state !== null && state.affectedEntities !== null) { + for (let i = 0; i < action.gtfsObjects.length; i++) { + let ent = action.gtfsObjects[i] + if (typeof ent.gtfs !== 'undefined' && ent.SignId === state.id) { + // let sign = action.gtfsSigns.find(a => a.id === ent.entity.SignId) + let updatedEntity = state.affectedEntities.find(e => e.id === ent.entity.Id) + updatedEntity[ent.type] = ent.gtfs + // entities.push(selectedEnt) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + } + } + return update(state, {affectedEntities: {$set: entities}}) + } + return state + case 'ADD_ACTIVE_SIGN_AFFECTED_ENTITY': + entities = [...state.affectedEntities, action.entity] + return update(state, {affectedEntities: {$set: entities}}) + case 'UPDATE_DISPLAYS': + return update(state, {displays: {$set: action.displays}}) + case 'TOGGLE_CONFIG_FOR_DISPLAY': + displayIndex = state.displays.findIndex(d => d.Id === action.display.Id) + switch (action.configType) { + case 'DRAFT': + return update(state, {displays: {[displayIndex]: {$merge: {DraftDisplayConfigurationId: action.configId}}}}) + case 'PUBLISHED': + // if setting published config to new value (not null), set draft config to null + // if (action.configId) + // return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId, DraftDisplayConfigurationId: null}}}}) + // else { + return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId}}}}) + // } + } + return state + case 'UPDATE_ACTIVE_SIGN_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + switch (action.field) { + case 'TYPE': + let updatedEntity = update(action.entity, { + type: {$set: action.value}, + stop: {$set: null}, + route: {$set: null}, + stop_id: {$set: null}, + route_id: {$set: null} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'AGENCY': + updatedEntity = update(action.entity, {agency: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'MODE': + updatedEntity = update(action.entity, {mode: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'STOP': + let stopId = action.value !== null ? action.value.stop_id : null + if (action.entity.type === 'STOP') { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency}, + route: {$set: null}, + route_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'ROUTES': + updatedEntity = update(action.entity, { + route: {$set: action.value} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + } + return state + case 'DELETE_ACTIVE_SIGN_AFFECTED_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + return state + + default: + return state + } +} + +export default active diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 3282135ea..1974ca81c 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -155,7 +155,18 @@ public static Object updateTripPattern(Request req, Response res) { tx.rollback(); halt(400); } - + + // check if frequency value has changed for pattern and nuke trips created for old value + // double check that we're working with the same trip pattern here + if (originalTripPattern.useFrequency != tripPattern.useFrequency) { + for (Trip trip : tx.getTripsByPattern(originalTripPattern.id)) { + if (originalTripPattern.useFrequency == trip.useFrequency) { + LOG.info("Removing frequency={} trip {}", trip.useFrequency, trip.id); + tx.trips.remove(trip.id); + } + } + } + // update stop times try { TripPattern.reconcilePatternStops(originalTripPattern, tripPattern, tx); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java new file mode 100644 index 000000000..f93ac9a79 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java @@ -0,0 +1,129 @@ +package com.conveyal.datatools.manager.controllers.api; + +import com.conveyal.datatools.manager.auth.Auth0UserProfile; +import com.conveyal.datatools.manager.models.JsonViews; +import com.conveyal.datatools.manager.models.Organization; +import com.conveyal.datatools.manager.utils.json.JsonManager; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import spark.Request; +import spark.Response; + +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import static spark.Spark.*; +import static spark.Spark.get; + +/** + * Created by landon on 1/30/17. + */ +public class OrganizationController { + public static JsonManager json = new JsonManager<>(Organization.class, JsonViews.UserInterface.class); + public static final Logger LOG = LoggerFactory.getLogger(OrganizationController.class); + private static ObjectMapper mapper = new ObjectMapper(); + + public static Organization getOrganization (Request req, Response res) { + String id = req.params("id"); + if (id == null) { + halt(400, "Must specify valid organization id"); + } + Organization org = Organization.get(id); + return org; + } + + public static Collection getAllOrganizations (Request req, Response res) { + Auth0UserProfile userProfile = req.attribute("user"); + if (userProfile.canAdministerApplication()) { + return Organization.getAll(); + } else { + halt(401, "Must be application admin to view organizations"); + } + return null; + } + + public static Organization createOrganization (Request req, Response res) { + Auth0UserProfile userProfile = req.attribute("user"); + if (userProfile.canAdministerApplication()) { + Organization org = null; + try { + org = mapper.readValue(req.body(), Organization.class); + org.save(); + } catch (IOException e) { + LOG.warn("Could not create organization", e); + halt(400, e.getMessage()); + } + return org; + } else { + halt(401, "Must be application admin to view organizations"); + } + return null; + } + + public static Organization updateOrganization (Request req, Response res) throws IOException { + Organization org = requestOrganizationById(req); + applyJsonToOrganization(org, req.body()); + org.save(); + return org; + } + + private static void applyJsonToOrganization(Organization org, String json) throws IOException { + JsonNode node = mapper.readTree(json); + Iterator> fieldsIter = node.fields(); + while (fieldsIter.hasNext()) { + Map.Entry entry = fieldsIter.next(); + + if(entry.getKey().equals("name")) { + org.name = entry.getValue().asText(); + } else if(entry.getKey().equals("logoUrl")) { + org.logoUrl = entry.getValue().asText(); + } else if(entry.getKey().equals("usageTier")) { + org.usageTier = Organization.UsageTier.valueOf(entry.getValue().asText()); + } else if(entry.getKey().equals("active")) { + org.active = entry.getValue().asBoolean(); +// } else if(entry.getKey().equals("extensions")) { +// org.extensions = entry.getValue().asBoolean(); +// } else if(entry.getKey().equals("subscriptionBeginDate")) { +// org.subscriptionBeginDate = Date.from; + } + } + } + + public static Organization deleteOrganization (Request req, Response res) { + Organization org = requestOrganizationById(req); + org.delete(); + return org; + } + + private static Organization requestOrganizationById(Request req) { + Auth0UserProfile userProfile = req.attribute("user"); + String id = req.params("id"); + if (id == null) { + halt(400, "Must specify valid organization id"); + } + if (userProfile.canAdministerApplication()) { + Organization org = Organization.get(id); + if (org == null) { + halt(400, "Organization does not exist"); + } + return org; + } else { + halt(401, "Must be application admin to modify organization"); + } + return null; + } + + public static void register (String apiPrefix) { + options(apiPrefix + "secure/organization", (q, s) -> ""); + options(apiPrefix + "secure/organization/:id", (q, s) -> ""); + get(apiPrefix + "secure/organization/:id", OrganizationController::getOrganization, json::write); + get(apiPrefix + "secure/organization", OrganizationController::getAllOrganizations, json::write); + post(apiPrefix + "secure/organization", OrganizationController::createOrganization, json::write); + put(apiPrefix + "secure/organization/:id", OrganizationController::updateOrganization, json::write); + delete(apiPrefix + "secure/organization/:id", OrganizationController::deleteOrganization, json::write); + } +} diff --git a/src/main/java/com/conveyal/datatools/manager/models/Organization.java b/src/main/java/com/conveyal/datatools/manager/models/Organization.java new file mode 100644 index 000000000..1f897ee74 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/manager/models/Organization.java @@ -0,0 +1,81 @@ +package com.conveyal.datatools.manager.models; + +import com.conveyal.datatools.manager.persistence.DataStore; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.stream.Collectors; + +/** + * Created by landon on 1/30/17. + */ +public class Organization extends Model implements Serializable { + private static final long serialVersionUID = 1L; + private static DataStore organizationStore = new DataStore<>("organizations"); + + public String name; + public String logoUrl; + public boolean active; + public UsageTier usageTier; + public Extension[] extensions; + public Date subscriptionBeginDate; + public Date subscriptionEndDate; + + public Organization () {} + + public void save () { + save(true); + } + + public void save(boolean commit) { + if (commit) + organizationStore.save(id, this); + else + organizationStore.saveWithoutCommit(id, this); + } + + public static Organization get (String id) { + return organizationStore.getById(id); + } + + public static Collection getAll() { + return organizationStore.getAll(); + } + + public static void commit() { + organizationStore.commit(); + } + + public void delete() { + for (Project p : getProjects()) { + p.delete(); + } + organizationStore.delete(this.id); + } + + public Collection getProjects () { + return Project.getAll().stream().filter(p -> p.organizationId == this.id).collect(Collectors.toList()); + } + + /** + * Created by landon on 1/30/17. + */ + public static enum Extension { + GTFS_PLUS, + DEPLOYMENT, + VALIDATOR, + ALERTS, + SIGN_CONFIG; + + } + + /** + * Created by landon on 1/30/17. + */ + public static enum UsageTier { + LOW, + MEDIUM, + HIGH + } +} From f9867e151ec900a91ff760833b502284fb33abb3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 31 Jan 2017 12:59:32 -0500 Subject: [PATCH 245/323] fix(TripPatternController): **actually** delete trips for trip pattern when userFreqency value chang fixes #19 --- src/main/client/admin/reducers/users.js | 34 ++++ src/main/client/alerts/reducers/active.js | 157 +++++++++++++++ src/main/client/signs/reducers/active.js | 183 ++++++++++++++++++ .../api/TripPatternController.java | 13 +- .../api/OrganizationController.java | 129 ++++++++++++ .../manager/models/Organization.java | 81 ++++++++ 6 files changed, 596 insertions(+), 1 deletion(-) create mode 100644 src/main/client/admin/reducers/users.js create mode 100644 src/main/client/alerts/reducers/active.js create mode 100644 src/main/client/signs/reducers/active.js create mode 100644 src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java create mode 100644 src/main/java/com/conveyal/datatools/manager/models/Organization.java diff --git a/src/main/client/admin/reducers/users.js b/src/main/client/admin/reducers/users.js new file mode 100644 index 000000000..5d5d4e8e7 --- /dev/null +++ b/src/main/client/admin/reducers/users.js @@ -0,0 +1,34 @@ +import update from 'react-addons-update' + +const users = (state = { + isFetching: false, + data: null, + userCount: 0, + page: 0, + perPage: 10, + userQueryString: null +}, action) => { + switch (action.type) { + case 'REQUESTING_USERS': + return update(state, {isFetching: { $set: true }}) + case 'RECEIVE_USERS': + return update(state, { + isFetching: { $set: false }, + data: { $set: action.users }, + userCount: { $set: action.totalUserCount } + }) + case 'CREATED_USER': + if (state.data) { + return update(state, {data: { $push: [action.profile] }}) + } + break + case 'SET_USER_PAGE': + return update(state, {page: { $set: action.page }}) + case 'SET_USER_QUERY_STRING': + return update(state, {userQueryString: { $set: action.queryString }}) + default: + return state + } +} + +export default users diff --git a/src/main/client/alerts/reducers/active.js b/src/main/client/alerts/reducers/active.js new file mode 100644 index 000000000..f1d7e977a --- /dev/null +++ b/src/main/client/alerts/reducers/active.js @@ -0,0 +1,157 @@ +import update from 'react-addons-update' + +const active = (state = null, action) => { + let entities, foundIndex, updatedEntity + switch (action.type) { + case 'UPDATE_ACTIVE_ALERT_ALERT': + case 'CREATE_ALERT': + case 'EDIT_ALERT': + return action.alert + case 'SET_ACTIVE_ALERT_TITLE': + return update(state, {title: {$set: action.title}}) + case 'SET_ACTIVE_ALERT_DESCRIPTION': + return update(state, {description: {$set: action.description}}) + case 'SET_ACTIVE_ALERT_URL': + return update(state, {url: {$set: action.url}}) + case 'SET_ACTIVE_ALERT_CAUSE': + return update(state, {cause: {$set: action.cause}}) + case 'SET_ACTIVE_ALERT_EFFECT': + return update(state, {effect: {$set: action.effect}}) + case 'SET_ACTIVE_ALERT_START': + return update(state, {start: {$set: parseInt(action.start)}}) + case 'SET_ACTIVE_ALERT_END': + return update(state, {end: {$set: parseInt(action.end)}}) + case 'SET_ACTIVE_ALERT_PUBLISHED': + return update(state, {published: {$set: action.published}}) + case 'RECEIVED_ALERT_GTFS_ENTITIES': + // TODO: update GTFS entities for active alert + if (state !== null && state.affectedEntities !== null) { + for (var i = 0; i < action.gtfsObjects.length; i++) { + let ent = action.gtfsObjects[i] + if (typeof ent.gtfs !== 'undefined' && ent.AlertId === state.id) { + // let alert = action.gtfsAlerts.find(a => a.id === ent.entity.AlertId) + updatedEntity = state.affectedEntities.find(e => e.id === ent.entity.Id) + updatedEntity[ent.type] = ent.gtfs + // entities.push(selectedEnt) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + } + } + return update(state, {affectedEntities: {$set: entities}}) + } + return state + case 'ADD_ACTIVE_ALERT_AFFECTED_ENTITY': + entities = [...state.affectedEntities, action.entity] + return update(state, {affectedEntities: {$set: entities}}) + case 'UPDATE_ACTIVE_ALERT_ENTITY': + console.log('update entity', action.entity, action.field, action.value) + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + switch (action.field) { + case 'TYPE': + updatedEntity = update(action.entity, { + type: {$set: action.value}, + stop: {$set: null}, + route: {$set: null}, + stop_id: {$set: null}, + route_id: {$set: null} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'AGENCY': + updatedEntity = update(action.entity, {agency: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'MODE': + updatedEntity = update(action.entity, {mode: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'STOP': + let stopId = action.value !== null ? action.value.stop_id : null + console.log(action.entity) + // set route to null if stop is updated for type stop + if (action.entity.type === 'STOP') { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency}, + route: {$set: null}, + route_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + console.log(updatedEntity) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'ROUTE': + let routeId = action.value !== null ? action.value.route_id : null + // set route to null if stop is updated for type stop + if (action.entity.type === 'ROUTE') { + updatedEntity = update(action.entity, { + route: {$set: action.value}, + route_id: {$set: routeId}, + agency: {$set: action.agency}, + stop: {$set: null}, + stop_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + route: {$set: action.value}, + route_id: {$set: routeId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + } + return state + case 'DELETE_ACTIVE_ALERT_AFFECTED_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + return state + + default: + return state + } +} + +export default active diff --git a/src/main/client/signs/reducers/active.js b/src/main/client/signs/reducers/active.js new file mode 100644 index 000000000..c3b2a783e --- /dev/null +++ b/src/main/client/signs/reducers/active.js @@ -0,0 +1,183 @@ +import update from 'react-addons-update' + +const active = (state = null, action) => { + let entities, foundIndex, displayIndex + switch (action.type) { + case 'UPDATE_ACTIVE_SIGN': + return update(state, {$set: action.sign}) + case 'CREATE_SIGN': + case 'EDIT_SIGN': + return action.sign + + case 'SET_ACTIVE_SIGN_TITLE': + return update(state, {title: {$set: action.title}}) + case 'SET_ACTIVE_SIGN_DESCRIPTION': + return update(state, {description: {$set: action.description}}) + case 'SET_ACTIVE_SIGN_URL': + return update(state, {url: {$set: action.url}}) + case 'SET_ACTIVE_SIGN_CAUSE': + return update(state, {cause: {$set: action.cause}}) + case 'SET_ACTIVE_SIGN_EFFECT': + return update(state, {effect: {$set: action.effect}}) + case 'SET_ACTIVE_SIGN_START': + return update(state, {start: {$set: parseInt(action.start)}}) + case 'SET_ACTIVE_SIGN_END': + return update(state, {end: {$set: parseInt(action.end)}}) + case 'SET_ACTIVE_SIGN_PUBLISHED': + return update(state, {published: {$set: action.published}}) + case 'RECEIVED_RTD_DISPLAYS': + if (state !== null) { + let displayMap = {} + for (var i = 0; i < action.rtdDisplays.length; i++) { + let d = action.rtdDisplays[i] + if (!d.DraftDisplayConfigurationId && !d.PublishedDisplayConfigurationId) { + continue + } + if (d.DraftDisplayConfigurationId) { + if (displayMap[d.DraftDisplayConfigurationId] && displayMap[d.DraftDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { + displayMap[d.DraftDisplayConfigurationId].push(d) + } else if (!displayMap[d.DraftDisplayConfigurationId]) { + displayMap[d.DraftDisplayConfigurationId] = [] + displayMap[d.DraftDisplayConfigurationId].push(d) + } + } + if (d.PublishedDisplayConfigurationId) { + if (displayMap[d.PublishedDisplayConfigurationId] && displayMap[d.PublishedDisplayConfigurationId].findIndex(display => display.Id === d.Id) === -1) { + displayMap[d.PublishedDisplayConfigurationId].push(d) + } else if (!displayMap[d.PublishedDisplayConfigurationId]) { + displayMap[d.PublishedDisplayConfigurationId] = [] + displayMap[d.PublishedDisplayConfigurationId].push(d) + } + } + } + return update(state, {displays: {$set: displayMap[state.id]}}) + } + return state + case 'RECEIVED_SIGN_GTFS_ENTITIES': + // TODO: update GTFS entities for active sign + if (state !== null && state.affectedEntities !== null) { + for (let i = 0; i < action.gtfsObjects.length; i++) { + let ent = action.gtfsObjects[i] + if (typeof ent.gtfs !== 'undefined' && ent.SignId === state.id) { + // let sign = action.gtfsSigns.find(a => a.id === ent.entity.SignId) + let updatedEntity = state.affectedEntities.find(e => e.id === ent.entity.Id) + updatedEntity[ent.type] = ent.gtfs + // entities.push(selectedEnt) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + } + } + return update(state, {affectedEntities: {$set: entities}}) + } + return state + case 'ADD_ACTIVE_SIGN_AFFECTED_ENTITY': + entities = [...state.affectedEntities, action.entity] + return update(state, {affectedEntities: {$set: entities}}) + case 'UPDATE_DISPLAYS': + return update(state, {displays: {$set: action.displays}}) + case 'TOGGLE_CONFIG_FOR_DISPLAY': + displayIndex = state.displays.findIndex(d => d.Id === action.display.Id) + switch (action.configType) { + case 'DRAFT': + return update(state, {displays: {[displayIndex]: {$merge: {DraftDisplayConfigurationId: action.configId}}}}) + case 'PUBLISHED': + // if setting published config to new value (not null), set draft config to null + // if (action.configId) + // return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId, DraftDisplayConfigurationId: null}}}}) + // else { + return update(state, {displays: {[displayIndex]: {$merge: {PublishedDisplayConfigurationId: action.configId}}}}) + // } + } + return state + case 'UPDATE_ACTIVE_SIGN_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + switch (action.field) { + case 'TYPE': + let updatedEntity = update(action.entity, { + type: {$set: action.value}, + stop: {$set: null}, + route: {$set: null}, + stop_id: {$set: null}, + route_id: {$set: null} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'AGENCY': + updatedEntity = update(action.entity, {agency: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'MODE': + updatedEntity = update(action.entity, {mode: {$set: action.value}}) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'STOP': + let stopId = action.value !== null ? action.value.stop_id : null + if (action.entity.type === 'STOP') { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency}, + route: {$set: null}, + route_id: {$set: null} + // TODO: update agency id from feed id? + }) + } else { + updatedEntity = update(action.entity, { + stop: {$set: action.value}, + stop_id: {$set: stopId}, + agency: {$set: action.agency} + // TODO: update agency id from feed id? + }) + } + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + case 'ROUTES': + updatedEntity = update(action.entity, { + route: {$set: action.value} + }) + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + updatedEntity, + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + } + return state + case 'DELETE_ACTIVE_SIGN_AFFECTED_ENTITY': + foundIndex = state.affectedEntities.findIndex(e => e.id === action.entity.id) + if (foundIndex !== -1) { + entities = [ + ...state.affectedEntities.slice(0, foundIndex), + ...state.affectedEntities.slice(foundIndex + 1) + ] + return update(state, {affectedEntities: {$set: entities}}) + } + return state + + default: + return state + } +} + +export default active diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java index 3282135ea..1974ca81c 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/TripPatternController.java @@ -155,7 +155,18 @@ public static Object updateTripPattern(Request req, Response res) { tx.rollback(); halt(400); } - + + // check if frequency value has changed for pattern and nuke trips created for old value + // double check that we're working with the same trip pattern here + if (originalTripPattern.useFrequency != tripPattern.useFrequency) { + for (Trip trip : tx.getTripsByPattern(originalTripPattern.id)) { + if (originalTripPattern.useFrequency == trip.useFrequency) { + LOG.info("Removing frequency={} trip {}", trip.useFrequency, trip.id); + tx.trips.remove(trip.id); + } + } + } + // update stop times try { TripPattern.reconcilePatternStops(originalTripPattern, tripPattern, tx); diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java new file mode 100644 index 000000000..f93ac9a79 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/OrganizationController.java @@ -0,0 +1,129 @@ +package com.conveyal.datatools.manager.controllers.api; + +import com.conveyal.datatools.manager.auth.Auth0UserProfile; +import com.conveyal.datatools.manager.models.JsonViews; +import com.conveyal.datatools.manager.models.Organization; +import com.conveyal.datatools.manager.utils.json.JsonManager; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import spark.Request; +import spark.Response; + +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import static spark.Spark.*; +import static spark.Spark.get; + +/** + * Created by landon on 1/30/17. + */ +public class OrganizationController { + public static JsonManager json = new JsonManager<>(Organization.class, JsonViews.UserInterface.class); + public static final Logger LOG = LoggerFactory.getLogger(OrganizationController.class); + private static ObjectMapper mapper = new ObjectMapper(); + + public static Organization getOrganization (Request req, Response res) { + String id = req.params("id"); + if (id == null) { + halt(400, "Must specify valid organization id"); + } + Organization org = Organization.get(id); + return org; + } + + public static Collection getAllOrganizations (Request req, Response res) { + Auth0UserProfile userProfile = req.attribute("user"); + if (userProfile.canAdministerApplication()) { + return Organization.getAll(); + } else { + halt(401, "Must be application admin to view organizations"); + } + return null; + } + + public static Organization createOrganization (Request req, Response res) { + Auth0UserProfile userProfile = req.attribute("user"); + if (userProfile.canAdministerApplication()) { + Organization org = null; + try { + org = mapper.readValue(req.body(), Organization.class); + org.save(); + } catch (IOException e) { + LOG.warn("Could not create organization", e); + halt(400, e.getMessage()); + } + return org; + } else { + halt(401, "Must be application admin to view organizations"); + } + return null; + } + + public static Organization updateOrganization (Request req, Response res) throws IOException { + Organization org = requestOrganizationById(req); + applyJsonToOrganization(org, req.body()); + org.save(); + return org; + } + + private static void applyJsonToOrganization(Organization org, String json) throws IOException { + JsonNode node = mapper.readTree(json); + Iterator> fieldsIter = node.fields(); + while (fieldsIter.hasNext()) { + Map.Entry entry = fieldsIter.next(); + + if(entry.getKey().equals("name")) { + org.name = entry.getValue().asText(); + } else if(entry.getKey().equals("logoUrl")) { + org.logoUrl = entry.getValue().asText(); + } else if(entry.getKey().equals("usageTier")) { + org.usageTier = Organization.UsageTier.valueOf(entry.getValue().asText()); + } else if(entry.getKey().equals("active")) { + org.active = entry.getValue().asBoolean(); +// } else if(entry.getKey().equals("extensions")) { +// org.extensions = entry.getValue().asBoolean(); +// } else if(entry.getKey().equals("subscriptionBeginDate")) { +// org.subscriptionBeginDate = Date.from; + } + } + } + + public static Organization deleteOrganization (Request req, Response res) { + Organization org = requestOrganizationById(req); + org.delete(); + return org; + } + + private static Organization requestOrganizationById(Request req) { + Auth0UserProfile userProfile = req.attribute("user"); + String id = req.params("id"); + if (id == null) { + halt(400, "Must specify valid organization id"); + } + if (userProfile.canAdministerApplication()) { + Organization org = Organization.get(id); + if (org == null) { + halt(400, "Organization does not exist"); + } + return org; + } else { + halt(401, "Must be application admin to modify organization"); + } + return null; + } + + public static void register (String apiPrefix) { + options(apiPrefix + "secure/organization", (q, s) -> ""); + options(apiPrefix + "secure/organization/:id", (q, s) -> ""); + get(apiPrefix + "secure/organization/:id", OrganizationController::getOrganization, json::write); + get(apiPrefix + "secure/organization", OrganizationController::getAllOrganizations, json::write); + post(apiPrefix + "secure/organization", OrganizationController::createOrganization, json::write); + put(apiPrefix + "secure/organization/:id", OrganizationController::updateOrganization, json::write); + delete(apiPrefix + "secure/organization/:id", OrganizationController::deleteOrganization, json::write); + } +} diff --git a/src/main/java/com/conveyal/datatools/manager/models/Organization.java b/src/main/java/com/conveyal/datatools/manager/models/Organization.java new file mode 100644 index 000000000..1f897ee74 --- /dev/null +++ b/src/main/java/com/conveyal/datatools/manager/models/Organization.java @@ -0,0 +1,81 @@ +package com.conveyal.datatools.manager.models; + +import com.conveyal.datatools.manager.persistence.DataStore; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.stream.Collectors; + +/** + * Created by landon on 1/30/17. + */ +public class Organization extends Model implements Serializable { + private static final long serialVersionUID = 1L; + private static DataStore organizationStore = new DataStore<>("organizations"); + + public String name; + public String logoUrl; + public boolean active; + public UsageTier usageTier; + public Extension[] extensions; + public Date subscriptionBeginDate; + public Date subscriptionEndDate; + + public Organization () {} + + public void save () { + save(true); + } + + public void save(boolean commit) { + if (commit) + organizationStore.save(id, this); + else + organizationStore.saveWithoutCommit(id, this); + } + + public static Organization get (String id) { + return organizationStore.getById(id); + } + + public static Collection getAll() { + return organizationStore.getAll(); + } + + public static void commit() { + organizationStore.commit(); + } + + public void delete() { + for (Project p : getProjects()) { + p.delete(); + } + organizationStore.delete(this.id); + } + + public Collection getProjects () { + return Project.getAll().stream().filter(p -> p.organizationId == this.id).collect(Collectors.toList()); + } + + /** + * Created by landon on 1/30/17. + */ + public static enum Extension { + GTFS_PLUS, + DEPLOYMENT, + VALIDATOR, + ALERTS, + SIGN_CONFIG; + + } + + /** + * Created by landon on 1/30/17. + */ + public static enum UsageTier { + LOW, + MEDIUM, + HIGH + } +} From 9ad0e61aed90af9f5c2f9cc366f059b015869a8e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 31 Jan 2017 13:12:57 -0500 Subject: [PATCH 246/323] fix(PatternsLayer): fix issue with pattern shape rendering #21 --- src/main/client/editor/components/map/PatternsLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/client/editor/components/map/PatternsLayer.js b/src/main/client/editor/components/map/PatternsLayer.js index 601f329a2..407267c33 100644 --- a/src/main/client/editor/components/map/PatternsLayer.js +++ b/src/main/client/editor/components/map/PatternsLayer.js @@ -26,7 +26,7 @@ export default class PatternsLayer extends Component { .map(tp => { const isActive = subEntityId === tp.id let pattern = isActive ? activePattern : tp - const latLngs = pattern.shape ? pattern.shape.coordinates.map(c => ([c[1], c[0]])) : [] + const latLngs = pattern && pattern.shape ? pattern.shape.coordinates.map(c => ([c[1], c[0]])) : [] // NOTE: don't render pattern if latlngs don't exist or a single pattern is active if (!latLngs || !isActive && subEntityId) { From 9a6dafd0348818b8d02c5cf5ff686e8a297bec9c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 31 Jan 2017 14:03:57 -0500 Subject: [PATCH 247/323] org id for proj --- .../java/com/conveyal/datatools/manager/models/Project.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/models/Project.java b/src/main/java/com/conveyal/datatools/manager/models/Project.java index 8b6dbdd13..966c21776 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/Project.java +++ b/src/main/java/com/conveyal/datatools/manager/models/Project.java @@ -39,6 +39,8 @@ public class Project extends Model { public Collection otpServers; + public String organizationId; + @JsonIgnore public OtpServer getServer (String name) { for (OtpServer otpServer : otpServers) { From 72664df8141d48277d08be210bd963ba77c24099 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 1 Feb 2017 10:45:12 -0500 Subject: [PATCH 248/323] feat(Project): publish public feeds to s3 static page #18 --- i18n/english.yml | 11 ++ i18n/espanol.yml | 24 ++++ i18n/francais.yml | 24 ++++ .../manager/containers/ActiveProjectViewer.js | 4 +- .../controllers/api/ProjectController.java | 19 +++- .../datatools/manager/jobs/MakePublicJob.java | 107 ++++++++++++++++++ .../manager/jobs/ProcessSingleFeedJob.java | 4 - .../datatools/manager/models/FeedSource.java | 37 ++++++ .../datatools/manager/models/FeedVersion.java | 39 ++++--- 9 files changed, 249 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java diff --git a/i18n/english.yml b/i18n/english.yml index e21a842a2..8f06d71f7 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -51,6 +51,7 @@ DeploymentsPanel: ProjectViewer: mergeFeeds: Merge all settings: Settings + makePublic: Publish public feeds deployments: Deployments feeds: title: Feed Sources @@ -272,6 +273,16 @@ UserHomePage: help: title: What's a project? content: A project is used to group GTFS feeds. For example, the feeds in a project may be in the same region or they may collectively define a planning scenario. +OrganizationList: + search: Search orgs + new: Create org +OrganizationSettings: + subscriptionBeginDate: Subscription begins + subscriptionEndDate: Subscription ends + usageTier: + low: Low + medium: Medium + high: High CreateUser: new: Create User diff --git a/i18n/espanol.yml b/i18n/espanol.yml index f851f78ec..f943eff9b 100644 --- a/i18n/espanol.yml +++ b/i18n/espanol.yml @@ -48,6 +48,8 @@ DeploymentsPanel: feedCount: Number of feeds ProjectViewer: mergeFeeds: Merge Feeds + settings: Settings + makePublic: Publish public feeds feeds: title: Feed Sources search: Search by name @@ -214,6 +216,28 @@ UserList: showing: Showing Users of: of search: Search by username +UserHomePage: + title: Projects + noProjects: You currently do not have any projects. + createFirst: Create my first project + search: Search feeds + table: + name: Project Name + new: New Project + help: + title: What's a project? + content: A project is used to group GTFS feeds. For example, the feeds in a project may be in the same region or they may collectively define a planning scenario. +OrganizationList: + search: Search orgs + new: Create org +OrganizationSettings: + subscriptionBeginDate: Subscription begins + subscriptionEndDate: Subscription ends + usageTier: + low: Low + medium: Medium + high: High + CreateUser: new: Create User UserSettings: diff --git a/i18n/francais.yml b/i18n/francais.yml index 37b8050ae..c36286a6b 100644 --- a/i18n/francais.yml +++ b/i18n/francais.yml @@ -48,6 +48,8 @@ DeploymentsPanel: feedCount: Number of feeds ProjectViewer: mergeFeeds: Merge Feeds + settings: Settings + makePublic: Publish public feeds feeds: title: Feed Sources search: Search by name @@ -214,6 +216,28 @@ UserList: showing: Showing Users of: of search: Search by username +UserHomePage: + title: Projects + noProjects: You currently do not have any projects. + createFirst: Create my first project + search: Search feeds + table: + name: Project Name + new: New Project + help: + title: What's a project? + content: A project is used to group GTFS feeds. For example, the feeds in a project may be in the same region or they may collectively define a planning scenario. +OrganizationList: + search: Search orgs + new: Create org +OrganizationSettings: + subscriptionBeginDate: Subscription begins + subscriptionEndDate: Subscription ends + usageTier: + low: Low + medium: Medium + high: High + CreateUser: new: Create User UserSettings: diff --git a/src/main/client/manager/containers/ActiveProjectViewer.js b/src/main/client/manager/containers/ActiveProjectViewer.js index b60301767..7ca768c66 100644 --- a/src/main/client/manager/containers/ActiveProjectViewer.js +++ b/src/main/client/manager/containers/ActiveProjectViewer.js @@ -9,7 +9,8 @@ import { thirdPartySync, fetchFeedsForProject, updateProject, - downloadFeedForProject + downloadFeedForProject, + deployPublic } from '../actions/projects' import { fetchProjectFeeds, @@ -72,6 +73,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { dispatch(updateFeedSource(feedSource, { [propName]: newValue })) }, deploymentsRequested: () => { dispatch(fetchProjectDeployments(projectId)) }, + deployPublic: (project) => { return dispatch(deployPublic(project)) }, createDeploymentFromFeedSource: (feedSource) => { dispatch(createDeploymentFromFeedSource(feedSource)) }, diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index a996ce2cc..890dad11c 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -3,6 +3,7 @@ import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.FetchProjectFeedsJob; +import com.conveyal.datatools.manager.jobs.MakePublicJob; import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.JsonViews; @@ -15,7 +16,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; -import com.google.gson.JsonObject; import org.apache.http.concurrent.Cancellable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -622,6 +622,22 @@ private static byte[] mergeTables(JsonNode tableNode, Map f return tableOut.toByteArray(); } + public static boolean deployPublic (Request req, Response res) { + Auth0UserProfile userProfile = req.attribute("user"); + String id = req.params("id"); + if (id == null) { + halt(400, "must provide project id!"); + } + Project proj = Project.get(id); + + if (proj == null) + halt(400, "no such project!"); + + // run as sync job; if it gets too slow change to async + new MakePublicJob(proj, userProfile.getUser_id()).run(); + return true; + } + public static Project thirdPartySync(Request req, Response res) throws Exception { Auth0UserProfile userProfile = req.attribute("user"); String id = req.params("id"); @@ -709,6 +725,7 @@ public static void register (String apiPrefix) { delete(apiPrefix + "secure/project/:id", ProjectController::deleteProject, json::write); get(apiPrefix + "secure/project/:id/thirdPartySync/:type", ProjectController::thirdPartySync, json::write); post(apiPrefix + "secure/project/:id/fetch", ProjectController::fetch, json::write); + post(apiPrefix + "secure/project/:id/deployPublic", ProjectController::deployPublic, json::write); get(apiPrefix + "public/project/:id/download", ProjectController::downloadMergedFeed); diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java new file mode 100644 index 000000000..7e1e57b7b --- /dev/null +++ b/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java @@ -0,0 +1,107 @@ +package com.conveyal.datatools.manager.jobs; + +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.conveyal.datatools.common.status.MonitorableJob; +import com.conveyal.datatools.manager.DataManager; +import com.conveyal.datatools.manager.models.FeedSource; +import com.conveyal.datatools.manager.models.FeedVersion; +import com.conveyal.datatools.manager.models.Project; +import com.conveyal.datatools.manager.persistence.FeedStore; +import org.apache.commons.io.FileUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Map; + +/** + * Created by landon on 1/31/17. + */ +public class MakePublicJob extends MonitorableJob { + public Project project; + public Status status; + private static final Logger LOG = LoggerFactory.getLogger(MakePublicJob.class); + + public MakePublicJob(Project project, String owner) { + super(owner, "Generating public html for " + project.name, JobType.MAKE_PROJECT_PUBLIC); + this.project = project; + status = new Status(); + status.message = "Waiting to begin validation..."; + status.percentComplete = 0; + } + @Override + public Status getStatus() { + return null; + } + + @Override + public void handleStatusEvent(Map statusMap) { + + } + + @Override + public void run() { + String output; + String title = "Public Feeds"; + StringBuilder r = new StringBuilder(); + r.append("\n"); + r.append("\n"); + r.append("\n"); + r.append("" + title + "\n"); + r.append(""); + + r.append("\n"); + r.append("\n"); + r.append("

    " + title + "

    \n"); + r.append("The following feeds, in GTFS format, are available for download and use.\n"); + r.append("
      \n"); + project.getProjectFeedSources().stream() + .filter(fs -> fs.isPublic && fs.getLatest() != null) + .forEach(fs -> { + String url = fs.url != null ? fs.url.toString() : "https://s3.amazonaws.com/" + DataManager.feedBucket + "/public/" + fs.name +".zip"; + FeedVersion latest = fs.getLatest(); + r.append("
    • "); + r.append(""); + r.append(fs.name); + r.append(""); + r.append(" ("); + if (fs.url != null && fs.lastFetched != null) { + r.append("last checked: " + new SimpleDateFormat("dd MMM yyyy").format(fs.lastFetched) + ", "); + } + if (fs.getLastUpdated() != null) { + r.append("last updated: " + new SimpleDateFormat("dd MMM yyyy").format(fs.getLastUpdated()) + ")"); + } + r.append("
    • "); + }); + r.append("
    "); + r.append(""); + r.append(""); + output = r.toString(); + String fileName = "index.html"; + String folder = "public/"; + File file = new File(FileUtils.getTempDirectory() + fileName); + file.deleteOnExit(); + try { + FileUtils.writeStringToFile(file, output); + } catch (IOException e) { + e.printStackTrace(); + } + + FeedStore.s3Client.putObject(DataManager.feedBucket, folder + fileName, file); + FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, folder + fileName, CannedAccessControlList.PublicRead); + + jobFinished(); + } +} diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java index e5554ceb3..549abc377 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ProcessSingleFeedJob.java @@ -1,14 +1,10 @@ package com.conveyal.datatools.manager.jobs; -import com.conveyal.datatools.common.status.MonitorableJob; import com.conveyal.datatools.editor.jobs.ProcessGtfsSnapshotMerge; import com.conveyal.datatools.editor.models.Snapshot; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.models.FeedVersion; -import java.util.Collection; - - /** * Process/validate a single GTFS feed * @author mattwigway diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java index 6b1614b46..a8ac055a3 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedSource.java @@ -1,5 +1,8 @@ package com.conveyal.datatools.manager.models; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.DeleteObjectRequest; +import com.amazonaws.services.s3.model.DeleteObjectsRequest; import com.conveyal.datatools.editor.datastore.FeedTx; import com.conveyal.datatools.editor.datastore.GlobalTx; import com.conveyal.datatools.editor.datastore.VersionedDataStore; @@ -7,6 +10,7 @@ import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.NotifyUsersForSubscriptionJob; import com.conveyal.datatools.manager.persistence.DataStore; +import com.conveyal.datatools.manager.persistence.FeedStore; import com.conveyal.datatools.manager.utils.HashUtils; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -97,6 +101,11 @@ public void setProject(Project proj) { */ public URL url; + /** + * Where the feed exists on s3 + */ + public URL s3Url; + /** * What is the GTFS Editor snapshot for this feed? * @@ -400,6 +409,27 @@ public int getNoteCount() { return this.noteIds != null ? this.noteIds.size() : 0; } + public void makePublic() { + String sourceKey = FeedStore.s3Prefix + this.id + ".zip"; + String publicKey = "public/" + this.name + ".zip"; + if (FeedStore.s3Client.doesObjectExist(DataManager.feedBucket, sourceKey)) { + LOG.info("copying feed {} to s3 public folder", this); + FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, sourceKey, CannedAccessControlList.PublicRead); + FeedStore.s3Client.copyObject(DataManager.feedBucket, sourceKey, DataManager.feedBucket, publicKey); + FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, publicKey, CannedAccessControlList.PublicRead); + } + } + + public void makePrivate() { + String sourceKey = FeedStore.s3Prefix + this.id + ".zip"; + String publicKey = "public/" + this.name + ".zip"; + if (FeedStore.s3Client.doesObjectExist(DataManager.feedBucket, sourceKey)) { + LOG.info("removing feed {} from s3 public folder", this); + FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, sourceKey, CannedAccessControlList.AuthenticatedRead); + FeedStore.s3Client.deleteObject(DataManager.feedBucket, publicKey); + } + } + /** * Represents ways feeds can be retrieved */ @@ -419,6 +449,13 @@ public static void commit() { public void delete() { getFeedVersions().forEach(FeedVersion::delete); + // delete latest copy of feed source + if (DataManager.useS3) { + DeleteObjectsRequest delete = new DeleteObjectsRequest(DataManager.feedBucket); + delete.withKeys("public/" + this.name + ".zip", FeedStore.s3Prefix + this.id + ".zip"); + FeedStore.s3Client.deleteObjects(delete); + } + // Delete editor feed mapdb // TODO: does the mapdb folder need to be deleted separately? GlobalTx gtx = VersionedDataStore.getGlobalTx(); diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index cb41210e0..b57a68171 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -259,6 +259,11 @@ public void validate(EventBus eventBus) { Map tripsPerDate; try { + // make feed public... this shouldn't take very long + FeedSource fs = this.getFeedSource(); + if (fs.isPublic) { + fs.makePublic(); + } // eventBus.post(new StatusEvent("Validating feed...", 30, false)); statusMap.put("message", "Validating feed..."); statusMap.put("percentComplete", 30.0); @@ -589,24 +594,30 @@ public Long getFileSize() { * Delete this feed version. */ public void delete() { - // reset lastModified if feed is latest version - System.out.println("deleting version"); - FeedSource fs = getFeedSource(); - FeedVersion latest = fs.getLatest(); - if (latest != null && latest.id.equals(this.id)) { - fs.lastFetched = null; - fs.save(); - } - feedStore.deleteFeed(id); + try { + // reset lastModified if feed is latest version + System.out.println("deleting version"); + String id = this.id; + FeedSource fs = getFeedSource(); + FeedVersion latest = fs.getLatest(); + if (latest != null && latest.id.equals(this.id)) { + fs.lastFetched = null; + fs.save(); + } + feedStore.deleteFeed(id); - for (Deployment d : Deployment.getAll()) { - d.feedVersionIds.remove(this.id); - } + for (Deployment d : Deployment.getAll()) { + d.feedVersionIds.remove(this.id); + } - getTransportNetworkPath().delete(); + getTransportNetworkPath().delete(); - versionStore.delete(this.id); + versionStore.delete(this.id); + LOG.info("Version {} deleted", id); + } catch (Exception e) { + LOG.warn("Error deleting version", e); + } } @JsonIgnore public String getR5Path () { From 0e3ea7896444734c859b539ecb22ee887cdc4bb4 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 1 Feb 2017 10:52:01 -0500 Subject: [PATCH 249/323] fix(MakePublicJob): remove unused velocity imports #18 --- .../conveyal/datatools/common/status/MonitorableJob.java | 3 ++- .../conveyal/datatools/manager/jobs/MakePublicJob.java | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/common/status/MonitorableJob.java b/src/main/java/com/conveyal/datatools/common/status/MonitorableJob.java index b6be36549..e38ca288d 100644 --- a/src/main/java/com/conveyal/datatools/common/status/MonitorableJob.java +++ b/src/main/java/com/conveyal/datatools/common/status/MonitorableJob.java @@ -30,7 +30,8 @@ public enum JobType { PROCESS_SNAPSHOT, VALIDATE_FEED, FETCH_PROJECT_FEEDS, - FETCH_SINGLE_FEED + FETCH_SINGLE_FEED, + MAKE_PROJECT_PUBLIC } public MonitorableJob(String owner, String name, JobType type) { diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java index 7e1e57b7b..7024ede31 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/MakePublicJob.java @@ -3,23 +3,15 @@ import com.amazonaws.services.s3.model.CannedAccessControlList; import com.conveyal.datatools.common.status.MonitorableJob; import com.conveyal.datatools.manager.DataManager; -import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.Project; import com.conveyal.datatools.manager.persistence.FeedStore; import org.apache.commons.io.FileUtils; -import org.apache.velocity.Template; -import org.apache.velocity.VelocityContext; -import org.apache.velocity.app.Velocity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.StringWriter; -import java.nio.charset.StandardCharsets; import java.text.SimpleDateFormat; import java.util.Map; From ed3e93a40a7af5c06cf2702879be9b54e65ef2d6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 1 Feb 2017 11:49:19 -0500 Subject: [PATCH 250/323] fix(TimetableEditor): fix offset for new trips offset was not applying to newly added trips, due to referencing code that had been refactored (offset moved from this.state to this.props.timetable) #22 --- .../client/editor/components/timetable/TimetableEditor.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/client/editor/components/timetable/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js index b685ebb51..146470901 100644 --- a/src/main/client/editor/components/timetable/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -44,7 +44,7 @@ export default class TimetableEditor extends Component { // set starting time for first arrival let cumulativeTravelTime = !toClone ? 0 : objectPath.get(newRow, `stopTimes.0.arrivalTime`) - cumulativeTravelTime += this.state.offsetSeconds + cumulativeTravelTime += this.props.timetable.offset for (let i = 0; i < activePattern.patternStops.length; i++) { let stop = activePattern.patternStops[i] @@ -80,13 +80,13 @@ export default class TimetableEditor extends Component { addNewRow (blank = false, scroll = false) { // set blank to true if there are no rows to clone blank = blank || this.props.timetable.trips.length === 0 - - let clone = blank ? null : this.props.timetable.trips[this.props.timetable.trips.length - 1] + const lastIndex = this.props.timetable.trips.length - 1 + let clone = blank ? null : this.props.timetable.trips[lastIndex] let newRow = this.constructNewRow(clone) let stateUpdate = { activeCell: {$set: null}, - scrollToRow: {$set: this.props.timetable.trips.length}, + scrollToRow: {$set: lastIndex + 1}, // increment selected row scrollToColumn: {$set: 0} } this.props.addNewTrip(newRow) From 82a0d36ec03575f36b4003f076f0a799a826210e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 2 Feb 2017 15:48:29 -0500 Subject: [PATCH 251/323] feat(TimetableEditor): enhance/clarify trip add/clone/offset behavior closes #22 --- .../editor/components/timetable/Timetable.js | 34 +++++++++++-------- .../components/timetable/TimetableEditor.js | 16 ++++++++- .../components/timetable/TimetableHeader.js | 17 +++++++--- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/main/client/editor/components/timetable/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js index 332798303..2a2cde30b 100644 --- a/src/main/client/editor/components/timetable/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -86,8 +86,13 @@ export default class Timetable extends Component { this.setState(update(this.state, stateUpdate)) } _getColumnWidth ({ index }) { + index = this.props.timetable.hideDepartureTimes && index > 3 ? index * 2 - 3 : index const col = this.props.columns[index] - const width = col ? col.width : 90 + const width = col.type === 'ARRIVAL_TIME' && this.props.timetable.hideDepartureTimes + ? col.width * 2 + : col + ? col.width + : 90 return width } _getColumnHeaderWidth ({ index }) { @@ -107,10 +112,12 @@ export default class Timetable extends Component { return isTimeFormat(col.type) && value >= 0 && value < previousValue } _cellRenderer ({ columnIndex, key, rowIndex, scrollToColumn, scrollToRow, style }) { - const isFocused = columnIndex === scrollToColumn && rowIndex === scrollToRow - const isEditing = this.state.activeCell === `${rowIndex}-${columnIndex}` - const col = this.props.columns[columnIndex] - const previousCol = this.props.columns[columnIndex - 1] + // adjust columnIndex for hideDepartures + const colIndex = this.props.timetable.hideDepartureTimes && columnIndex > 3 ? columnIndex * 2 - 3 : columnIndex + const isFocused = colIndex === scrollToColumn && rowIndex === scrollToRow + const isEditing = this.state.activeCell === `${rowIndex}-${colIndex}` + const col = this.props.columns[colIndex] + const previousCol = this.props.columns[colIndex - 1] const row = this.props.data[rowIndex] let rowIsChecked = this.props.selected[0] === '*' && @@ -126,9 +133,9 @@ export default class Timetable extends Component { return ( this.handleCellClick(rowIndex, columnIndex)} + onClick={() => this.handleCellClick(rowIndex, colIndex)} duplicateLeft={(evt) => this.props.updateCellValue(previousValue, rowIndex, `${rowIndex}.${col.key}`)} - handlePastedRows={(rows) => this.handlePastedRows(rows, rowIndex, columnIndex, this.props.columns)} + handlePastedRows={(rows) => this.handlePastedRows(rows, rowIndex, colIndex, this.props.columns)} invalidData={isInvalid} isEditing={isEditing} isSelected={rowIsChecked} @@ -144,11 +151,11 @@ export default class Timetable extends Component { this.props.updateCellValue(value, rowIndex, `${rowIndex}.${col.key}`) // this.setState({activeCell: null}) - // TODO: add below back in - // // set departure time value if departure times are hidden - // if (this.props.timetable.hideDepartureTimes && this.props.timetable.columns[colIndex + 1] && this.props.timetable.columns[colIndex + 1].type === 'DEPARTURE_TIME') { - // this.updateCellValue(value, rowIndex, `${rowIndex}.${this.props.timetable.columns[columnIndex + 1].key}`) - // } + // set departure time value equal to arrival time if departure times are hidden + const nextCol = this.props.timetable.columns[colIndex + 1] + if (this.props.timetable.hideDepartureTimes && nextCol && nextCol.type === 'DEPARTURE_TIME') { + this.props.updateCellValue(value, rowIndex, `${rowIndex}.${nextCol.key}`) + } }} /> ) } @@ -253,7 +260,6 @@ export default class Timetable extends Component { } } render () { - // console.log(this.props, this.state) if (this.props.columns.length === 0 && this.props.data.length === 0) { return (
    @@ -378,7 +384,7 @@ export default class Timetable extends Component { ref={Grid => { this.grid = Grid }} style={{outline: 'none'}} columnWidth={this._getColumnWidth} - columnCount={this.props.columns.length} + columnCount={this.props.timetable.hideDepartureTimes ? columnHeaderCount : this.props.columns.length} height={height} onScroll={onScroll} overscanColumnCount={overscanColumnCount} diff --git a/src/main/client/editor/components/timetable/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js index 146470901..c22f5e3a1 100644 --- a/src/main/client/editor/components/timetable/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -44,7 +44,9 @@ export default class TimetableEditor extends Component { // set starting time for first arrival let cumulativeTravelTime = !toClone ? 0 : objectPath.get(newRow, `stopTimes.0.arrivalTime`) - cumulativeTravelTime += this.props.timetable.offset + + // TODO: auto-add offset to any new trip? No, for now. Add toggle/checkbox that allows for this. + // cumulativeTravelTime += this.props.timetable.offset for (let i = 0; i < activePattern.patternStops.length; i++) { let stop = activePattern.patternStops[i] @@ -77,6 +79,17 @@ export default class TimetableEditor extends Component { return newRow } + duplicateRows (indexArray) { + let arrayAscending = indexArray.sort((a, b) => { + return a - b + }) + for (var i = 0; i < arrayAscending.length; i++) { + const index = arrayAscending[i] + const toClone = this.props.timetable.trips[index] + const newRow = this.constructNewRow(toClone) + this.props.addNewTrip(newRow) + } + } addNewRow (blank = false, scroll = false) { // set blank to true if there are no rows to clone blank = blank || this.props.timetable.trips.length === 0 @@ -214,6 +227,7 @@ export default class TimetableEditor extends Component { removeSelectedRows={() => this.removeSelectedRows()} offsetRows={(rowIndexes, offsetAmount) => this.offsetRows(rowIndexes, offsetAmount)} addNewRow={(blank, scroll) => this.addNewRow(blank, scroll)} + duplicateRows={(indexArray) => this.duplicateRows(indexArray)} saveEditedTrips={(pattern, scheduleId) => this.saveEditedTrips(pattern, scheduleId)} {...this.props} /> {activeSchedule diff --git a/src/main/client/editor/components/timetable/TimetableHeader.js b/src/main/client/editor/components/timetable/TimetableHeader.js index eb63fbcf2..f75f57874 100644 --- a/src/main/client/editor/components/timetable/TimetableHeader.js +++ b/src/main/client/editor/components/timetable/TimetableHeader.js @@ -14,7 +14,7 @@ export default class TimetableHeader extends Component { feedSource: PropTypes.object } render () { - const { feedSource, timetable, setOffset, offsetRows, toggleDepartureTimes, addNewRow, removeSelectedRows, saveEditedTrips, route, tableData, activeScheduleId, activePattern, setActiveEntity, fetchTripsForCalendar } = this.props + const { feedSource, timetable, setOffset, offsetRows, toggleDepartureTimes, addNewRow, removeSelectedRows, saveEditedTrips, route, tableData, activeScheduleId, activePattern, setActiveEntity, fetchTripsForCalendar, duplicateRows } = this.props const { selected, trips, hideDepartureTimes, edited, offset } = timetable const calendars = tableData.calendar || [] const activeCalendar = calendars.find(c => c.id === activeScheduleId) @@ -130,9 +130,9 @@ export default class TimetableHeader extends Component { - Add new trip}> + Add blank trip}> From 57cb21ea83cbc0f3268bd2efa7d23495198c795d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 12:37:53 -0500 Subject: [PATCH 252/323] fix(GTFSEditor): change publiclyVisible route field values from 0/1 to true/false closes #24 --- gtfs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gtfs.yml b/gtfs.yml index e6d84e6df..5d86bca60 100644 --- a/gtfs.yml +++ b/gtfs.yml @@ -233,9 +233,9 @@ required: true inputType: DROPDOWN options: - - value: 0 + - value: false text: 'No' - - value: 1 + - value: true text: 'Yes' columnWidth: 6 adminOnly: true From 28ca09af8d3535cd8e416e86f6e75db7ead296e2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 13:56:20 -0500 Subject: [PATCH 253/323] make feed source public/private on isPublic field change --- i18n/english.yml | 9 +++++++ i18n/espanol.yml | 9 +++++++ i18n/francais.yml | 1 + .../controllers/api/FeedSourceController.java | 24 ++++++++++++------- 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/i18n/english.yml b/i18n/english.yml index 8f06d71f7..ae36a1b47 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -277,6 +277,15 @@ OrganizationList: search: Search orgs new: Create org OrganizationSettings: + orgDetails: Organization details + projects: Projects + subDetails: Subscription details + name: + label: Name + placeholder: Big City Transit + logoUrl: + label: Logo URL + placeholder: http://example.com/logo_30x30.png subscriptionBeginDate: Subscription begins subscriptionEndDate: Subscription ends usageTier: diff --git a/i18n/espanol.yml b/i18n/espanol.yml index f943eff9b..4fdec8a34 100644 --- a/i18n/espanol.yml +++ b/i18n/espanol.yml @@ -231,6 +231,15 @@ OrganizationList: search: Search orgs new: Create org OrganizationSettings: + orgDetails: Organization details + projects: Projects + subDetails: Subscription details + name: + label: Name + placeholder: Big City Transit + logoUrl: + label: Logo URL + placeholder: http://example.com/logo_30x30.png subscriptionBeginDate: Subscription begins subscriptionEndDate: Subscription ends usageTier: diff --git a/i18n/francais.yml b/i18n/francais.yml index c36286a6b..8985f8f58 100644 --- a/i18n/francais.yml +++ b/i18n/francais.yml @@ -203,6 +203,7 @@ ProjectSettings: public: Public URL internal: Internal URLs admin: Admin access only? + s3Bucket: S3 Bucket Name osm: title: OSM Extract gtfs: Use GTFS-Derived Extract Bounds diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java index 5684c9d5f..e3084bf5e 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedSourceController.java @@ -1,6 +1,7 @@ package com.conveyal.datatools.manager.controllers.api; import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.CopyObjectRequest; import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.auth.Auth0UserProfile; import com.conveyal.datatools.manager.jobs.FetchSingleFeedJob; @@ -176,14 +177,14 @@ public static void applyJsonToFeedSource(FeedSource source, String json) throws if(entry.getKey().equals("isPublic")) { source.isPublic = entry.getValue().asBoolean(); - // TODO: set AWS GTFS zips to publix/private after "isPublic" change + // TODO: set AWS GTFS zips to public/private after "isPublic" change if (DataManager.useS3) { -// if (source.isPublic) { -// FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, keyName, CannedAccessControlList.PublicRead); -// } -// else { -// FeedStore.s3Client.setObjectAcl(DataManager.feedBucket, keyName, CannedAccessControlList.AuthenticatedRead); -// } + if (source.isPublic) { + source.makePublic(); + } + else { + source.makePrivate(); + } } } @@ -273,7 +274,8 @@ private static FeedSource requestFeedSourceById(Request req, String action) { } public static FeedSource requestFeedSource(Request req, FeedSource s, String action) { Auth0UserProfile userProfile = req.attribute("user"); - Boolean publicFilter = Boolean.valueOf(req.queryParams("public")); + Boolean publicFilter = Boolean.valueOf(req.queryParams("public")) || req.url().split("/api/manager/")[1].startsWith("public"); +// System.out.println(req.url().split("/api/manager/")[1].startsWith("public")); // check for null feedsource if (s == null) @@ -285,7 +287,11 @@ public static FeedSource requestFeedSource(Request req, FeedSource s, String act authorized = userProfile.canManageFeed(s.projectId, s.id); break; case "view": - authorized = userProfile.canViewFeed(s.projectId, s.id); + if (!publicFilter) { + authorized = userProfile.canViewFeed(s.projectId, s.id); + } else { + authorized = false; + } break; default: authorized = false; From 5509d3fb06ee47d4bf3c4e4b3a27833a89239183 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 14:39:52 -0500 Subject: [PATCH 254/323] feat(Deployments): deploy bundles to s3 closes #25 --- i18n/english.yml | 1 + i18n/espanol.yml | 1 + i18n/francais.yml | 1 + .../client/manager/components/DeploymentSettings.js | 11 +++++++++++ .../client/manager/components/DeploymentViewer.js | 9 +++++---- .../manager/controllers/api/DeploymentController.java | 4 ++-- .../conveyal/datatools/manager/jobs/DeployJob.java | 11 ++++++----- 7 files changed, 27 insertions(+), 11 deletions(-) diff --git a/i18n/english.yml b/i18n/english.yml index e21a842a2..ae1553f78 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -229,6 +229,7 @@ ProjectSettings: public: Public URL internal: Internal URLs admin: Admin access only? + s3Bucket: S3 bucket name osm: title: OSM Extract gtfs: Use GTFS-Derived Extract Bounds diff --git a/i18n/espanol.yml b/i18n/espanol.yml index f851f78ec..b7091ab8d 100644 --- a/i18n/espanol.yml +++ b/i18n/espanol.yml @@ -201,6 +201,7 @@ ProjectSettings: public: Public URL internal: Internal URLs admin: Admin access only? + s3Bucket: S3 bucket name osm: title: OSM Extract gtfs: Use GTFS-Derived Extract Bounds diff --git a/i18n/francais.yml b/i18n/francais.yml index 37b8050ae..658f5c0c5 100644 --- a/i18n/francais.yml +++ b/i18n/francais.yml @@ -201,6 +201,7 @@ ProjectSettings: public: Public URL internal: Internal URLs admin: Admin access only? + s3Bucket: S3 bucket name osm: title: OSM Extract gtfs: Use GTFS-Derived Extract Bounds diff --git a/src/main/client/manager/components/DeploymentSettings.js b/src/main/client/manager/components/DeploymentSettings.js index 5143398b5..c0423a08e 100644 --- a/src/main/client/manager/components/DeploymentSettings.js +++ b/src/main/client/manager/components/DeploymentSettings.js @@ -241,6 +241,17 @@ export default class DeploymentSettings extends Component { this.setState(update(this.state, stateUpdate)) }} /> + + {getMessage(messages, 'deployment.servers.s3Bucket')} + { + let stateUpdate = { deployment: { otpServers: { [i]: { $merge: { s3Bucket: evt.target.value } } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + { diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index f0608f17b..5e20b8a33 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -59,6 +59,7 @@ export default class DeploymentViewer extends Component { {getMessage(messages, 'deploy')} @@ -71,8 +72,8 @@ export default class DeploymentViewer extends Component { }} > {project.otpServers - ? project.otpServers.map(server => ( - {server.name} + ? project.otpServers.map((server, i) => ( + {server.name} )) : null } @@ -125,8 +126,8 @@ export default class DeploymentViewer extends Component { }} > { - deployableFeeds.map(fs => ( - {fs.name} + deployableFeeds.map((fs, i) => ( + {fs.name} )) } diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java index 314f40263..de297ce50 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java @@ -208,7 +208,7 @@ private static void applyJsonToDeployment(Deployment d, JsonNode params) { Map.Entry entry = fieldsIter.next(); if (entry.getKey() == "feedVersions") { JsonNode versions = entry.getValue(); - ArrayList versionsToInsert = new ArrayList(versions.size()); + ArrayList versionsToInsert = new ArrayList<>(versions.size()); for (JsonNode version : versions) { if (!version.has("id")) { halt(400, "Version not supplied"); @@ -269,7 +269,7 @@ public static Object deploy (Request req, Response res) throws IOException { d.deployedTo = target; d.save(); - DeployJob job = new DeployJob(d, userProfile.getUser_id(), targetUrls, p.getServer(target).publicUrl, p.getServer(target).s3Bucket, p.getServer(target).s3Credentials); + DeployJob job = new DeployJob(d, userProfile.getUser_id(), targetUrls, otpServer.publicUrl, otpServer.s3Bucket, otpServer.s3Credentials); deploymentJobsByServer.put(target, job); Thread tnThread = new Thread(job); diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java index 9752c760a..96c1858fa 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java @@ -38,6 +38,7 @@ public class DeployJob extends MonitorableJob { private static final Logger LOG = LoggerFactory.getLogger(DeployJob.class); + private static final String bundlePrefix = "bundles/"; /** The URLs to deploy to */ private List targets; @@ -88,8 +89,8 @@ public void handleStatusEvent(Map statusMap) { } public void run() { - - int totalTasks = 1 + targets.size(); + int targetCount = targets != null ? targets.size() : 0; + int totalTasks = 1 + targetCount; int tasksCompleted = 0; // create a temporary file in which to save the deployment @@ -143,7 +144,7 @@ public void run() { status.message = "Uploading to S3"; status.uploadingS3 = true; } - + LOG.info("Uploading deployment {} to s3", deployment.name); try { AWSCredentials creds; @@ -156,7 +157,7 @@ public void run() { } TransferManager tx = new TransferManager(creds); - String key = deployment.name + ".zip"; + String key = bundlePrefix + deployment.name + ".zip"; final Upload upload = tx.upload(this.s3Bucket, key, temp); upload.addProgressListener(new ProgressListener() { @@ -171,7 +172,7 @@ public void progressChanged(ProgressEvent progressEvent) { tx.shutdownNow(); // copy to [name]-latest.zip - String copyKey = deployment.getProject().name.toLowerCase() + "-latest.zip"; + String copyKey = bundlePrefix + deployment.getProject().name.toLowerCase() + "-latest.zip"; AmazonS3 s3client = new AmazonS3Client(creds); CopyObjectRequest copyObjRequest = new CopyObjectRequest( this.s3Bucket, key, this.s3Bucket, copyKey); From 21994f518ea7c6db3022a661a2c9d4257fae7938 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 14:46:48 -0500 Subject: [PATCH 255/323] add s3bucket/s3credentials field --- .../manager/controllers/api/ProjectController.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index a996ce2cc..446b18a80 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -243,6 +243,14 @@ private static void updateOtpServers(Project proj, JsonNode otpServers) { JsonNode publicUrl = otpServer.get("publicUrl"); otpServerObj.publicUrl = publicUrl.isNull() ? null : publicUrl.asText(); } + if (otpServer.has("s3Bucket")) { + JsonNode s3Bucket = otpServer.get("s3Bucket"); + otpServerObj.s3Bucket = s3Bucket.isNull() ? null : s3Bucket.asText(); + } + if (otpServer.has("s3Credentials")) { + JsonNode s3Credentials = otpServer.get("s3Credentials"); + otpServerObj.s3Credentials = s3Credentials.isNull() ? null : s3Credentials.asText(); + } if (otpServer.has("internalUrl") && otpServer.get("internalUrl").isArray()) { JsonNode internalUrl = otpServer.get("internalUrl"); for (int j = 0; j < internalUrl.size(); j++) { From 700cd0950d7d4592c46f8302729601cd40c6e4df Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 15:50:09 -0500 Subject: [PATCH 256/323] add project id folder prefix to saved deployment bundles --- .../java/com/conveyal/datatools/manager/jobs/DeployJob.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java index 96c1858fa..41ff91db8 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java @@ -157,7 +157,7 @@ public void run() { } TransferManager tx = new TransferManager(creds); - String key = bundlePrefix + deployment.name + ".zip"; + String key = bundlePrefix + deployment.getProject().id + "/" + deployment.name + ".zip"; final Upload upload = tx.upload(this.s3Bucket, key, temp); upload.addProgressListener(new ProgressListener() { @@ -172,7 +172,7 @@ public void progressChanged(ProgressEvent progressEvent) { tx.shutdownNow(); // copy to [name]-latest.zip - String copyKey = bundlePrefix + deployment.getProject().name.toLowerCase() + "-latest.zip"; + String copyKey = bundlePrefix + deployment.getProject().id + "/" + deployment.getProject().name.toLowerCase() + "-latest.zip"; AmazonS3 s3client = new AmazonS3Client(creds); CopyObjectRequest copyObjRequest = new CopyObjectRequest( this.s3Bucket, key, this.s3Bucket, copyKey); From 2461b73c3c1c34ee3abd46afe89fef75a7523181 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 16:04:34 -0500 Subject: [PATCH 257/323] finish job before return statements for deployments --- .../com/conveyal/datatools/manager/jobs/DeployJob.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java index 41ff91db8..990e562ef 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/DeployJob.java @@ -107,6 +107,7 @@ public void run() { status.message = "app.deployment.error.dump"; } + jobFinished(); return; } @@ -129,6 +130,7 @@ public void run() { status.message = "app.deployment.error.dump"; } + jobFinished(); return; } @@ -202,6 +204,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.completed = true; } + jobFinished(); return; } @@ -264,6 +267,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.completed = true; } + jobFinished(); return; } @@ -280,6 +284,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.completed = true; } + jobFinished(); return; } @@ -294,6 +299,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.completed = true; } + jobFinished(); return; } @@ -310,6 +316,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.completed = true; } + jobFinished(); return; } @@ -324,7 +331,7 @@ public void progressChanged(ProgressEvent progressEvent) { status.message = "app.deployment.error.net"; status.completed = true; } - + jobFinished(); return; } @@ -350,6 +357,7 @@ public void progressChanged(ProgressEvent progressEvent) { } // no reason to take out more servers, it's going to have the same result + jobFinished(); return; } } catch (IOException e) { From 67432d5b4d38bb93ddfb3fe19cd3327c9e5c2b80 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 17:04:08 -0500 Subject: [PATCH 258/323] fix(DeploymentSettings): add back in deployment config labels closes #26 --- .../manager/components/DeploymentSettings.js | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/main/client/manager/components/DeploymentSettings.js b/src/main/client/manager/components/DeploymentSettings.js index c0423a08e..d69218d99 100644 --- a/src/main/client/manager/components/DeploymentSettings.js +++ b/src/main/client/manager/components/DeploymentSettings.js @@ -41,10 +41,10 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.buildConfig.fetchElevationUS')} { let stateUpdate = { deployment: { buildConfig: { fetchElevationUS: { $set: (evt.target.value === 'true') } } } } @@ -57,10 +57,10 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.buildConfig.stationTransfers')} { let stateUpdate = { deployment: { buildConfig: { stationTransfers: { $set: (evt.target.value === 'true') } } } } @@ -73,10 +73,10 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.buildConfig.subwayAccessTime')} { let stateUpdate = { deployment: { buildConfig: { subwayAccessTime: { $set: +evt.target.value } } } } @@ -84,10 +84,10 @@ export default class DeploymentSettings extends Component { }} /> + {getMessage(messages, 'deployment.buildConfig.fares')} { let stateUpdate = { deployment: { buildConfig: { fares: { $set: evt.target.value } } } } @@ -99,11 +99,11 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.routerConfig.numItineraries')} { let stateUpdate = { deployment: { routerConfig: { numItineraries: { $set: +evt.target.value } } } } @@ -113,11 +113,11 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.routerConfig.walkSpeed')} { let stateUpdate = { deployment: { routerConfig: { walkSpeed: { $set: +evt.target.value } } } } @@ -129,11 +129,11 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.routerConfig.stairsReluctance')} { let stateUpdate = { deployment: { routerConfig: { stairsReluctance: { $set: +evt.target.value } } } } @@ -143,11 +143,11 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.routerConfig.carDropoffTime')} { let stateUpdate = { deployment: { routerConfig: { carDropoffTime: { $set: +evt.target.value } } } } @@ -157,11 +157,11 @@ export default class DeploymentSettings extends Component { + {getMessage(messages, 'deployment.routerConfig.brandingUrlRoot')} { let stateUpdate = { deployment: { routerConfig: { brandingUrlRoot: { $set: evt.target.value } } } } @@ -287,19 +287,21 @@ export default class DeploymentSettings extends Component { {project.useCustomOsmBounds || this.state.deployment.useCustomOsmBounds - ? {getMessage(messages, 'deployment.osm.bounds')})} - ref='osmBounds' - onChange={(evt) => { - const bBox = evt.target.value.split(',') - if (bBox.length === 4) { - let stateUpdate = { deployment: { $merge: { osmWest: bBox[0], osmSouth: bBox[1], osmEast: bBox[2], osmNorth: bBox[3] } } } - this.setState(update(this.state, stateUpdate)) - } - }} /> + ? + {( {getMessage(messages, 'deployment.osm.bounds')})} + { + const bBox = evt.target.value.split(',') + if (bBox.length === 4) { + let stateUpdate = { deployment: { $merge: { osmWest: bBox[0], osmSouth: bBox[1], osmEast: bBox[2], osmNorth: bBox[3] } } } + this.setState(update(this.state, stateUpdate)) + } + }} /> + : null } From ce7b41103614d810c49249a31983257c4c539802 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 18:12:30 -0500 Subject: [PATCH 259/323] fix(GTFSEditor): trip pattern undo change button has been fixed closes #27 --- src/main/client/editor/actions/tripPattern.js | 20 ++++++++++-- .../components/map/DirectionIconsLayer.js | 14 ++++---- .../client/editor/components/map/EditorMap.js | 2 +- .../editor/components/map/PatternsLayer.js | 4 +-- src/main/client/editor/reducers/settings.js | 32 ++++++------------- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/main/client/editor/actions/tripPattern.js b/src/main/client/editor/actions/tripPattern.js index da43b4e5c..2a88a24c6 100644 --- a/src/main/client/editor/actions/tripPattern.js +++ b/src/main/client/editor/actions/tripPattern.js @@ -60,9 +60,25 @@ export function fetchTripPattern (feedId, tripPatternId) { } } -export function undoActiveTripPatternEdits () { +export function undoingActiveTripPatternEdits (lastActionIndex, lastActionType, lastCoordinatesIndex, lastControlPointsIndex, lastCoordinates) { return { - type: 'UNDO_TRIP_PATTERN_EDITS' + type: 'UNDO_TRIP_PATTERN_EDITS', + lastActionIndex, + lastActionType, + lastCoordinatesIndex, + lastControlPointsIndex, + lastCoordinates + } +} + +export function undoActiveTripPatternEdits () { + return function (dispatch, getState) { + const lastActionIndex = getState().editor.editSettings.actions.length - 1 + const lastActionType = getState().editor.editSettings.actions[lastActionIndex] + const lastCoordinatesIndex = getState().editor.editSettings.coordinatesHistory.length - 1 + const lastControlPointsIndex = getState().editor.editSettings.controlPoints.length - 1 + const lastCoordinates = getState().editor.editSettings.coordinatesHistory[lastCoordinatesIndex] + dispatch(undoingActiveTripPatternEdits(lastActionIndex, lastActionType, lastCoordinatesIndex, lastControlPointsIndex, lastCoordinates)) } } diff --git a/src/main/client/editor/components/map/DirectionIconsLayer.js b/src/main/client/editor/components/map/DirectionIconsLayer.js index f7cf8dbf2..7cbf6fc73 100644 --- a/src/main/client/editor/components/map/DirectionIconsLayer.js +++ b/src/main/client/editor/components/map/DirectionIconsLayer.js @@ -3,19 +3,21 @@ import bearing from 'turf-bearing' import { divIcon } from 'leaflet' import { Marker, FeatureGroup } from 'react-leaflet' import lineDistance from 'turf-line-distance' +import lineString from 'turf-linestring' import along from 'turf-along' export default class DirectionIconsLayer extends Component { static propTypes = { - activePattern: PropTypes.object + patternCoordinates: PropTypes.array } render () { - const { activePattern, mapState } = this.props + const { patternCoordinates, mapState } = this.props const { zoom, bounds } = mapState // let zoom = this.refs.map ? this.refs.map.leafletElement.getZoom() : 11 // let bounds = this.refs.map && this.refs.map.leafletElement.getBounds() // get intervals along path for arrow icons - let patternLength = activePattern && activePattern.shape ? lineDistance(activePattern.shape, 'meters') : 0 + let patternLine = patternCoordinates && patternCoordinates.length && lineString(patternCoordinates) + let patternLength = patternLine ? lineDistance(patternLine, 'meters') : 0 let iconInterval = zoom > 15 ? 200 : zoom > 14 @@ -30,7 +32,7 @@ export default class DirectionIconsLayer extends Component { let lengthsAlongPattern = [] for (var i = 0; i < Math.floor(patternLength / iconInterval); i++) { let distance = i ? iconInterval * i : iconInterval / 2 - let position = along(activePattern.shape, distance, 'meters') + let position = along(patternLine, distance, 'meters') if (!bounds) continue if (position.geometry.coordinates[1] > bounds.getNorth() || position.geometry.coordinates[1] < bounds.getSouth() || position.geometry.coordinates[0] > bounds.getEast() || position.geometry.coordinates[0] < bounds.getWest()) { continue @@ -39,12 +41,12 @@ export default class DirectionIconsLayer extends Component { } return ( - {lengthsAlongPattern.length && activePattern // this.refs[activePattern.id] + {lengthsAlongPattern.length && patternLine // this.refs[activePattern.id] ? lengthsAlongPattern.map((length, index) => { let distance = length[0] let position = length[1] - let nextPosition = along(activePattern.shape, distance + 5, 'meters') + let nextPosition = along(patternLine, distance + 5, 'meters') const dir = position && nextPosition ? bearing(position, nextPosition) : 0 const color = '#000' const arrowIcon = divIcon({ diff --git a/src/main/client/editor/components/map/EditorMap.js b/src/main/client/editor/components/map/EditorMap.js index e0905335a..93e3cc2a8 100644 --- a/src/main/client/editor/components/map/EditorMap.js +++ b/src/main/client/editor/components/map/EditorMap.js @@ -169,7 +169,7 @@ export default class EditorMap extends Component { controlPoints={this.props.controlPoints} constructControlPoint={this.props.constructControlPoint} /> {route && route.tripPatterns @@ -42,8 +43,7 @@ export default class PatternsLayer extends Component { return ( ([c[1], c[0]])) || latLngs} - ref={pattern.id} - key={pattern.id} + key={tp.id} onClick={e => this._onClick(pattern, isActive, controlPoints, e)} lineCap='butt' color={lineColor} diff --git a/src/main/client/editor/reducers/settings.js b/src/main/client/editor/reducers/settings.js index caba5adec..b7dbd2689 100644 --- a/src/main/client/editor/reducers/settings.js +++ b/src/main/client/editor/reducers/settings.js @@ -88,36 +88,22 @@ const editSettings = (state = defaultState, action) => { }) } case 'UNDO_TRIP_PATTERN_EDITS': - let lastActionIndex = state.actions.length - 1 - let lastActionType = state.actions[lastActionIndex] - let lastCoordinatesIndex = state.coordinatesHistory.length - 1 - let lastControlPointsIndex = state.controlPoints.length - 1 stateUpdate = { - actions: {$splice: [[lastActionIndex, 1]]} + actions: {$splice: [[action.lastActionIndex, 1]]} } - switch (lastActionType) { + switch (action.lastActionType) { case 'ADD_CONTROL_POINT': - stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} + stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]} break case 'UPDATE_CONTROL_POINT': - stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} - stateUpdate.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} - coordinates = state.coordinatesHistory[lastCoordinatesIndex] - if (coordinates) { - stateUpdate.active = { - subEntity: {shape: {coordinates: {$set: coordinates}}} - } - } + stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]} + stateUpdate.coordinatesHistory = {$splice: [[action.lastCoordinatesIndex, 1]]} + stateUpdate.patternCoordinates = {$set: action.lastCoordinates} break case 'REMOVE_CONTROL_POINT': - stateUpdate.controlPoints = {$splice: [[lastControlPointsIndex, 1]]} - stateUpdate.coordinatesHistory = {$splice: [[lastCoordinatesIndex, 1]]} - coordinates = state.coordinatesHistory[lastCoordinatesIndex] - if (coordinates) { - stateUpdate.active = { - subEntity: {shape: {coordinates: {$set: coordinates}}} - } - } + stateUpdate.controlPoints = {$splice: [[action.lastControlPointsIndex, 1]]} + stateUpdate.coordinatesHistory = {$splice: [[action.lastCoordinatesIndex, 1]]} + stateUpdate.patternCoordinates = {$set: action.lastCoordinates} break } return update(state, stateUpdate) From efc0b3e560e5de44baacb3ac2760240e3b677fd9 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Mon, 6 Feb 2017 18:19:54 -0500 Subject: [PATCH 260/323] fix(ErrorMessage): inform users when calendars fail to delete, baseline for more improvements to req #20 --- src/main/client/editor/actions/active.js | 15 +++++++++++++-- .../datatools/common/utils/SparkUtils.java | 4 ++++ .../controllers/api/CalendarController.java | 4 +++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/main/client/editor/actions/active.js b/src/main/client/editor/actions/active.js index d34f4ceda..73fb31412 100644 --- a/src/main/client/editor/actions/active.js +++ b/src/main/client/editor/actions/active.js @@ -1,6 +1,7 @@ import clone from 'clone' import { browserHistory } from 'react-router' +import { setErrorMessage } from '../../manager/actions/status' import { secureFetch } from '../../common/util/util' import { saveFeedInfo } from './feedInfo' import { saveAgency } from './agency' @@ -162,10 +163,20 @@ export function deleteGtfsEntity (feedId, component, entityId, routeId) { if (entityId === 'new') { return dispatch(getGtfsTable(component, feedId)) } + var error = false const url = `/api/manager/secure/${component}/${entityId}?feedId=${feedId}` return secureFetch(url, getState(), 'delete') - .then(res => res.json()) - .then(entity => { + .then(res => { + if (res.status >= 300) { + error = true + } + return res.json() + }) + .then(json => { + if (error) { + dispatch(setErrorMessage(`Error deleting ${component}. ${json && json.message ? json.message : ''}`)) + return null + } if (component === 'trippattern' && routeId) { dispatch(fetchTripPatternsForRoute(feedId, routeId)) } else { diff --git a/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java index 38ac5552d..8645919d0 100644 --- a/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java +++ b/src/main/java/com/conveyal/datatools/common/utils/SparkUtils.java @@ -38,4 +38,8 @@ public static Object downloadFile(File file, Response res) { return res.raw(); } + + public static String formatJSON(String message, int code) { + return String.format("{\"result\":\"ERR\",\"message\":\"%s\",\"code\":%d}", message, code); + } } diff --git a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java index c594c9267..809aafa06 100644 --- a/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java +++ b/src/main/java/com/conveyal/datatools/editor/controllers/api/CalendarController.java @@ -1,5 +1,6 @@ package com.conveyal.datatools.editor.controllers.api; +import com.conveyal.datatools.common.utils.SparkUtils; import com.conveyal.datatools.editor.datastore.FeedTx; import com.conveyal.datatools.manager.models.JsonViews; import com.conveyal.datatools.manager.utils.json.JsonManager; @@ -19,6 +20,7 @@ import spark.Request; import spark.Response; +import static com.conveyal.datatools.common.utils.SparkUtils.formatJSON; import static spark.Spark.*; import java.util.Calendar; @@ -211,7 +213,7 @@ public static Object deleteCalendar(Request req, Response res) { Long count = tx.tripCountByCalendar.get(id); if (count != null && count > 0) { tx.rollback(); - halt(400, "Cannot delete calendar that is referenced by trips."); + halt(400, formatJSON("Cannot delete calendar that is referenced by trips.", 400)); } // drop this calendar from any schedule exceptions From c76565fceadf3bb0064d5b1ee967167a1b889601 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 7 Feb 2017 16:34:03 -0500 Subject: [PATCH 261/323] fix(Datastore): fix for issue when multiple trip saves are attempted closes #28 --- .../datatools/editor/datastore/FeedTx.java | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java index cea46f98b..faf1d06d2 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java @@ -113,15 +113,35 @@ public FeedTx(DB tx, boolean buildSecondaryIndices) { // editedSinceSnapshot = tx.getAtomicBoolean("editedSinceSnapshot") == null ? tx.createAtomicBoolean("editedSinceSnapshot", false) : tx.; } - public void commit () { + public void commit (int counter) { try { // editedSinceSnapshot.set(true); tx.commit(); - } catch (UnsupportedOperationException e) { + } catch (TxRollbackException e) { + /** NOTE: Due to an issue with concurrent modifications when saving multiple trips, + * a counter has been added to retry commits if they fail due to TxRollbackException + * per the suggestion here: https://groups.google.com/forum/#!topic/mapdb/2ukEDHL6Kq8 + */ + if (counter < 5) { + try { + Thread.sleep(100); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + LOG.warn("Commit failed (count = {}), trying again.", counter); + commit(counter + 1); + } else { + throw e; + } + } + catch (UnsupportedOperationException e) { // probably read only, but warn LOG.warn("Rollback failed; if this is a read-only database this is not unexpected"); } closed = true; } + public void commit () { + commit(0); + } public void buildSecondaryIndices () { // build secondary indices // we store indices in the mapdb not because we care about persistence, but because then they From c2bf6faaf3e5b229f9aceb6873ab1bc0fe75d184 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 8 Feb 2017 09:37:11 -0500 Subject: [PATCH 262/323] feat(DeploymentSettings): add requestLogFile setting to OTP router config closes #29 --- .../client/manager/components/DeploymentSettings.js | 12 ++++++++++++ .../manager/controllers/api/ProjectController.java | 5 +++++ .../datatools/manager/models/OtpRouterConfig.java | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/main/client/manager/components/DeploymentSettings.js b/src/main/client/manager/components/DeploymentSettings.js index d69218d99..d74a97a0e 100644 --- a/src/main/client/manager/components/DeploymentSettings.js +++ b/src/main/client/manager/components/DeploymentSettings.js @@ -168,6 +168,18 @@ export default class DeploymentSettings extends Component { this.setState(update(this.state, stateUpdate)) }} /> + + {getMessage(messages, 'deployment.routerConfig.requestLogFile')} + { + let stateUpdate = { deployment: { routerConfig: { requestLogFile: { $set: evt.target.value } } } } + this.setState(update(this.state, stateUpdate)) + }} /> + diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java index 446b18a80..4fa9f259c 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/ProjectController.java @@ -366,6 +366,11 @@ private static void updateRouterConfig(Project proj, JsonNode routerConfig) { proj.routerConfig.stairsReluctance = stairsReluctance.isNull() ? null : stairsReluctance.asDouble(); } + if (routerConfig.has("requestLogFile")) { + JsonNode requestLogFile = routerConfig.get("requestLogFile"); + proj.routerConfig.requestLogFile = requestLogFile.isNull() || requestLogFile.asText().equals("") ? null : requestLogFile.asText(); + } + if (routerConfig.has("updaters")) { updateProjectUpdaters(proj, routerConfig.get("updaters")); } diff --git a/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java b/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java index 234f80083..f1fa3c770 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java +++ b/src/main/java/com/conveyal/datatools/manager/models/OtpRouterConfig.java @@ -33,4 +33,6 @@ public static class Updater implements Serializable { } public String brandingUrlRoot; + + public String requestLogFile; } From a97031f2220046f8385df62673fae3a2d886a778 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 8 Feb 2017 14:49:35 -0500 Subject: [PATCH 263/323] request log file string --- i18n/english.yml | 1 + i18n/espanol.yml | 1 + i18n/francais.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/i18n/english.yml b/i18n/english.yml index ae1553f78..183987d4e 100644 --- a/i18n/english.yml +++ b/i18n/english.yml @@ -219,6 +219,7 @@ ProjectSettings: stairsReluctance: Stairs Reluctance carDropoffTime: Car Dropoff Time brandingUrlRoot: Branding URL Root + requestLogFile: Request log file servers: title: Servers new: Add server diff --git a/i18n/espanol.yml b/i18n/espanol.yml index b7091ab8d..5d985fe6b 100644 --- a/i18n/espanol.yml +++ b/i18n/espanol.yml @@ -191,6 +191,7 @@ ProjectSettings: stairsReluctance: Stairs Reluctance carDropoffTime: Car Dropoff Time brandingUrlRoot: Branding URL Root + requestLogFile: Request log file servers: title: Servers new: Add server diff --git a/i18n/francais.yml b/i18n/francais.yml index 658f5c0c5..b0bcc1fa3 100644 --- a/i18n/francais.yml +++ b/i18n/francais.yml @@ -191,6 +191,7 @@ ProjectSettings: stairsReluctance: Stairs Reluctance carDropoffTime: Car Dropoff Time brandingUrlRoot: Branding URL Root + requestLogFile: Request log file servers: title: Servers new: Add server From 31fc58f5ab5b5eced8c711b1d4e83bf152586997 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Thu, 9 Feb 2017 14:56:46 -0500 Subject: [PATCH 264/323] really fix trip saving issue, closes #28 --- .../datatools/editor/datastore/FeedTx.java | 24 ++----------------- .../editor/datastore/VersionedDataStore.java | 1 + 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java index faf1d06d2..cea46f98b 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/FeedTx.java @@ -113,35 +113,15 @@ public FeedTx(DB tx, boolean buildSecondaryIndices) { // editedSinceSnapshot = tx.getAtomicBoolean("editedSinceSnapshot") == null ? tx.createAtomicBoolean("editedSinceSnapshot", false) : tx.; } - public void commit (int counter) { + public void commit () { try { // editedSinceSnapshot.set(true); tx.commit(); - } catch (TxRollbackException e) { - /** NOTE: Due to an issue with concurrent modifications when saving multiple trips, - * a counter has been added to retry commits if they fail due to TxRollbackException - * per the suggestion here: https://groups.google.com/forum/#!topic/mapdb/2ukEDHL6Kq8 - */ - if (counter < 5) { - try { - Thread.sleep(100); - } catch (InterruptedException e1) { - e1.printStackTrace(); - } - LOG.warn("Commit failed (count = {}), trying again.", counter); - commit(counter + 1); - } else { - throw e; - } - } - catch (UnsupportedOperationException e) { + } catch (UnsupportedOperationException e) { // probably read only, but warn LOG.warn("Rollback failed; if this is a read-only database this is not unexpected"); } closed = true; } - public void commit () { - commit(0); - } public void buildSecondaryIndices () { // build secondary indices // we store indices in the mapdb not because we care about persistence, but because then they diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java index e40dd10dd..64c001c26 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java @@ -38,6 +38,7 @@ public class VersionedDataStore { // initialize the global database globalTxMaker = DBMaker.newFileDB(new File(globalDataDirectory, "global.db")) .mmapFileEnable() + .asyncWriteEnable() .compressionEnable() .makeTxMaker(); } From 9689342384f220eb01e351c1c54822947a71814c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 10 Feb 2017 16:04:22 -0500 Subject: [PATCH 265/323] fix(TransportNetwork): add r5 version into cached transport network name Force datatools to regenerate a new r5 transport network if the version of r5 has changed since last building the transport network closes #33 --- .../datatools/manager/jobs/ReadTransportNetworkJob.java | 4 ++++ .../com/conveyal/datatools/manager/models/FeedVersion.java | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java index afa40b6bc..1c0ece9a7 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java @@ -35,6 +35,10 @@ public void run() { is = new File(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + feedVersion.feedSourceId + "/" + feedVersion.id + "_network.dat"); try { feedVersion.transportNetwork = TransportNetwork.read(is); + // check to see if distance tables are built yet... should be removed once better caching strategy is implemeneted. + if (feedVersion.transportNetwork.transitLayer.stopToVertexDistanceTables == null) { + feedVersion.transportNetwork.transitLayer.buildDistanceTables(null); + } } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index cb41210e0..610374ece 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -32,6 +32,7 @@ import com.conveyal.gtfs.GTFSFeed; import com.conveyal.gtfs.validator.json.LoadStatus; import com.conveyal.gtfs.stats.FeedStats; +import com.conveyal.r5.common.R5Version; import com.conveyal.r5.point_to_point.builder.TNBuilderConfig; import com.conveyal.r5.transit.TransportNetwork; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -501,6 +502,7 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { return null; } this.transportNetwork = tn; + this.transportNetwork.transitLayer.buildDistanceTables(null); File tnFile = getTransportNetworkPath(); try { tn.write(tnFile); @@ -620,7 +622,7 @@ public String getR5Path () { } @JsonIgnore public File getTransportNetworkPath () { - return new File(String.join(File.separator, getR5Path(), id + "_network.dat")); + return new File(String.join(File.separator, getR5Path(), id + "_" + R5Version.describe + "_network.dat")); } @JsonIgnore From 18e85b843056c3b2baccc5718cad5e9c3d6897b3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 10 Feb 2017 17:54:51 -0500 Subject: [PATCH 266/323] fix(FeedVersionController): fix hashing on upload closes #36 --- .../api/FeedVersionController.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java index 848fe9dac..611cee802 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/FeedVersionController.java @@ -10,6 +10,7 @@ import com.conveyal.datatools.manager.models.FeedSource; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.datatools.manager.models.JsonViews; +import com.conveyal.datatools.manager.utils.HashUtils; import com.conveyal.datatools.manager.utils.json.JsonManager; import com.conveyal.r5.analyst.PointSet; import com.conveyal.r5.analyst.cluster.AnalystClusterRequest; @@ -89,6 +90,9 @@ private static FeedSource requestFeedSourceById(Request req, String action) { * x-multipart-formdata rather than a json blob. This is done because uploading files in a JSON * blob is not pretty, and we don't really need to get the Backbone object directly; page re-render isn't * a problem. + * + * Auto-fetched feeds are no longer restricted from having directly-uploaded versions, so we're not picky about + * that anymore. * @return * @throws JsonProcessingException */ @@ -97,32 +101,30 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc Auth0UserProfile userProfile = req.attribute("user"); FeedSource s = requestFeedSourceById(req, "manage"); - // Autofetched feeds are no longer restricted from directly-uploaded versions -// if (FeedSource.FeedRetrievalMethod.FETCHED_AUTOMATICALLY.equals(s.retrievalMethod)) { -// halt(400, "Feed is auto-fetched! Cannot upload."); -// } FeedVersion latest = s.getLatest(); FeedVersion v = new FeedVersion(s); v.setUser(userProfile); -// String lastModified = req.raw().getPart("lastModified").toString(); -// System.out.println("last modified: " + lastModified); + if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) { MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir")); req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement); } Part part = req.raw().getPart("file"); -// System.out.println(lastModified); LOG.info("Saving feed from upload {}", s); InputStream uploadStream; + File file = null; try { uploadStream = part.getInputStream(); - // set last modified based on value of query param - File file = v.newGtfsFile(uploadStream, Long.valueOf(req.queryParams("lastModified"))); - System.out.println(file.lastModified()); + /** + * Set last modified based on value of query param. This is determined/supplied by the client + * request because this data gets lost in the uploadStream otherwise. + */ + file = v.newGtfsFile(uploadStream, Long.valueOf(req.queryParams("lastModified"))); + LOG.info("Last modified: {}", new Date(file.lastModified())); } catch (Exception e) { e.printStackTrace(); LOG.error("Unable to open input stream from upload"); @@ -130,11 +132,19 @@ public static Boolean createFeedVersion (Request req, Response res) throws IOExc } v.hash(); + // TODO: fix hash() call when called in this context. Nothing gets hashed because the file has not been saved yet. + v.hash = HashUtils.hashFile(file); // Check that hashes don't match (as long as v and latest are not the same entry) if (latest != null && latest.hash.equals(v.hash)) { - LOG.error("Upload matches latest version."); - v.getGtfsFile().delete(); + LOG.error("Upload version {} matches latest version {}.", v.id, latest.id); + File gtfs = v.getGtfsFile(); + if (gtfs != null) { + gtfs.delete(); + } else { + file.delete(); + LOG.warn("File deleted"); + } // Uploaded feed is same as latest version v.delete(); halt(304); From 89ddec9aa2b4f5f876732339530ffb4c6c64176d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:51:49 -0500 Subject: [PATCH 267/323] fix(TimetableEditor): scroll to end when duplicating rows --- .../client/editor/components/timetable/Timetable.js | 2 +- .../editor/components/timetable/TimetableEditor.js | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/timetable/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js index 2a2cde30b..9975b5b80 100644 --- a/src/main/client/editor/components/timetable/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -101,7 +101,7 @@ export default class Timetable extends Component { ? col.width * 2 : col ? col.width - : 90 + : 200 return width } handleCellClick = (rowIndex, columnIndex) => { diff --git a/src/main/client/editor/components/timetable/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js index c22f5e3a1..c93ffbb8c 100644 --- a/src/main/client/editor/components/timetable/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -41,7 +41,11 @@ export default class TimetableEditor extends Component { constructNewRow (toClone = null) { const activePattern = this.props.route && this.props.route.tripPatterns ? this.props.route.tripPatterns.find(p => p.id === this.props.activePatternId) : null let newRow = toClone ? clone(toClone) || {} : {} - + if (toClone) { + objectPath.set(newRow, 'id', 'new') + objectPath.set(newRow, 'gtfsTripId', null) + return newRow + } // set starting time for first arrival let cumulativeTravelTime = !toClone ? 0 : objectPath.get(newRow, `stopTimes.0.arrivalTime`) @@ -83,11 +87,18 @@ export default class TimetableEditor extends Component { let arrayAscending = indexArray.sort((a, b) => { return a - b }) + const lastIndex = this.props.timetable.trips.length - 1 for (var i = 0; i < arrayAscending.length; i++) { const index = arrayAscending[i] const toClone = this.props.timetable.trips[index] const newRow = this.constructNewRow(toClone) + let stateUpdate = { + activeCell: {$set: null}, + scrollToRow: {$set: lastIndex + arrayAscending.length}, // increment selected row + scrollToColumn: {$set: 0} + } this.props.addNewTrip(newRow) + this.setState(update(this.state, stateUpdate)) } } addNewRow (blank = false, scroll = false) { From 452ff9fda729eabad46511d834ecbc0b2b25d8f2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:55:18 -0500 Subject: [PATCH 268/323] add keys where missing --- .../client/editor/components/EditorInput.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/main/client/editor/components/EditorInput.js b/src/main/client/editor/components/EditorInput.js index 95870df80..a980a6577 100644 --- a/src/main/client/editor/components/EditorInput.js +++ b/src/main/client/editor/components/EditorInput.js @@ -43,6 +43,7 @@ export default class EditorInput extends Component { uploadBrandingAsset, feedSource, getGtfsEntity, + key, fieldEdited, gtfsEntitySelected, tableData, @@ -56,15 +57,17 @@ export default class EditorInput extends Component { } = this.props const formProps = { controlId: `${editorField}`, - className: `col-xs-${field.columnWidth}`, - validationState: isNotValid - ? 'error' - : field.required - ? 'success' - : '' + key: key, + className: `col-xs-${field.columnWidth}` + } + if (isNotValid) { + formProps.validationState = 'error' + } else if (field.required) { + formProps.validationState = 'success' } const simpleFormProps = { controlId: `${editorField}`, + key: key, className: `col-xs-${field.columnWidth}` } const styles = reactCSS({ @@ -397,7 +400,7 @@ export default class EditorInput extends Component { ) case 'DAY_OF_WEEK_BOOLEAN': return ( - + {editorField === 'monday' ?
    Days of service
    : null @@ -421,11 +424,12 @@ export default class EditorInput extends Component { case 'DROPDOWN': return ( {basicLabel} { let props = {} @@ -454,6 +458,7 @@ export default class EditorInput extends Component { Date: Wed, 15 Feb 2017 09:55:44 -0500 Subject: [PATCH 269/323] tweaks for actions --- src/main/client/editor/actions/active.js | 1 - src/main/client/editor/actions/calendar.js | 10 ++- src/main/client/editor/actions/trip.js | 67 ++++++++++--------- src/main/client/editor/actions/tripPattern.js | 2 +- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/main/client/editor/actions/active.js b/src/main/client/editor/actions/active.js index 73fb31412..37e491539 100644 --- a/src/main/client/editor/actions/active.js +++ b/src/main/client/editor/actions/active.js @@ -82,7 +82,6 @@ export function setActiveGtfsEntity (feedSourceId, component, entityId, subCompo ? clone(activeEntity.tripPatterns.find(p => p.id === subEntityId)) : null const activePatternStops = getStopsForPattern(activeSubEntity, getState().editor.data.tables.stop) - console.log('active', activeSubEntity) const activeColumns = getTimetableColumns(activeSubEntity, activePatternStops) dispatch(settingActiveGtfsEntity(feedSourceId, component, entityId, subComponent, subEntityId, subSubComponent, subSubEntityId, activeEntity, activeSubEntity, activeColumns)) } diff --git a/src/main/client/editor/actions/calendar.js b/src/main/client/editor/actions/calendar.js index 4736cd8e7..5a0e3f335 100644 --- a/src/main/client/editor/actions/calendar.js +++ b/src/main/client/editor/actions/calendar.js @@ -1,4 +1,5 @@ import { secureFetch } from '../../common/util/util' +import { setErrorMessage } from '../../manager/actions/status' import { setActiveGtfsEntity } from './active' // CALENDAR + SCHEDULE_EXCEPTION @@ -87,6 +88,7 @@ export function deletingCalendar (feedId, calendar) { } } +// Unused... currently deleteGtfsEntity is used for deleting entities. export function deleteCalendar (feedId, calendar) { return function (dispatch, getState) { dispatch(deletingCalendar(feedId, calendar)) @@ -95,7 +97,13 @@ export function deleteCalendar (feedId, calendar) { } const url = `/api/manager/secure/calendar/${calendar.id}?feedId=${feedId}` return secureFetch(url, getState(), 'delete') - .then(res => res.json()) + .then(res => { + if (res.status >= 300) { + console.log(res) + dispatch(setErrorMessage('Error deleting calendar')) + } + return res.json() + }) .then(calendar => { dispatch(fetchCalendars(feedId)) }) diff --git a/src/main/client/editor/actions/trip.js b/src/main/client/editor/actions/trip.js index 66d6bcab5..a34fa9d62 100644 --- a/src/main/client/editor/actions/trip.js +++ b/src/main/client/editor/actions/trip.js @@ -85,36 +85,43 @@ export function saveTripsForCalendar (feedId, pattern, calendarId, trips) { } } -// export function saveTripsForCalendar2 (feedId, pattern, calendarId, trips) { -// return function (dispatch, getState) { -// dispatch(savingTrips(feedId, pattern, calendarId, trips)) -// const tripExists = trip.id !== 'new' && trip.id !== null -// const method = tripExists ? 'put' : 'post' -// const url = tripExists -// ? `/api/manager/secure/trip/${trip.id}?feedId=${feedId}` -// : `/api/manager/secure/trip?feedId=${feedId}` -// trip.id = tripExists ? trip.id : null -// return secureFetch(url, getState(), method, trip) -// .then(res => { -// if (res.status >= 300) { -// errorCount++ -// errorIndexes.push(index) -// return null -// } else { -// return res.json() -// } -// }) -// .then(trips => { -// // console.log(trips) -// if (errorCount) { -// dispatch(setErrorMessage(`Unknown error encountered while saving trips. Could not save ${errorCount} trips`)) -// } -// dispatch(fetchTripsForCalendar(feedId, pattern, calendarId)) -// return errorIndexes -// }) -// // return result -// } -// } +// TODO: action is under construction... +export function saveMultipleTripsForCalendar (feedId, pattern, calendarId, trips) { + return function (dispatch, getState) { + let errorCount = 0 + let errorIndexes = [] + dispatch(savingTrips(feedId, pattern, calendarId, trips)) + const newTrips = [] + const existingTrips = [] + trips.forEach(t => { + const tripExists = t.id !== 'new' && t.id !== null + if (tripExists) { + existingTrips.push(t) + } else { + newTrips.push(t) + } + }) + return secureFetch(url, getState(), method, trip) + .then(res => { + if (res.status >= 300) { + errorCount++ + errorIndexes.push(index) + return null + } else { + return res.json() + } + }) + .then(trips => { + // console.log(trips) + if (errorCount) { + dispatch(setErrorMessage(`Unknown error encountered while saving trips. Could not save ${errorCount} trips`)) + } + dispatch(fetchTripsForCalendar(feedId, pattern, calendarId)) + return errorIndexes + }) + // return result + } +} // export function saveTrip (feedId, trip) { // // return function (dispatch, getState) { diff --git a/src/main/client/editor/actions/tripPattern.js b/src/main/client/editor/actions/tripPattern.js index 2a88a24c6..b8ba147f9 100644 --- a/src/main/client/editor/actions/tripPattern.js +++ b/src/main/client/editor/actions/tripPattern.js @@ -30,7 +30,7 @@ export function receiveTripPattern (feedId, tripPattern) { export function fetchTripPatterns (feedId) { return function (dispatch, getState) { - dispatch(requestingTripPatternsForRoute(feedId)) + dispatch(requestingTripPatterns(feedId)) const url = `/api/manager/secure/trippattern?feedId=${feedId}` return secureFetch(url, getState()) .then(res => { From 6937765e7b2f006d7ddd2d417bb4aae1b1d5bf91 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:57:34 -0500 Subject: [PATCH 270/323] catch error if pattern drawing fails --- .../editor/components/pattern/EditShapePanel.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/client/editor/components/pattern/EditShapePanel.js b/src/main/client/editor/components/pattern/EditShapePanel.js index 05c3f6347..6ede03c8e 100644 --- a/src/main/client/editor/components/pattern/EditShapePanel.js +++ b/src/main/client/editor/components/pattern/EditShapePanel.js @@ -10,10 +10,14 @@ import { CLICK_OPTIONS } from '../../util' export default class EditShapePanel extends Component { async drawPatternFromStops (pattern, stops) { let newShape = await getPolyline(stops) - console.log(newShape) - this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: newShape}}) - this.props.saveActiveEntity('trippattern') - return true + if (newShape) { + this.props.updateActiveEntity(pattern, 'trippattern', {shape: {type: 'LineString', coordinates: newShape}}) + this.props.saveActiveEntity('trippattern') + return true + } else { + this.props.setErrorMessage('Error drawing pattern from stops! Some stops may be unreachable by streets.') + return false + } } renderEditSettings (isEditing) { return isEditing From db2ec13c726c703cdfcebb7505dede6a6094b450 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:57:48 -0500 Subject: [PATCH 271/323] fix validation --- src/main/client/editor/components/ScheduleExceptionForm.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/editor/components/ScheduleExceptionForm.js b/src/main/client/editor/components/ScheduleExceptionForm.js index 94c0680a3..f8183a06f 100644 --- a/src/main/client/editor/components/ScheduleExceptionForm.js +++ b/src/main/client/editor/components/ScheduleExceptionForm.js @@ -43,6 +43,7 @@ export default class ScheduleExceptionForm extends Component { dateMap[moment(d).format('YYYYMMDD')].push(allExceptions[i].id) }) } + activeEntity && activeEntity.dates.length === 0 && validate({field: 'dates', invalid: true}) return (
    From 3f00eaad54e2bc091e9f216a8189e166afc026aa Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:58:12 -0500 Subject: [PATCH 272/323] fix icon --- src/main/client/editor/components/EntityDetailsHeader.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/EntityDetailsHeader.js b/src/main/client/editor/components/EntityDetailsHeader.js index 3d998636f..ea24e0d75 100644 --- a/src/main/client/editor/components/EntityDetailsHeader.js +++ b/src/main/client/editor/components/EntityDetailsHeader.js @@ -21,6 +21,7 @@ export default class EntityDetailsHeader extends Component { resetActiveEntity, saveActiveEntity, activeEntity, + activePattern, validationErrors, setActiveEntity, feedSource, @@ -62,7 +63,7 @@ export default class EntityDetailsHeader extends Component { disabled={!entityEdited} onClick={(e) => { if (subComponent === 'trippattern') { - let pattern = activeEntity.tripPatterns.find(p => p.id === subComponent) + let pattern = activeEntity.tripPatterns.find(p => p.id === activePattern.id) resetActiveEntity(pattern, 'trippattern') } else { resetActiveEntity(activeEntity, activeComponent) @@ -102,7 +103,7 @@ export default class EntityDetailsHeader extends Component { : iconName ? - + // schedule exception icon if no icon founds : From e9647ae7ceb470810560771d2856aa433b95865b Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:58:31 -0500 Subject: [PATCH 273/323] refactor --- .../client/editor/components/EntityDetails.js | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/client/editor/components/EntityDetails.js b/src/main/client/editor/components/EntityDetails.js index b471a223f..938b57dee 100644 --- a/src/main/client/editor/components/EntityDetails.js +++ b/src/main/client/editor/components/EntityDetails.js @@ -101,7 +101,8 @@ export default class EntityDetails extends Component { const rowIndex = 0 const validationErrors = [] const currentTable = getEditorTable(activeComponent) - const inputs = currentTable.fields.map((field, colIndex) => { + const renderDefault = subComponent !== 'trippattern' && !this.state.editFareRules && activeComponent !== 'scheduleexception' + const inputs = renderDefault && currentTable.fields.map((field, colIndex) => { const isNotValid = validate(field.inputType, field.required, field.name, activeEntity[field.name], entities, activeEntity.id) isNotValid && validationErrors.push(isNotValid) return ( @@ -117,6 +118,30 @@ export default class EntityDetails extends Component { {...this.props} /> ) }) + const detailsBody = ( +
    + {/* Render relevant form based on entity type */} + {subComponent === 'trippattern' + ? + : this.state.editFareRules && activeEntity + ? + : activeComponent === 'scheduleexception' + ? validationErrors.push(invalid)} /> + :
    + + {inputs} +

    * = field is required

    + +
    + } +
    + ) return (
    @@ -125,28 +150,7 @@ export default class EntityDetails extends Component { editFareRules={this.state.editFareRules} toggleEditFareRules={(bool) => this.setState({editFareRules: bool})} {...this.props} /> -
    - {/* Render relevant form based on entity type */} - {subComponent === 'trippattern' - ? - : this.state.editFareRules && activeEntity - ? - : activeComponent === 'scheduleexception' - ? - :
    -
    - {inputs} -

    * = field is required

    -
    -
    - } -
    + {detailsBody}
    ) From 4404e7d993152ce4954ebb072d0a4bd760354076 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 09:59:50 -0500 Subject: [PATCH 274/323] remove log --- src/main/client/editor/components/map/PatternsLayer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/client/editor/components/map/PatternsLayer.js b/src/main/client/editor/components/map/PatternsLayer.js index 03e683b3a..85b1a21c2 100644 --- a/src/main/client/editor/components/map/PatternsLayer.js +++ b/src/main/client/editor/components/map/PatternsLayer.js @@ -19,7 +19,6 @@ export default class PatternsLayer extends Component { controlPoints, patternCoordinates } = this.props - console.log(this.props) return ( {route && route.tripPatterns From 4fe239669404a485c207d2752f5baee93c6ba15a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:01:45 -0500 Subject: [PATCH 275/323] add setError action and initial work on mapping gtfsIds --- .../editor/containers/ActiveGtfsEditor.js | 21 +++++++++++++------ .../containers/ActiveTripPatternList.js | 4 +++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/client/editor/containers/ActiveGtfsEditor.js b/src/main/client/editor/containers/ActiveGtfsEditor.js index c2bfb8549..fccdd2f12 100644 --- a/src/main/client/editor/containers/ActiveGtfsEditor.js +++ b/src/main/client/editor/containers/ActiveGtfsEditor.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux' import GtfsEditor from '../components/GtfsEditor' -import { componentList } from '../util/gtfs' +import { componentList, findEntityByGtfsId } from '../util/gtfs' import { fetchFeedSourceAndProject } from '../../manager/actions/feeds' import { fetchFeedInfo } from '../actions/feedInfo' import { @@ -138,11 +138,14 @@ const mapDispatchToProps = (dispatch, ownProps) => { dispatch(fetchFeedInfo(feedSourceId)) for (var i = 0; i < tablesToFetch.length; i++) { if (tablesToFetch[i] !== activeComponent) { + console.log('requesting ' + tablesToFetch[i]) dispatch(getGtfsTable(tablesToFetch[i], feedSourceId)) } } }) .then(() => { + console.log('here') + var newId = activeEntityId if (componentList.indexOf(activeComponent) !== -1) { dispatch(getGtfsTable(activeComponent, feedSourceId)) // FETCH trip patterns if route selected @@ -150,12 +153,17 @@ const mapDispatchToProps = (dispatch, ownProps) => { if (activeEntityId === 'new') { dispatch(newGtfsEntity(feedSourceId, activeComponent)) } else if (activeEntityId && entities.findIndex(e => e.id === activeEntityId) === -1) { - console.log('bad entity id, going back to ' + activeComponent) - return dispatch(setActiveGtfsEntity(feedSourceId, activeComponent)) + console.log('could not find ID... trying to map to GTFS ID') + // TODO: try to match against gtfsRouteId / gtfsStopId / gtfsAgencyId / etc. + newId = findEntityByGtfsId(activeComponent, activeEntityId, entities) + if (newId === -1) { + console.log('bad entity id, going back to ' + activeComponent) + return dispatch(setActiveGtfsEntity(feedSourceId, activeComponent)) + } } - dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, activeEntityId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) - if (activeComponent === 'route' && activeEntityId) { - dispatch(fetchTripPatternsForRoute(feedSourceId, activeEntityId)) + dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, newId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) + if (activeComponent === 'route' && newId) { + dispatch(fetchTripPatternsForRoute(feedSourceId, newId)) .then((tripPatterns) => { let pattern = tripPatterns && tripPatterns.find(p => p.id === subEntityId) if (subSubComponent === 'timetable' && activeSubSubEntity) { @@ -173,6 +181,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { for (var i = 0; i < tablesToFetch.length; i++) { if (tablesToFetch[i] !== activeComponent) { dispatch(getGtfsTable(tablesToFetch[i], feedSourceId)) + // TODO: add setActive here (e.g., for redirections from validation summary) } } } diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js index b6d33ba1d..84481e812 100644 --- a/src/main/client/editor/containers/ActiveTripPatternList.js +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -10,6 +10,7 @@ import { newGtfsEntity, updateMapSetting, cloneGtfsEntity } from '../actions/editor' +import { setErrorMessage } from '../../manager/actions/status' import { undoActiveTripPatternEdits } from '../actions/tripPattern' import { findProjectByFeedSource } from '../../manager/util' @@ -71,7 +72,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { return dispatch(newGtfsEntity(feedSourceId, component, props, save)) }, - undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) } + undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, + setErrorMessage: (message) => { dispatch(setErrorMessage(message)) } } } From 4a6479d2bca5a6244eda2bfe31c147e49e42d4b0 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:02:23 -0500 Subject: [PATCH 276/323] find entity by gtfsId --- src/main/client/editor/util/gtfs.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/client/editor/util/gtfs.js b/src/main/client/editor/util/gtfs.js index d4a01aeb8..8dc9fe8f3 100644 --- a/src/main/client/editor/util/gtfs.js +++ b/src/main/client/editor/util/gtfs.js @@ -40,6 +40,25 @@ export function getEntityBounds (entity, offset = 0.005) { } } +export function findEntityByGtfsId (component, gtfsId, entities) { + let entity + switch (component) { + case 'stop': + entity = entities.find(e => e.gtfsStopId === gtfsId) + break + case 'route': + entity = entities.find(e => e.gtfsRouteId === gtfsId) + break + case 'calendar': + entity = entities.find(e => e.gtfsServiceId === gtfsId) + break + default: + entity = null + break + } + return entity ? entity.id : -1 +} + export function getEntityName (component, entity) { if (!entity) { return '[Unnamed]' From 05b5227a34402d15d42ab68ff4b16f7a52c4149e Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:03:32 -0500 Subject: [PATCH 277/323] log error --- src/main/client/scenario-editor/utils/valhalla.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/client/scenario-editor/utils/valhalla.js b/src/main/client/scenario-editor/utils/valhalla.js index 1500470b2..54f97fd4c 100644 --- a/src/main/client/scenario-editor/utils/valhalla.js +++ b/src/main/client/scenario-editor/utils/valhalla.js @@ -5,7 +5,7 @@ import lineString from 'turf-linestring' import { getConfigProperty } from '../../common/util/config' -export async function route (points) { +export function route (points) { if (points.length < 2) { return null } @@ -17,8 +17,9 @@ export async function route (points) { costing: 'bus', locations } - const response = await fetch(`https://valhalla.mapzen.com/route?json=${JSON.stringify(json)}&api_key=${MAPZEN_TURN_BY_TURN_KEY}`) - return await response.json() + return fetch(`https://valhalla.mapzen.com/route?json=${JSON.stringify(json)}&api_key=${MAPZEN_TURN_BY_TURN_KEY}`) + .then(res => res.json()) + .catch(e => console.log(e)) } export async function polyline (points) { @@ -26,9 +27,10 @@ export async function polyline (points) { try { json = await route(points) } catch (e) { + console.log(e) return null } - if (json) { + if (json && json.trip) { const legArray = json.trip.legs.map((leg, index) => { let ignorePoints = {} for (var i = 0; i < leg.maneuvers.length; i++) { From 3941438f890058b49412ebf6d6ee997ac3d2c0b7 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:03:59 -0500 Subject: [PATCH 278/323] deploy public action --- src/main/client/manager/actions/projects.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/client/manager/actions/projects.js b/src/main/client/manager/actions/projects.js index aaf1018f0..d5830a0b7 100644 --- a/src/main/client/manager/actions/projects.js +++ b/src/main/client/manager/actions/projects.js @@ -150,6 +150,18 @@ export function updateProject (project, changes, fetchFeeds = false) { } } +export function deployPublic (project) { + return function (dispatch, getState) { + // dispatch(savingProject()) + const url = `/api/manager/secure/project/${project.id}/deployPublic` + return secureFetch(url, getState(), 'post') + .then((res) => res.json()) + .then(json => { + return json + }) + } +} + export function createProject () { return { type: 'CREATE_PROJECT' From 295941cf14daff7c4471eb343e4b99be7f892588 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:05:19 -0500 Subject: [PATCH 279/323] deploy public button and remove this.props --- .../manager/components/ProjectViewer.js | 84 +++++++++++++------ 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/src/main/client/manager/components/ProjectViewer.js b/src/main/client/manager/components/ProjectViewer.js index e395dbfb7..fdb9e6809 100644 --- a/src/main/client/manager/components/ProjectViewer.js +++ b/src/main/client/manager/components/ProjectViewer.js @@ -78,17 +78,32 @@ export default class ProjectViewer extends Component { this.props.onComponentMount(this.props) } render () { - if (!this.props.project) { + const { + project, + user, + visibilityFilter, + visibilityFilterChanged, + searchTextChanged, + onNewFeedSourceClick, + thirdPartySync, + updateAllFeeds, + downloadMergedFeed, + activeComponent, + deployPublic, + activeSubComponent + } = this.props + if (!project) { return } const messages = getComponentMessages('ProjectViewer') - const isWatchingProject = this.props.user.subscriptions.hasProjectSubscription(this.props.project.id, 'project-updated') - const projectEditDisabled = !this.props.user.permissions.isProjectAdmin(this.props.project.id) - const filteredFeedSources = this.props.project.feedSources - ? this.props.project.feedSources.filter(feedSource => { + const publicFeedsLink = `https://s3.amazonaws.com/${getConfigProperty('application.data.gtfs_s3_bucket')}/public/index.html` + const isWatchingProject = user.subscriptions.hasProjectSubscription(project.id, 'project-updated') + const projectEditDisabled = !user.permissions.isProjectAdmin(project.id) + const filteredFeedSources = project.feedSources + ? project.feedSources.filter(feedSource => { if (feedSource.isCreating) return true // feeds actively being created are always visible - let visible = feedSource.name !== null ? feedSource.name.toLowerCase().indexOf((this.props.visibilityFilter.searchText || '').toLowerCase()) !== -1 : '[unnamed project]' - switch (this.props.visibilityFilter.filter) { + let visible = feedSource.name !== null ? feedSource.name.toLowerCase().indexOf((visibilityFilter.searchText || '').toLowerCase()) !== -1 : '[unnamed project]' + switch (visibilityFilter.filter) { case 'ALL': return visible case 'STARRED': @@ -109,9 +124,9 @@ export default class ProjectViewer extends Component { { - this.props.visibilityFilterChanged(key) + visibilityFilterChanged(key) }} > All @@ -121,7 +136,7 @@ export default class ProjectViewer extends Component { this.props.searchTextChanged(evt.target.value)} + onChange={evt => searchTextChanged(evt.target.value)} /> @@ -130,7 +145,7 @@ export default class ProjectViewer extends Component { bsStyle='primary' disabled={projectEditDisabled} className='pull-right' - onClick={() => this.props.onNewFeedSourceClick()} + onClick={() => onNewFeedSourceClick()} > {getMessage(messages, 'feeds.new')} @@ -138,7 +153,7 @@ export default class ProjectViewer extends Component { {isExtensionEnabled('transitland') || isExtensionEnabled('transitfeeds') || isExtensionEnabled('mtc') ? : null } @@ -146,14 +161,14 @@ export default class ProjectViewer extends Component { bsStyle='default' disabled={projectEditDisabled} onClick={() => { - this.props.updateAllFeeds(this.props.project) + updateAllFeeds(project) }} > {getMessage(messages, 'feeds.update')} @@ -165,12 +180,12 @@ export default class ProjectViewer extends Component { } >

    - {this.props.project.name} + {project.name} {getConfigProperty('application.notifications_enabled') ? : null @@ -203,8 +218,8 @@ export default class ProjectViewer extends Component {

      -
    • {this.props.project.defaultLocationLon ? `${this.props.project.defaultLocationLat}, ${this.props.project.defaultLocationLon}` : 'n/a'}
    • -
    • {this.props.project.autoFetchFeeds ? `${this.props.project.autoFetchHour}:${this.props.project.autoFetchMinute < 10 ? '0' + this.props.project.autoFetchMinute : this.props.project.autoFetchMinute}` : 'Auto fetch disabled'}
    • +
    • {project.defaultLocationLon ? `${project.defaultLocationLat}, ${project.defaultLocationLon}` : 'n/a'}
    • +
    • {project.autoFetchFeeds ? `${project.autoFetchHour}:${project.autoFetchMinute < 10 ? '0' + project.autoFetchMinute : project.autoFetchMinute}` : 'Auto fetch disabled'}
    • {/*
    • {fs.feedVersions ? `${this.getAverageFileSize(fs.feedVersions)} MB` : 'n/a'}
    • */} @@ -213,7 +228,7 @@ export default class ProjectViewer extends Component { this._selectTab(key)} > + {isModuleEnabled('enterprise') && +
      + +

      + Note: Public feeds page can be viewed here. +

      +
      + } What is a feed source?}> A feed source defines the location or upstream source of a GTFS feed. GTFS can be populated via automatic fetch, directly editing or uploading a zip file. @@ -249,8 +279,8 @@ export default class ProjectViewer extends Component { title={{getMessage(messages, 'deployments')}} >
      @@ -262,7 +292,7 @@ export default class ProjectViewer extends Component { title={{getMessage(messages, 'settings')}} > From ae2c528f49d56b66e95e168caeabb413211d6b75 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:05:53 -0500 Subject: [PATCH 280/323] line formatting --- .../com/conveyal/datatools/editor/datastore/DatabaseTx.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/DatabaseTx.java b/src/main/java/com/conveyal/datatools/editor/datastore/DatabaseTx.java index e0e616fef..2bd77826b 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/DatabaseTx.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/DatabaseTx.java @@ -70,7 +70,8 @@ public void commit() { } catch (UnsupportedOperationException e) { // probably read only, but warn LOG.warn("Rollback failed; if this is a read-only database this is not unexpected"); - } closed = true; + } + closed = true; } public void rollback() { From b9580cd8a3e743122a343c39f1bcb7940e776ce8 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:06:20 -0500 Subject: [PATCH 281/323] close on shutdown --- .../datatools/editor/datastore/VersionedDataStore.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java index 64c001c26..7b4eec216 100644 --- a/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java +++ b/src/main/java/com/conveyal/datatools/editor/datastore/VersionedDataStore.java @@ -37,10 +37,11 @@ public class VersionedDataStore { // initialize the global database globalTxMaker = DBMaker.newFileDB(new File(globalDataDirectory, "global.db")) - .mmapFileEnable() - .asyncWriteEnable() - .compressionEnable() - .makeTxMaker(); + .mmapFileEnable() + .asyncWriteEnable() + .compressionEnable() + .closeOnJvmShutdown() + .makeTxMaker(); } /** Start a transaction in the global database */ From 3150f0c7287310c31395b1f59dd55c084b59ce3f Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:08:07 -0500 Subject: [PATCH 282/323] remove logger --- .../manager/controllers/api/DeploymentController.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java index de297ce50..76624d823 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/DeploymentController.java @@ -40,7 +40,7 @@ public class DeploymentController { private static JsonManager statusJson = new JsonManager(DeployJob.DeployStatus.class, JsonViews.UserInterface.class); - public static final Logger LOG = LoggerFactory.getLogger(DeploymentController.class); + private static final Logger LOG = LoggerFactory.getLogger(DeploymentController.class); private static HashMap deploymentJobsByServer = new HashMap(); @@ -112,7 +112,6 @@ public static Object downloadDeployment (Request req, Response res) throws IOExc public static Object getAllDeployments (Request req, Response res) throws JsonProcessingException { Auth0UserProfile userProfile = req.attribute("user"); String projectId = req.queryParams("projectId"); - System.out.println("getting deployments..."); if (!userProfile.canAdministerProject(projectId)) halt(401); From 73c2d3c33ede1cf2555f5791aeb4d4867828a979 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:11:04 -0500 Subject: [PATCH 283/323] remove log --- .../datatools/manager/controllers/api/UserController.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java b/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java index b75c03a48..70b502b38 100644 --- a/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java +++ b/src/main/java/com/conveyal/datatools/manager/controllers/api/UserController.java @@ -194,15 +194,12 @@ public static Object getRecentActivity(Request req, Response res) { List activity = new ArrayList<>(); for (Auth0UserProfile.Subscription sub : userProfile.getApp_metadata().getDatatoolsInfo().getSubscriptions()) { - System.out.println("sub type = " + sub.getType()); switch (sub.getType()) { // TODO: add all activity types case "feed-commented-on": for (String targetId : sub.getTarget()) { - System.out.println(" target: " + targetId); FeedSource fs = FeedSource.get(targetId); if(fs == null) continue; - System.out.println(" obj=" + fs); for (Note note : fs.getNotes()) { // TODO: Check if actually recent // if (note.date.after(Date.from(Instant.ofEpochSecond(from))) && note.date.before(Date.from(Instant.ofEpochSecond(to)))) { From d0685dc132cf2ea6342e00e5be2f5df6039f6122 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:11:38 -0500 Subject: [PATCH 284/323] add logger --- .../datatools/manager/jobs/ReadTransportNetworkJob.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java index 1c0ece9a7..03d186a61 100644 --- a/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java +++ b/src/main/java/com/conveyal/datatools/manager/jobs/ReadTransportNetworkJob.java @@ -4,6 +4,8 @@ import com.conveyal.datatools.manager.DataManager; import com.conveyal.datatools.manager.models.FeedVersion; import com.conveyal.r5.transit.TransportNetwork; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; @@ -16,6 +18,7 @@ * Created by landon on 10/11/16. */ public class ReadTransportNetworkJob extends MonitorableJob { + private static final Logger LOG = LoggerFactory.getLogger(ReadTransportNetworkJob.class); public FeedVersion feedVersion; public TransportNetwork result; public Status status; @@ -30,9 +33,9 @@ public ReadTransportNetworkJob (FeedVersion feedVersion, String owner) { @Override public void run() { - System.out.println("Reading network"); - File is = null; - is = new File(DataManager.config.get("application").get("data").get("gtfs").asText() + "/" + feedVersion.feedSourceId + "/" + feedVersion.id + "_network.dat"); + LOG.info("Reading network"); + File is; + is = feedVersion.getTransportNetworkPath(); try { feedVersion.transportNetwork = TransportNetwork.read(is); // check to see if distance tables are built yet... should be removed once better caching strategy is implemeneted. From 17cfd62ce85eb1feb423f4d0eeb9443e4e7d5ef3 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:21:51 -0500 Subject: [PATCH 285/323] fix create account button showing on enterprise instances --- src/main/client/public/components/PublicHeader.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/client/public/components/PublicHeader.js b/src/main/client/public/components/PublicHeader.js index 3e62a0f2d..16c6ad1ba 100644 --- a/src/main/client/public/components/PublicHeader.js +++ b/src/main/client/public/components/PublicHeader.js @@ -4,7 +4,7 @@ import { Grid, Row, Col, Button, Glyphicon, ButtonToolbar } from 'react-bootstra import { browserHistory } from 'react-router' import { LinkContainer } from 'react-router-bootstrap' -import { getConfigProperty } from '../../common/util/config' +import { isModuleEnabled } from '../../common/util/config' export default class PublicHeader extends Component { @@ -44,11 +44,11 @@ export default class PublicHeader extends Component { } {/* "Create Account" Button (only show if no active user) */} - {this.props.username || getConfigProperty('modules.enterprise.enabled') - ? null - : + : null } {/* "Log out" Button */} From bfa06064eb7ae7e5dae92af5907565af1fd95470 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 10:27:53 -0500 Subject: [PATCH 286/323] fix lint issue --- src/main/client/editor/actions/trip.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/actions/trip.js b/src/main/client/editor/actions/trip.js index a34fa9d62..f65caa34d 100644 --- a/src/main/client/editor/actions/trip.js +++ b/src/main/client/editor/actions/trip.js @@ -101,11 +101,12 @@ export function saveMultipleTripsForCalendar (feedId, pattern, calendarId, trips newTrips.push(t) } }) - return secureFetch(url, getState(), method, trip) + const createUrl = `/api/manager/secure/trip?feedId=${feedId}` + return secureFetch(createUrl, getState(), 'post') .then(res => { if (res.status >= 300) { errorCount++ - errorIndexes.push(index) + // errorIndexes.push(index) return null } else { return res.json() From 5ebaaa02a893b63b5e51d44ecf9b47b0def72ce6 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 11:55:17 -0500 Subject: [PATCH 287/323] fix(PatternStopCard): fix timepoint saving bug --- src/main/client/editor/components/pattern/PatternStopCard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/client/editor/components/pattern/PatternStopCard.js b/src/main/client/editor/components/pattern/PatternStopCard.js index 2cbb48594..8f072c9cc 100644 --- a/src/main/client/editor/components/pattern/PatternStopCard.js +++ b/src/main/client/editor/components/pattern/PatternStopCard.js @@ -119,10 +119,10 @@ class PatternStopCard extends Component { { let patternStops = [...activePattern.patternStops] - patternStops[index].timepoint = !stop.timepoint + patternStops[index].timepoint = !patternStop.timepoint updateActiveEntity(activePattern, 'trippattern', {patternStops}) saveActiveEntity('trippattern') }} From f33a36e6e7123be04b23a024a0092e72d4a72996 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 13:00:09 -0500 Subject: [PATCH 288/323] fix bug with click stop strategy --- src/main/client/editor/actions/map/stopStrategies.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/client/editor/actions/map/stopStrategies.js b/src/main/client/editor/actions/map/stopStrategies.js index 048e4c8a4..e05449261 100644 --- a/src/main/client/editor/actions/map/stopStrategies.js +++ b/src/main/client/editor/actions/map/stopStrategies.js @@ -173,12 +173,12 @@ export function addStopToPattern (pattern, stop, index) { } else { // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) patternStops.push(newStop) if (patternStops.length > 1) { - let previousStop = getState().editor.data.tables.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) + let previousStop = getState().editor.data.tables.stop.find(s => s.id === patternStops[patternStops.length - 2].stopId) console.log(previousStop) - getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], getState().editor.editSettings.followStreets) + return getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], getState().editor.editSettings.followStreets) .then(geojson => { dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}})) - dispatch(saveActiveGtfsEntity('trippattern')) + return dispatch(saveActiveGtfsEntity('trippattern')) }) } else { dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {patternStops: patternStops})) From a7d33e7c05faaaf0888eae3326c08d7466bed502 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 13:12:35 -0500 Subject: [PATCH 289/323] feat(DeploymentViewer): add preview button for testing deployed feeds closes #34 --- .../components/DeploymentPreviewButton.js | 63 +++++++++++++++++++ .../manager/components/DeploymentViewer.js | 7 ++- 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 src/main/client/manager/components/DeploymentPreviewButton.js diff --git a/src/main/client/manager/components/DeploymentPreviewButton.js b/src/main/client/manager/components/DeploymentPreviewButton.js new file mode 100644 index 000000000..a13c5eaaf --- /dev/null +++ b/src/main/client/manager/components/DeploymentPreviewButton.js @@ -0,0 +1,63 @@ +import {Icon} from '@conveyal/woonerf' +import React, {PropTypes, Component} from 'react' +import { Button } from 'react-bootstrap' + +export default class DeploymentPreviewButton extends Component { + static propTypes = { + deployment: PropTypes.object + } + render () { + const { deployment } = this.props + const { projectBounds } = deployment + // TODO: add Try it button + const server = deployment.project.otpServers.find(server => server.name === deployment.deployedTo) + let href = server && server.publicUrl + if (!href || href.length === 0) { + return null + } + const lat = (projectBounds.north + projectBounds.south) / 2 + const lon = (projectBounds.east + projectBounds.west) / 2 + + // figure out the zoom. assume that otp.js will open in a window of the same size (e.g. a new tab) + const width = window.innerWidth + const height = window.innerHeight + + // what fraction of the world is this from north to south? + // note that we are storing the denominator only, to avoid roundoff errors + const boundsHeightMerc = 180 / (projectBounds.north - projectBounds.south) + + // longitude is generally more complicated, because the length depends on the latitude + // however, because we're using a Mercator projection, the map doesn't understand this either, + // and maps 360 degrees of longitude to an invariant width + // This is why Greenland appears larger than Africa, but it does make the math easy. + const boundsWidthMerc = 360 / (projectBounds.east - projectBounds.west) + + // figure out the zoom level + // level 0 is the entireer world in a single 256x256 tile, next level + // is entire world in 256 * 2^1, then 256 * 2^2, and so on + let z = 23 + + while (true) { + const worldSize = 256 * Math.pow(2, z) + // again, store the denominator/reciprocal + const windowWidthMerc = worldSize / width + const windowHeightMerc = worldSize / height + + // if it fits. We use < not > because we have stored the reciprocals. + if (windowWidthMerc < boundsWidthMerc && windowHeightMerc < boundsHeightMerc || z === 0) { + break + } + + z-- + } + href += `#start/${lat}/${lon}/${z}/${deployment.routerId || 'default'}` + return ( + + ) + } +} diff --git a/src/main/client/manager/components/DeploymentViewer.js b/src/main/client/manager/components/DeploymentViewer.js index 5e20b8a33..5a4d66662 100644 --- a/src/main/client/manager/components/DeploymentViewer.js +++ b/src/main/client/manager/components/DeploymentViewer.js @@ -9,6 +9,7 @@ import Loading from '../../common/components/Loading' import EditableTextField from '../../common/components/EditableTextField' import { versionsSorter } from '../../common/util/util' import { getComponentMessages, getMessage } from '../../common/util/config' +import DeploymentPreviewButton from './DeploymentPreviewButton' export default class DeploymentViewer extends Component { static propTypes = { @@ -92,7 +93,11 @@ export default class DeploymentViewer extends Component { onChange={(value) => deploymentPropertyChanged(deployment, 'name', value)} /> {deployment.deployedTo - ? + ? + + {' '} + + : null } From 1458b8025cf574bfeabafad6aadee19465b077ab Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 15:09:34 -0500 Subject: [PATCH 290/323] fix(PatternStopsPanel): fallback to straight line if valhalla routing fails on snap to street closes #38 --- src/main/client/editor/actions/map/index.js | 2 +- .../editor/actions/map/stopStrategies.js | 3 +- .../editor/components/map/ControlPoint.js | 4 +- .../components/map/PatternStopsLayer.js | 8 ++- .../components/pattern/PatternStopsPanel.js | 71 ++++--------------- .../containers/ActiveTripPatternList.js | 7 ++ 6 files changed, 32 insertions(+), 63 deletions(-) diff --git a/src/main/client/editor/actions/map/index.js b/src/main/client/editor/actions/map/index.js index 79e1ccf48..f3bdabdae 100644 --- a/src/main/client/editor/actions/map/index.js +++ b/src/main/client/editor/actions/map/index.js @@ -114,7 +114,7 @@ export function extendPatternToPoint (pattern, endPoint, newEndPoint) { // get single coordinate if polyline fails if (!newShape) { - newShape = ll.toCoordinates(newEndPoint) + newShape = [ll.toCoordinates(newEndPoint)] } const updatedShape = {type: 'LineString', coordinates: [...pattern.shape.coordinates, ...newShape]} dispatch(updateActiveGtfsEntity(pattern, 'trippattern', {shape: updatedShape})) diff --git a/src/main/client/editor/actions/map/stopStrategies.js b/src/main/client/editor/actions/map/stopStrategies.js index e05449261..c1eed13cb 100644 --- a/src/main/client/editor/actions/map/stopStrategies.js +++ b/src/main/client/editor/actions/map/stopStrategies.js @@ -161,6 +161,7 @@ export function addStopToPattern (pattern, stop, index) { let patternStops = [...pattern.patternStops] let coordinates = pattern.shape && pattern.shape.coordinates let newStop = stopToStopTime(stop) + // if adding stop to end, also a proxy for extending pattern to point if (typeof index === 'undefined' || index === null) { // if shape coordinates already exist, just extend them @@ -185,7 +186,7 @@ export function addStopToPattern (pattern, stop, index) { return dispatch(saveActiveGtfsEntity('trippattern')) } } - // if not following roads + // TODO: construct shape if not following roads // updateActiveGtfsEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) } else { // if adding stop in middle patternStops.splice(index, 0, newStop) diff --git a/src/main/client/editor/components/map/ControlPoint.js b/src/main/client/editor/components/map/ControlPoint.js index fe0162b71..3563bd4e6 100644 --- a/src/main/client/editor/components/map/ControlPoint.js +++ b/src/main/client/editor/components/map/ControlPoint.js @@ -27,7 +27,7 @@ export default class ControlPoint extends Component { onDragStart={(e) => { const timerFunction = () => { const latlng = this.refs.marker.leafletElement.getLatLng() - console.log(latlng) + // console.log(latlng) handlePatternEdit(latlng, this.props.previous, this.props.next, this.props.activePattern, followStreets, patternCoordinates) .then(coords => { this.setState({latlng}) @@ -39,7 +39,7 @@ export default class ControlPoint extends Component { this.setState({timer}) }} onDragEnd={(e) => { - console.log('drag end') + // console.log('drag end') // clear timer if (this.state.timer) clearInterval(this.state.timer) const { snap, distTraveled } = handleControlPointDragEnd(e, patternCoordinates) diff --git a/src/main/client/editor/components/map/PatternStopsLayer.js b/src/main/client/editor/components/map/PatternStopsLayer.js index ec77ca5d6..2621daad0 100644 --- a/src/main/client/editor/components/map/PatternStopsLayer.js +++ b/src/main/client/editor/components/map/PatternStopsLayer.js @@ -6,15 +6,19 @@ import PatternStopPopup from './PatternStopPopup' export default class PatternStopsLayer extends Component { static propTypes = { + activePattern: PropTypes.object, + addStopToPattern: PropTypes.func, + editSettings: PropTypes.object, + removeStopFromPattern: PropTypes.func, stops: PropTypes.array } render () { const { - stops, activePattern, + addStopToPattern, editSettings, removeStopFromPattern, - addStopToPattern + stops } = this.props return ( { if (!input) { return } - let patternStops = [...this.props.activePattern.patternStops] let stop = input.entity - let coordinates = this.props.activePattern.shape && this.props.activePattern.shape.coordinates - let newStop = {stopId: stop.id, defaultDwellTime: 0, defaultTravelTime: 0} - // if adding stop to end (currently only place to add stop in stop selector) - if (typeof index === 'undefined') { - // if shape coordinates already exist, just extend them - if (coordinates) { - let endPoint = ll.toLatlng(coordinates[coordinates.length - 1]) - this.extendPatternToStop(this.props.activePattern, endPoint, {lng: stop.stop_lon, lat: stop.stop_lat}) - .then(() => { - patternStops.push(newStop) - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - }) - } else { - // if shape coordinates do not exist, add pattern stop and get shape between stops (if multiple stops exist) - patternStops.push(newStop) - if (patternStops.length > 1) { - let previousStop = this.props.stops.find(s => s.id === patternStops[patternStops.length - 2].stopId) - getSegment([[previousStop.stop_lon, previousStop.stop_lat], [stop.stop_lon, stop.stop_lat]], this.props.editSettings.followStreets) - .then(geojson => { - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: geojson.coordinates}}) - this.props.saveActiveEntity('trippattern') - }) - } else { - this.props.updateActiveEntity(this.props.activePattern, 'trippattern', {patternStops: patternStops}) - this.props.saveActiveEntity('trippattern') - } - } - // TODO: add updated shape if not following roads - // updateActiveEntity(pattern, 'trippattern', {patternStops: patternStops, shape: {type: 'LineString', coordinates: coordinates}}) - } + return this.props.addStopToPattern(this.props.activePattern, stop) } render () { const { activePattern, - updateEditSetting, editSettings, mapState, + saveActiveEntity, stops, updateActiveEntity, - saveActiveEntity + updateEditSetting } = this.props const cardStyle = { border: '1px dashed gray', diff --git a/src/main/client/editor/containers/ActiveTripPatternList.js b/src/main/client/editor/containers/ActiveTripPatternList.js index 84481e812..28841d81a 100644 --- a/src/main/client/editor/containers/ActiveTripPatternList.js +++ b/src/main/client/editor/containers/ActiveTripPatternList.js @@ -10,6 +10,12 @@ import { newGtfsEntity, updateMapSetting, cloneGtfsEntity } from '../actions/editor' +import { + // removeStopFromPattern, + // addStopAtPoint, + // addStopAtIntersection, + // addStopAtInterval, + addStopToPattern } from '../actions/map/stopStrategies' import { setErrorMessage } from '../../manager/actions/status' import { undoActiveTripPatternEdits } from '../actions/tripPattern' import { findProjectByFeedSource } from '../../manager/util' @@ -72,6 +78,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { return dispatch(newGtfsEntity(feedSourceId, component, props, save)) }, + addStopToPattern: (pattern, stop, index) => dispatch(addStopToPattern(pattern, stop, index)), undoActiveTripPatternEdits: () => { dispatch(undoActiveTripPatternEdits()) }, setErrorMessage: (message) => { dispatch(setErrorMessage(message)) } } From bb7ba3de6934961ea6149f3e926cb4acbd22bff4 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 15:29:55 -0500 Subject: [PATCH 291/323] use collapse to smooth collapse animation --- .../components/pattern/PatternStopCard.js | 147 +++++++++--------- 1 file changed, 71 insertions(+), 76 deletions(-) diff --git a/src/main/client/editor/components/pattern/PatternStopCard.js b/src/main/client/editor/components/pattern/PatternStopCard.js index 8f072c9cc..62d1ad8c6 100644 --- a/src/main/client/editor/components/pattern/PatternStopCard.js +++ b/src/main/client/editor/components/pattern/PatternStopCard.js @@ -112,82 +112,77 @@ class PatternStopCard extends Component {
    {/* Collapsible interior div */} - {stopIsActive - ? ( -
    - {/* Remove from pattern button */} - - - { - let patternStops = [...activePattern.patternStops] - patternStops[index].timepoint = !patternStop.timepoint - updateActiveEntity(activePattern, 'trippattern', {patternStops}) - saveActiveEntity('trippattern') - }} - > - Timepoint? - - - - - - - {/* default travel time inputs */} - - - - Default travel time - { - let patternStops = [...activePattern.patternStops] - patternStops[index].defaultTravelTime = value - this.setState({defaultTravelTime: value}) - updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) - }} - /> - - - - - Default dwell time - { - let patternStops = [...activePattern.patternStops] - patternStops[index].defaultDwellTime = value - this.setState({defaultDwellTime: value}) - updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) - }} - /> - - - -
    - ) - :
    - } +
    + {/* Remove from pattern button */} + + + { + let patternStops = [...activePattern.patternStops] + patternStops[index].timepoint = !patternStop.timepoint + updateActiveEntity(activePattern, 'trippattern', {patternStops}) + saveActiveEntity('trippattern') + }} + > + Timepoint? + + + + + + + {/* default travel time inputs */} + + + + Default travel time + { + let patternStops = [...activePattern.patternStops] + patternStops[index].defaultTravelTime = value + this.setState({defaultTravelTime: value}) + updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + }} + /> + + + + + Default dwell time + { + let patternStops = [...activePattern.patternStops] + patternStops[index].defaultDwellTime = value + this.setState({defaultDwellTime: value}) + updateActiveEntity(activePattern, 'trippattern', {patternStops: patternStops}) + }} + /> + + + +
    )) From 7883c0dbbd7be4171c13f12664c28e39bda69b0c Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 15:44:37 -0500 Subject: [PATCH 292/323] fix(TimetableEditor): fix delete trips for unsaved trips closes #39 --- src/main/client/editor/actions/trip.js | 8 ++++++++ .../editor/components/timetable/TimetableEditor.js | 13 ++++++++++--- .../editor/containers/ActiveTimetableEditor.js | 2 ++ src/main/client/editor/reducers/timetable.js | 4 ++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/main/client/editor/actions/trip.js b/src/main/client/editor/actions/trip.js index f65caa34d..455e19d2d 100644 --- a/src/main/client/editor/actions/trip.js +++ b/src/main/client/editor/actions/trip.js @@ -222,6 +222,14 @@ export function addNewTrip (trip) { trip } } + +export function removeTrips (indexes) { + return { + type: 'REMOVE_TRIPS', + indexes + } +} + // export function fetchTripsForCalendar (feedId, patternId, calendarId) { // return function (dispatch, getState) { // dispatch(requestingTripsForCalendar(feedId, patternId, calendarId)) diff --git a/src/main/client/editor/components/timetable/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js index c93ffbb8c..e62e23da2 100644 --- a/src/main/client/editor/components/timetable/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -60,9 +60,15 @@ export default class TimetableEditor extends Component { } objectPath.set(newRow, `stopTimes.${i}.stopId`, stop.stopId) cumulativeTravelTime += +stop.defaultTravelTime + + // only set time if timepoint set to true or null + // if (stop.timepoint === null || stop.timepoint) { objectPath.set(newRow, `stopTimes.${i}.arrivalTime`, cumulativeTravelTime) + // } cumulativeTravelTime += +stop.defaultDwellTime + // if (stop.timepoint === null || stop.timepoint) { objectPath.set(newRow, `stopTimes.${i}.departureTime`, cumulativeTravelTime) + // } } for (let i = 0; i < this.props.timetable.columns.length; i++) { let col = this.props.timetable.columns[i] @@ -119,20 +125,20 @@ export default class TimetableEditor extends Component { } } removeSelectedRows () { - let splice = [] + let indexes = [] let tripsToDelete = [] let newRows = [...this.props.timetable.trips] let selectedDescending = this.props.timetable.selected.sort((a, b) => { return b - a }) - // loop over selected array in descending order to ensure that splice operates on indexes in reverse + // loop over selected array in descending order to ensure that indexes operates on indexes in reverse for (var i = 0; i < selectedDescending.length; i++) { let rowIndex = selectedDescending[i] // removed.push([this.props.selected[i], 1]) let row = newRows[rowIndex] if (row.id === 'new') { - splice.push([rowIndex, 1]) + indexes.push([rowIndex, 1]) } else { tripsToDelete.push(row) } @@ -140,6 +146,7 @@ export default class TimetableEditor extends Component { if (tripsToDelete.length > 0) { this.props.deleteTripsForCalendar(this.props.feedSource.id, this.props.activePattern, this.props.activeScheduleId, tripsToDelete) } + this.props.removeTrips(indexes) this.props.toggleAllRows(false) } componentWillReceiveProps (nextProps) { diff --git a/src/main/client/editor/containers/ActiveTimetableEditor.js b/src/main/client/editor/containers/ActiveTimetableEditor.js index db719b2a4..739f98e40 100644 --- a/src/main/client/editor/containers/ActiveTimetableEditor.js +++ b/src/main/client/editor/containers/ActiveTimetableEditor.js @@ -7,6 +7,7 @@ import { toggleAllRows, toggleDepartureTimes, addNewTrip, + removeTrips, setOffset } from '../actions/trip' @@ -31,6 +32,7 @@ const mapDispatchToProps = (dispatch, ownProps) => { // TIMETABLE FUNCTIONS updateCellValue: (value, rowIndex, key) => dispatch(updateCellValue(value, rowIndex, key)), addNewTrip: (trip) => dispatch(addNewTrip(trip)), + removeTrips: (indexes) => dispatch(removeTrips(indexes)), toggleAllRows: (select) => dispatch(toggleAllRows(select)), toggleRowSelection: (rowIndex) => dispatch(toggleRowSelection(rowIndex)), toggleDepartureTimes: () => dispatch(toggleDepartureTimes()), diff --git a/src/main/client/editor/reducers/timetable.js b/src/main/client/editor/reducers/timetable.js index 1a8fc02ae..ede244aea 100644 --- a/src/main/client/editor/reducers/timetable.js +++ b/src/main/client/editor/reducers/timetable.js @@ -69,6 +69,10 @@ const timetable = (state = defaultState, action) => { trips: {$push: [action.trip]}, edited: {$push: [state.trips.length]} }) + case 'REMOVE_TRIPS': + return update(state, { + trips: {$splice: action.indexes} + }) case 'TOGGLE_SINGLE_TIMETABLE_ROW_SELECTION': let selectIndex = state.selected.indexOf(action.rowIndex) if (selectIndex === -1) { From b461d9c412b41b3665d3e9b448036dd7d3777de1 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 16:15:42 -0500 Subject: [PATCH 293/323] fix(TimetableEditor): fix copy/paste from excel and (now) google sheets closes #37 --- .../components/timetable/EditableCell.js | 7 ++++- .../editor/components/timetable/Timetable.js | 26 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/main/client/editor/components/timetable/EditableCell.js b/src/main/client/editor/components/timetable/EditableCell.js index 7568cc248..ba18750ce 100644 --- a/src/main/client/editor/components/timetable/EditableCell.js +++ b/src/main/client/editor/components/timetable/EditableCell.js @@ -173,7 +173,12 @@ export default class EditableCell extends Component { } handlePaste (evt) { let text = evt.clipboardData.getData('Text') - let rows = text.split(String.fromCharCode(13)) + let rowDelimiter = text.indexOf('\n') > -1 // google sheets row delimiter + ? '\n' + : text.indexOf(String.fromCharCode(13)) > -1 // excel row delimiter + ? String.fromCharCode(13) + : null + let rows = text.split(rowDelimiter) for (var i = 0; i < rows.length; i++) { rows[i] = rows[i].split(String.fromCharCode(9)) diff --git a/src/main/client/editor/components/timetable/Timetable.js b/src/main/client/editor/components/timetable/Timetable.js index 9975b5b80..9bcc68118 100644 --- a/src/main/client/editor/components/timetable/Timetable.js +++ b/src/main/client/editor/components/timetable/Timetable.js @@ -54,23 +54,27 @@ export default class Timetable extends Component { handlePastedRows (pastedRows, rowIndex, colIndex) { let newRows = [...this.props.data] let editedRows = [] - + let activeRow = rowIndex + let activeCol = colIndex // iterate over rows in pasted selection for (var i = 0; i < pastedRows.length; i++) { + activeRow = rowIndex + i editedRows.push(i) + + // construct new row if it doesn't exist if (typeof this.props.data[i + rowIndex] === 'undefined') { - console.log('adding row' + i + rowIndex) this.props.addNewRow() } // iterate over number of this.props.columns in pasted selection for (var j = 0; j < pastedRows[0].length; j++) { + activeCol = colIndex + j let path = `${rowIndex + i}.${this.props.columns[colIndex + j].key}` - // construct new row if it doesn't exist - if (typeof newRows[i + rowIndex] === 'undefined' || typeof objectPath.get(newRows, path) === 'undefined') { - // newRows.push(this.props.constructNewRow()) - // this.props.addNewRow() - } + // // construct new row if it doesn't exist + // if (typeof newRows[i + rowIndex] === 'undefined' || typeof objectPath.get(newRows, path) === 'undefined') { + // // newRows.push(this.props.constructNewRow()) + // // this.props.addNewRow() + // } let value = this.parseTime(pastedRows[i][j]) // objectPath.set(newRows, path, value) this.props.updateCellValue(value, rowIndex + i, path) @@ -82,7 +86,13 @@ export default class Timetable extends Component { } } } - let stateUpdate = {activeCell: {$set: `${rowIndex}-${colIndex}`}, data: {$set: newRows}, edited: { $push: editedRows }} + let stateUpdate = { + activeCell: {$set: `${activeRow}-${activeCol}`}, + scrollToRow: {$set: activeRow}, + scrollToColumn: {$set: activeCol} + // data: {$set: newRows}, + // edited: { $push: editedRows } + } this.setState(update(this.state, stateUpdate)) } _getColumnWidth ({ index }) { From 2383fe868dce4969cee7bf309f2e3e3aef447f85 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 15 Feb 2017 16:46:25 -0500 Subject: [PATCH 294/323] handle null routeId cases --- .../editor/models/transit/ServiceCalendar.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/editor/models/transit/ServiceCalendar.java b/src/main/java/com/conveyal/datatools/editor/models/transit/ServiceCalendar.java index 435283fd5..509ea0b1d 100755 --- a/src/main/java/com/conveyal/datatools/editor/models/transit/ServiceCalendar.java +++ b/src/main/java/com/conveyal/datatools/editor/models/transit/ServiceCalendar.java @@ -214,10 +214,20 @@ public void addDerivedInfo(final FeedTx tx) { Map tripsForRoutes = new HashMap<>(); for (Trip trip : tx.getTripsByCalendar(this.id)) { Long count = 0L; + + /** + * if for some reason, routeId ever was set to null (or never properly initialized), + * take care of that here so we don't run into null map errors. + */ + if (trip.routeId == null) { + trip.routeId = tx.tripPatterns.get(trip.patternId).routeId; + } if (tripsForRoutes.containsKey(trip.routeId)) { count = tripsForRoutes.get(trip.routeId); } - tripsForRoutes.put(trip.routeId, count + 1); + if (trip.routeId != null) { + tripsForRoutes.put(trip.routeId, count + 1); + } // routeIds.add(trip.routeId); } this.routes = tripsForRoutes; From 371bc324f71c2456338678554a39d79bda7e9b6a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 17 Feb 2017 09:45:45 -0500 Subject: [PATCH 295/323] todo clean up deployment settings --- .../java/com/conveyal/datatools/manager/models/Deployment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/Deployment.java b/src/main/java/com/conveyal/datatools/manager/models/Deployment.java index ab648ac52..fc6fb1123 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/Deployment.java +++ b/src/main/java/com/conveyal/datatools/manager/models/Deployment.java @@ -357,7 +357,7 @@ public void dump (File output, boolean includeManifest, boolean includeOsm, bool out.closeEntry(); } - + // TODO: remove branding url root here and from config.yml String brandingUrlRoot = DataManager.config .get("application").get("data").get("branding_public").asText(); OtpRouterConfig routerConfig = proj.routerConfig; From b7c08648085318d577c570681476d8d222cdad5d Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 17 Feb 2017 09:46:12 -0500 Subject: [PATCH 296/323] add turf bbox calc --- package.json | 2 ++ yarn.lock | 54 ++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index a2bced37a..72eb47407 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,8 @@ "shpjs": "^3.3.2", "truncate": "^2.0.0", "turf-along": "^3.0.10", + "turf-area": "^3.0.12", + "turf-bbox-polygon": "^3.0.12", "turf-bearing": "^3.0.10", "turf-extent": "^1.0.4", "turf-line-distance": "^3.0.10", diff --git a/yarn.lock b/yarn.lock index b93a6d7b9..08a7b7662 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3013,6 +3013,12 @@ generate-object-property@^1.1.0: dependencies: is-property "^1.0.0" +geojson-area@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/geojson-area/-/geojson-area-0.2.1.tgz#2537b0982db86309f21d2c428a4257c7a6282cc6" + dependencies: + wgs84 "0.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" @@ -3333,14 +3339,14 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.1, inherits@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - -inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0: +inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" +inherits@2.0.1, inherits@^2.0.1, inherits@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + ini@^1.3.4, ini@~1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" @@ -5731,7 +5737,7 @@ read-pkg@^1.0.0: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2: +readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.1, readable-stream@^2.1.0, readable-stream@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e" dependencies: @@ -5743,16 +5749,7 @@ readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2. string_decoder "~0.10.x" util-deprecate "~1.0.1" -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@~2.0.0, readable-stream@~2.0.6: +readable-stream@^2.0.2, readable-stream@~2.0.0, readable-stream@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" dependencies: @@ -5763,6 +5760,15 @@ readable-stream@~2.0.0, readable-stream@~2.0.6: string_decoder "~0.10.x" util-deprecate "~1.0.1" +readable-stream@~1.1.9: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readable-stream@~2.1.4: version "2.1.5" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" @@ -6749,6 +6755,18 @@ turf-along@^3.0.10: turf-distance "^3.0.12" turf-helpers "^3.0.12" +turf-area@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-area/-/turf-area-3.0.12.tgz#9b7e469ef9fb558fd147bb0c214823263bdbf13c" + dependencies: + geojson-area "^0.2.1" + +turf-bbox-polygon@^3.0.12: + version "3.0.12" + resolved "https://registry.yarnpkg.com/turf-bbox-polygon/-/turf-bbox-polygon-3.0.12.tgz#330dc0bb38322d61545df966ce6c80f685acf4f2" + dependencies: + turf-helpers "^3.0.12" + turf-bearing@^3.0.10, turf-bearing@^3.0.12: version "3.0.12" resolved "https://registry.yarnpkg.com/turf-bearing/-/turf-bearing-3.0.12.tgz#65f609dd850e7364c7771aa6ded87b0e1917fd20" @@ -7058,6 +7076,10 @@ websocket-extensions@>=0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7" +wgs84@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/wgs84/-/wgs84-0.0.0.tgz#34fdc555917b6e57cf2a282ed043710c049cdc76" + whatwg-encoding@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.1.tgz#3c6c451a198ee7aec55b1ec61d0920c67801a5f4" From 253b671f1057661ff06365aceba2f8c3f20c4fea Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 17 Feb 2017 09:52:34 -0500 Subject: [PATCH 297/323] bbox for feed version report (dev) --- .../client/manager/components/FeedVersionReport.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/client/manager/components/FeedVersionReport.js b/src/main/client/manager/components/FeedVersionReport.js index 12132dcd0..816158bd8 100644 --- a/src/main/client/manager/components/FeedVersionReport.js +++ b/src/main/client/manager/components/FeedVersionReport.js @@ -5,11 +5,13 @@ import {Icon} from '@conveyal/woonerf' import numeral from 'numeral' import Rcslider from 'rc-slider' import fileDownload from 'react-file-download' +import area from 'turf-area' +import bboxPoly from 'turf-bbox-polygon' import EditableTextField from '../../common/components/EditableTextField' import ActiveGtfsMap from '../../gtfs/containers/ActiveGtfsMap' import { VersionButtonToolbar } from './FeedVersionViewer' -import { getComponentMessages, getMessage, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' +import { getComponentMessages, getConfigProperty, getMessage, isModuleEnabled, isExtensionEnabled } from '../../common/util/config' import { getProfileLink } from '../../common/util/util' // import { downloadAsShapefile } from '../util' import Patterns from './reporter/containers/Patterns' @@ -46,6 +48,10 @@ export default class FeedVersionReport extends Component { isochroneBand: 60 * 60 } } + getBoundsArea (bounds) { + const poly = bounds && bboxPoly([bounds.west, bounds.south, bounds.east, bounds.east]) + return poly && area(poly) + } getVersionDateLabel (version) { const now = +moment() const future = version.validationSummary && version.validationSummary.startDate > now @@ -216,7 +222,7 @@ export default class FeedVersionReport extends Component { padding: '0px' }} > - + @@ -259,6 +265,10 @@ export default class FeedVersionReport extends Component { ? {Math.floor(version.validationSummary.avgDailyRevenueTime / 60 / 60 * 100) / 100} hours daily service (Tuesday) : null } + {version.validationSummary && version.validationSummary.bounds && getConfigProperty('application.dev') + ? {this.getBoundsArea(version.validationSummary.bounds)} square meters + : null + }

    From cf263c81eba3f2fb5aad5fc8a1b5d42afc151b45 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 17 Feb 2017 09:53:26 -0500 Subject: [PATCH 298/323] add route ID to attributes set on new trip --- src/main/client/editor/components/timetable/TimetableEditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/client/editor/components/timetable/TimetableEditor.js b/src/main/client/editor/components/timetable/TimetableEditor.js index e62e23da2..94c537c51 100644 --- a/src/main/client/editor/components/timetable/TimetableEditor.js +++ b/src/main/client/editor/components/timetable/TimetableEditor.js @@ -85,6 +85,7 @@ export default class TimetableEditor extends Component { objectPath.set(newRow, 'useFrequency', activePattern.useFrequency) objectPath.set(newRow, 'feedId', this.props.feedSource.id) objectPath.set(newRow, 'patternId', activePattern.id) + objectPath.set(newRow, 'routeId', activePattern.routeId) objectPath.set(newRow, 'calendarId', this.props.activeScheduleId) return newRow From a1f0a3aa3722f32e7f165ab01c3e0b90ecd8fdc2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 17 Feb 2017 10:02:50 -0500 Subject: [PATCH 299/323] work on orgs; user home page --- src/main/client/admin/actions/admin.js | 4 +- .../client/admin/actions/organizations.js | 138 ++++++++++ .../client/admin/components/CreateUser.js | 4 +- .../admin/components/OrganizationList.js | 224 ++++++++++++++++ .../admin/components/OrganizationSettings.js | 141 ++++++++++ src/main/client/admin/components/UserAdmin.js | 69 +++-- src/main/client/admin/components/UserList.js | 2 + .../client/admin/components/UserSettings.js | 27 +- .../admin/containers/ActiveUserAdmin.js | 17 +- src/main/client/admin/reducers/index.js | 12 +- .../reducers/{admin.js => organizations.js} | 25 +- src/main/client/common/containers/App.js | 9 +- .../client/common/user/UserPermissions.js | 32 +++ .../manager/components/FeedSourcePanel.js | 95 +++++++ .../manager/components/HomeProjectDropdown.js | 66 +++++ .../manager/components/RecentActivity.js | 59 +++++ .../components/UserAccountInfoPanel.js | 60 +++++ .../client/manager/components/UserHomePage.js | 250 +++--------------- .../datatools/manager/DataManager.java | 1 + .../manager/auth/Auth0UserProfile.java | 26 +- .../api/OrganizationController.java | 45 +++- .../manager/models/Organization.java | 22 +- .../datatools/manager/models/Project.java | 19 +- 23 files changed, 1049 insertions(+), 298 deletions(-) create mode 100644 src/main/client/admin/actions/organizations.js create mode 100644 src/main/client/admin/components/OrganizationList.js create mode 100644 src/main/client/admin/components/OrganizationSettings.js rename src/main/client/admin/reducers/{admin.js => organizations.js} (50%) create mode 100644 src/main/client/manager/components/FeedSourcePanel.js create mode 100644 src/main/client/manager/components/HomeProjectDropdown.js create mode 100644 src/main/client/manager/components/RecentActivity.js create mode 100644 src/main/client/manager/components/UserAccountInfoPanel.js diff --git a/src/main/client/admin/actions/admin.js b/src/main/client/admin/actions/admin.js index f9b75d141..fc0c839d0 100644 --- a/src/main/client/admin/actions/admin.js +++ b/src/main/client/admin/actions/admin.js @@ -17,14 +17,14 @@ export function receiveUsers (users, totalUserCount) { export function fetchUsers () { return function (dispatch, getState) { dispatch(requestingUsers()) - const queryString = getState().admin.userQueryString + const queryString = getState().admin.users.userQueryString let countUrl = '/api/manager/secure/usercount' if (queryString) countUrl += `?queryString=${queryString}` const getCount = secureFetch(countUrl, getState()) .then(response => response.json()) - let usersUrl = `/api/manager/secure/user?page=${getState().admin.page}` + let usersUrl = `/api/manager/secure/user?page=${getState().admin.users.page}` if (queryString) usersUrl += `&queryString=${queryString}` const getUsers = secureFetch(usersUrl, getState()) .then(response => response.json()) diff --git a/src/main/client/admin/actions/organizations.js b/src/main/client/admin/actions/organizations.js new file mode 100644 index 000000000..02ebf2bac --- /dev/null +++ b/src/main/client/admin/actions/organizations.js @@ -0,0 +1,138 @@ +import { secureFetch } from '../../common/util/util' +import { fetchProjects } from '../../manager/actions/projects' +function requestingOrganizations () { + return { + type: 'REQUESTING_ORGANIZATIONS' + } +} + +function receiveOrganizations (organizations) { + return { + type: 'RECEIVE_ORGANIZATIONS', + organizations + } +} + +export function fetchOrganizations () { + return function (dispatch, getState) { + dispatch(requestingOrganizations()) + return secureFetch('/api/manager/secure/organization', getState()) + .then(response => response.json()) + // .catch(err => console.log(err)) + .then(organizations => { + dispatch(receiveOrganizations(organizations)) + return organizations + }) + } +} + +// Single Organization Actions + +function requestingOrganization () { + return { + type: 'REQUESTING_ORGANIZATION' + } +} + +export function receiveOrganization (organization) { + return { + type: 'RECEIVE_ORGANIZATION', + organization + } +} + +export function fetchOrganization (organizationId, unsecure) { + return function (dispatch, getState) { + dispatch(requestingOrganization()) + const apiRoot = unsecure ? 'public' : 'secure' + const url = `/api/manager/${apiRoot}/organization/${organizationId}` + return secureFetch(url, getState()) + .then(response => response.json()) + // .catch(err => console.log(err)) + .then(organization => { + dispatch(receiveOrganization(organization)) + return organization + // if (!unsecure) + // return dispatch(fetchOrganizationsFeeds(organization.id)) + }) + } +} + +function deletingOrganization () { + return { + type: 'DELETING_ORGANIZATION' + } +} + +export function deletedOrganization (organization) { + return { + type: 'DELETED_ORGANIZATION', + organization + } +} + +export function deleteOrganization (organization) { + return function (dispatch, getState) { + dispatch(deletingOrganization()) + const url = `/api/manager/secure/organization/${organization.id}` + return secureFetch(url, getState(), 'delete') + .then(response => response.json()) + // .catch(err => console.log(err)) + .then(organization => { + dispatch(deletedOrganization(organization)) + return dispatch(fetchOrganizations()) + }) + } +} + +function savingOrganization (organization, changes = null) { + return { + type: 'SAVING_ORGANIZATION', + organization, + changes + } +} + +export function updateOrganization (organization, changes, fetchFeeds = false) { + return function (dispatch, getState) { + dispatch(savingOrganization(organization, changes)) + const url = `/api/manager/secure/organization/${organization.id}` + return secureFetch(url, getState(), 'put', changes) + .then((res) => { + // fetch projects because a project may have been (re)assigned to an org + dispatch(fetchProjects()) + return dispatch(fetchOrganizations()) + }) + } +} + +export function creatingOrganization (organization) { + return { + type: 'CREATING_ORGANIZATION', + organization + } +} + +export function createdOrganization (organization) { + return { + type: 'CREATED_ORGANIZATION', + organization + } +} + +// server call +export function createOrganization (organization) { + return function (dispatch, getState) { + dispatch(creatingOrganization(organization)) + console.log(organization) + const url = '/api/manager/secure/organization' + return secureFetch(url, getState(), 'post', organization) + .then(response => response.json()) + .then(org => { + dispatch(createdOrganization(org)) + // fetch projects because a project may have been (re)assigned to an org + dispatch(fetchProjects()) + return dispatch(fetchOrganizations()) + }) + } +} diff --git a/src/main/client/admin/components/CreateUser.js b/src/main/client/admin/components/CreateUser.js index 885121eea..c3b3b7d40 100644 --- a/src/main/client/admin/components/CreateUser.js +++ b/src/main/client/admin/components/CreateUser.js @@ -43,6 +43,7 @@ export default class CreateUser extends Component { render () { const messages = getComponentMessages('CreateUser') + const {creatingUser} = this.props return (
    @@ -70,6 +71,7 @@ export default class CreateUser extends Component { diff --git a/src/main/client/admin/components/OrganizationList.js b/src/main/client/admin/components/OrganizationList.js new file mode 100644 index 000000000..44cc4f40a --- /dev/null +++ b/src/main/client/admin/components/OrganizationList.js @@ -0,0 +1,224 @@ +import React, { PropTypes, Component} from 'react' +import ReactDOM from 'react-dom' +import { Panel, Modal, Row, Col, Button, FormControl, ListGroup, ListGroupItem, Image } from 'react-bootstrap' +import {Icon} from '@conveyal/woonerf' +import validator from 'validator' + +import OrganizationSettings from './OrganizationSettings' +import { getComponentMessages, getMessage } from '../../common/util/config' + +export default class OrganizationList extends Component { + static propTypes = { + // userSearch: PropTypes.func, + // page: PropTypes.number, + // perPage: PropTypes.number, + // userCount: PropTypes.number, + // projects: PropTypes.array, + // fetchProjectFeeds: PropTypes.func, + // createUser: PropTypes.func, + // setPage: PropTypes.func, + // isFetching: PropTypes.bool, + organizations: PropTypes.object + // setUserPermission: PropTypes.func, + // saveUser: PropTypes.func, + // deleteUser: PropTypes.func, + // token: PropTypes.string + } + constructor (props) { + super(props) + this.state = {} + } + componentWillMount () { + this.props.fetchOrganizations() + } + userSearch () { + this.props.userSearch(ReactDOM.findDOMNode(this.refs.searchInput).value) + } + showModal = () => { + this.setState({showModal: true}) + } + close = () => { + this.setState({showModal: false}) + } + save = () => { + const settings = this.refs.orgSettings.getSettings() + if (settings) { + this.props.createOrganization(settings) + .then(org => { + this.close() + }) + } else { + console.log('must provide org name') + // this.setState({errorMessage: true}) + } + } + render () { + console.log(this.state, this.props) + const messages = getComponentMessages('OrganizationList') + // const minUserIndex = this.props.page * this.props.perPage + 1 + // const maxUserIndex = Math.min((this.props.page + 1) * this.props.perPage, this.props.userCount) + // const maxPage = Math.ceil(this.props.userCount / this.props.perPage) - 1 + + return ( +
    + + + { + if (e.keyCode === 13) this.userSearch() + }} /> + + + + + + } + > + + {this.props.isFetching + ? + + + : this.props.organizations.data && this.props.organizations.data.map((organization, i) => { + return + }) + } + + + + + Create Organization + + + + + + + + +
    + ) + } +} + +class OrganizationRow extends Component { + static propTypes = { + organization: PropTypes.object + } + constructor (props) { + super(props) + this.state = { + isEditing: false + } + } + toggleExpansion () { + this.setState({ + isEditing: !this.state.isEditing + }) + } + + cancel () { + this.toggleExpansion() + } + + save () { + const settings = this.refs.orgSettings.getSettings() + this.props.updateOrganization(this.props.organization, settings) + this.toggleExpansion() + } + + delete () { + this.props.deleteOrganization(this.props.organization) + this.toggleExpansion() + } + render () { + const { + organization + } = this.props + return ( + + + {organization.name} + + +
    {organization.name}
    + + + + + {this.state.isEditing + ? + : null + } + {this.state.isEditing + ? + : null + } + + + } + > + { this.state.isEditing + ? + : '' + } +
    + ) + } +} diff --git a/src/main/client/admin/components/OrganizationSettings.js b/src/main/client/admin/components/OrganizationSettings.js new file mode 100644 index 000000000..19141d622 --- /dev/null +++ b/src/main/client/admin/components/OrganizationSettings.js @@ -0,0 +1,141 @@ +import React, { Component, PropTypes } from 'react' +import { ControlLabel, FormControl, FormGroup, Radio, Row, Col } from 'react-bootstrap' +import DateTimeField from 'react-bootstrap-datetimepicker' +import Select from 'react-select' +import moment from 'moment' + +import { getComponentMessages, getMessage } from '../../common/util/config' + +export default class OrganizationSettings extends Component { + static propTypes = { + organization: PropTypes.object + } + constructor (props) { + super(props) + this.state = this.props.organization || { + // default values for organization + usageTier: 'LOW', + subscriptionBeginDate: +moment().startOf('day'), + subscriptionEndDate: +moment().startOf('day').add(1, 'year').add(1, 'day') + } + } + getSettings () { + if (this.isValid()) { + return this.state + } else { + return null + } + } + isValid () { + if (this.state.name) { + return true + } else { + return false + } + } + save = () => { + if (!this.props.organization) { + this.props.createOrganization(this.state) + } else { + this.props.saveOrganization(this.props.organization) + } + } + handleChange = (evt) => { + let change = {} + console.log(evt.target.name) + change[evt.target.name] = evt.target.value + return this.setState(change) + } + handleDateChange (name, value) { + return this.setState({[name]: +value}) + } + render () { + console.log(this.state) + const { organization, projects } = this.props + const messages = getComponentMessages('OrganizationSettings') + const orgFields = ['name', 'logoUrl'] + const projectToOption = project => ({label: project.name, value: project.id, project}) + const USAGE_TIERS = [ + { + label: getMessage(messages, 'usageTier.low'), + value: 'LOW' + }, + { + label: getMessage(messages, 'usageTier.medium'), + value: 'MEDIUM' + }, + { + label: getMessage(messages, 'usageTier.high'), + value: 'HIGH' + } + ] + return ( + + +

    {getMessage(messages, 'orgDetails')}

    + {orgFields.map((f, index) => ( + + {getMessage(messages, `${f}.label`)} + + + ))} + + {getMessage(messages, 'projects')} + p.organizationId === null || p.organizationId === organization.id).map(projectToOption)} - value={this.state.projects.map(projectToOption)} + options={projects.filter(p => p.organizationId === null || organization && p.organizationId === organization.id).map(projectToOption)} + value={this.state.projects && this.state.projects.map(projectToOption)} onChange={(values) => { console.log(values) this.setState({projects: values && values.map(v => v.project) || []}) @@ -134,6 +143,22 @@ export default class OrganizationSettings extends Component {
    + + + + {getMessage(messages, 'extensions')} + this.setState({organization: value ? value.organization : null})} + placeholder={getMessage(messages, 'filterByOrg')} + /> + + */} + fetchProjectFeeds={fetchProjectFeeds} + createUser={createUser.bind(this)} /> } > - {this.props.isFetching + {isFetching ? - : this.props.users.map((user, i) => { + : users.map((user, i) => { return }) } @@ -137,6 +175,7 @@ class UserRow extends Component { saveUser: PropTypes.func, deleteUser: PropTypes.func, fetchProjectFeeds: PropTypes.func, + organizations: PropTypes.array, projects: PropTypes.array } constructor (props) { @@ -163,12 +202,33 @@ class UserRow extends Component { } delete () { - this.props.deleteUser(this.props.user) - this.toggleExpansion() + const messages = getComponentMessages('UserRow') + this.refs.confirm.open({ + title: `${getMessage(messages, 'delete')} ${this.props.user.email}?`, + body: getMessage(messages, 'deleteConfirm'), + onConfirm: () => { + this.props.deleteUser(this.props.user) + this.toggleExpansion() + } + }) } render () { - let permissions = new UserPermissions(this.props.user.app_metadata && this.props.user.app_metadata.datatools ? this.props.user.app_metadata.datatools : null) + const { + creatingUser, + user, + organizations, + projects, + fetchProjectFeeds + } = this.props + const messages = getComponentMessages('UserRow') + let permissions = new UserPermissions(user.app_metadata && user.app_metadata.datatools ? user.app_metadata.datatools : null) + const creatorIsApplicationAdmin = creatingUser.permissions.isApplicationAdmin() + const userOrganization = organizations.find(o => o.id === permissions.getOrganizationId()) + // return null if creating user is not app admin and list item user is part of a different org + if (!creatorIsApplicationAdmin && !creatingUser.permissions.hasOrganization(permissions.getOrganizationId())) { + return null + } return ( -
    {this.props.user.email} {permissions.isApplicationAdmin() ? : null}
    +
    + {user.email}{' '} + {permissions.isApplicationAdmin() + ? + : permissions.canAdministerAnOrganization() + ? + : null + }{' '} + {userOrganization && creatorIsApplicationAdmin ? : null} +
    - {this.state.isEditing @@ -201,7 +272,7 @@ class UserRow extends Component { bsStyle='primary' style={{marginRight: '5px'}} onClick={this.save.bind(this)}> - Save + {getMessage(messages, 'save')} : null } @@ -211,7 +282,7 @@ class UserRow extends Component { bsStyle='danger' style={{marginRight: '5px'}} onClick={this.delete.bind(this)}> - Delete + {getMessage(messages, 'delete')} : null } @@ -219,10 +290,13 @@ class UserRow extends Component { } > + { this.state.isEditing ? : '' } diff --git a/src/main/client/admin/components/UserSettings.js b/src/main/client/admin/components/UserSettings.js index 1e1ec4ccc..27af7173c 100644 --- a/src/main/client/admin/components/UserSettings.js +++ b/src/main/client/admin/components/UserSettings.js @@ -1,14 +1,17 @@ import React, { Component, PropTypes } from 'react' import { Row, Col, Checkbox, Panel, SplitButton, MenuItem, Label, ButtonGroup, Button } from 'react-bootstrap' import update from 'react-addons-update' -import ReactDOM from 'react-dom' +import Select from 'react-select' import allPermissions from './permissions' import { getComponentMessages, getMessage, getConfigProperty } from '../../common/util/config' export default class UserSettings extends Component { static propTypes = { + creatingUser: PropTypes.object, fetchProjectFeeds: PropTypes.func, + isCreating: PropTypes.bool, + organizations: PropTypes.array, permissions: PropTypes.object, projects: PropTypes.array } @@ -17,14 +20,26 @@ export default class UserSettings extends Component { this.state = { appAdminChecked: this.props.permissions.isApplicationAdmin(), currentProjectIndex: 0, - projectSettings: {} + projectSettings: {}, + organization: null } + + this.props.organizations.forEach((organization, i) => { + if (this.props.permissions.hasOrganization(organization.id)) { + this.state.organization = organization + if (this.props.permissions.isOrganizationAdmin(organization.id)) { + this.state.orgAdminChecked = true + } + } else if (this.props.isCreating && this.props.creatingUser.permissions.getOrganizationId() === organization.id) { + this.state.organization = organization + } + }) this.props.projects.forEach((project, i) => { let access = 'none' let defaultFeeds = [] let permissions = [] - if (this.props.permissions.hasProject(project.id)) { - if (this.props.permissions.isProjectAdmin(project.id)) { + if (this.props.permissions.hasProject(project.id, project.organizationId)) { + if (this.props.permissions.isProjectAdmin(project.id, project.organizationId)) { access = 'admin' } else { access = 'custom' @@ -50,9 +65,18 @@ export default class UserSettings extends Component { let settings = { permissions: [], projects: [], + organizations: [], client_id: getConfigProperty('auth0.client_id') } - + if (this.state.organization) { + const orgSettings = { + organization_id: this.state.organization.id, + permissions: this.state.orgAdminChecked + ? [{type: 'administer-organization'}] // this.state.orgPermissions[this.state.organization.id] + : [] + } + settings.organizations.push(orgSettings) + } this.props.projects.forEach((project, i) => { let stateProjectSettings = this.state.projectSettings[project.id] if (stateProjectSettings.access === 'none') return @@ -86,33 +110,39 @@ export default class UserSettings extends Component { this.setState({ currentProjectIndex: key }) - // this.props.projects.forEach((project, i) => { - // let access = 'none' - // let defaultFeeds = [] - // let permissions = [] - // if (this.props.permissions.hasProject(project.id)) { - // if (this.props.permissions.isProjectAdmin(project.id)) { - // access = 'admin' - // } else { - // access = 'custom' - // let projectPermissions = this.props.permissions.getProjectPermissions(project.id) - // permissions = projectPermissions.map((p) => { return p.type }) - // defaultFeeds = this.props.permissions.getProjectDefaultFeeds(project.id) - // } - // } - // this.state.projectSettings[project.id] = { access, defaultFeeds, permissions } - // }) } appAdminClicked (value) { - console.log(ReactDOM.findDOMNode(this.refs.appAdminCheckbox)) - this.setState({ + let stateUpdate = { appAdminChecked: value - }) + } + if (value) { + stateUpdate.organization = null + stateUpdate.orgAdminChecked = false + } + this.setState(stateUpdate) } + orgAdminClicked (value, projects, org) { + this.setState({orgAdminChecked: value}) + } + orgUpdated = (val) => { + let stateUpdate = { + organization: val && val.organization || null, + orgAdminChecked: false, + projectSettings: {} + } + this.props.projects.forEach(p => { + const access = 'none' + const defaultFeeds = [] + const permissions = [] + stateUpdate.projectSettings[p.id] = {access, defaultFeeds, permissions} + }) + this.setState(stateUpdate) + } projectAccessUpdated (projectId, newAccess) { var stateUpdate = {projectSettings: {[projectId]: {$merge: {access: newAccess}}}} + console.log(stateUpdate) this.setState(update(this.state, stateUpdate)) } @@ -128,9 +158,14 @@ export default class UserSettings extends Component { render () { const messages = getComponentMessages('UserSettings') - const { creatingUser } = this.props - const isAdmin = creatingUser.permissions.isApplicationAdmin() - let currentProject = this.props.projects[this.state.currentProjectIndex] + const { creatingUser, fetchProjectFeeds, organizations, projects } = this.props + const orgToOption = organization => ({label: organization.name, value: organization.id, organization}) + const creatorIsApplicationAdmin = creatingUser.permissions.isApplicationAdmin() + + // limit available projects to those that either have no org or match the current state org + const orgProjects = projects.filter(p => !p.organizationId || this.state.organization && this.state.organization.id === p.organizationId) + console.log(this.state.organization, projects, orgProjects, this.state.projectSettings) + const currentProject = orgProjects[this.state.currentProjectIndex] const getProjectLabel = (access) => { switch (access) { @@ -144,16 +179,16 @@ export default class UserSettings extends Component { - Project Settings for  + Project Settings for{' '} this.projectSelected(key)} - > - {this.props.projects.map((project, i) => { + {orgProjects.map((project, i) => { let settings = this.state.projectSettings[project.id] - if (typeof settings !== 'undefined') { + if (settings) { return {project.name} {getProjectLabel(settings.access)} } })} @@ -161,14 +196,14 @@ export default class UserSettings extends Component { } > - {this.props.projects.map((project, i) => { + {orgProjects.map((project, i) => { if (i !== this.state.currentProjectIndex) return null let settings = this.state.projectSettings[project.id] return }> - {isAdmin && + {creatorIsApplicationAdmin && this.appAdminClicked(evt.target.checked)} @@ -198,11 +233,32 @@ export default class UserSettings extends Component { {getMessage(messages, 'admin.title')} } + {!this.state.appAdminChecked && +
    + Date: Tue, 21 Feb 2017 16:45:38 -0500 Subject: [PATCH 315/323] style(client): lint fixes (prefer consts, a11y) --- .../admin/components/OrganizationList.js | 2 +- .../admin/components/OrganizationSettings.js | 2 +- src/main/client/admin/components/UserList.js | 2 +- .../client/admin/components/UserSettings.js | 14 +-- .../manager/components/FeedSourcePanel.js | 2 +- .../manager/components/HomeProjectDropdown.js | 4 +- .../client/manager/components/NotesViewer.js | 2 +- .../client/public/components/UserAccount.js | 102 +++++++++--------- 8 files changed, 65 insertions(+), 65 deletions(-) diff --git a/src/main/client/admin/components/OrganizationList.js b/src/main/client/admin/components/OrganizationList.js index 2a3150fb1..5c5afb223 100644 --- a/src/main/client/admin/components/OrganizationList.js +++ b/src/main/client/admin/components/OrganizationList.js @@ -89,7 +89,7 @@ export default class OrganizationList extends Component { : organizations.data && organizations.data.map((organization, i) => { const orgUsers = users.data ? users.data.filter(u => { - let permissions = new UserPermissions(u.app_metadata && u.app_metadata.datatools ? u.app_metadata.datatools : null) + const permissions = new UserPermissions(u.app_metadata && u.app_metadata.datatools ? u.app_metadata.datatools : null) return permissions.getOrganizationId() === organization.id }) : [] return { - let change = {} + const change = {} console.log(evt.target.name) change[evt.target.name] = evt.target.value return this.setState(change) diff --git a/src/main/client/admin/components/UserList.js b/src/main/client/admin/components/UserList.js index bba8e02e6..0ce33a03a 100644 --- a/src/main/client/admin/components/UserList.js +++ b/src/main/client/admin/components/UserList.js @@ -222,7 +222,7 @@ class UserRow extends Component { fetchProjectFeeds } = this.props const messages = getComponentMessages('UserRow') - let permissions = new UserPermissions(user.app_metadata && user.app_metadata.datatools ? user.app_metadata.datatools : null) + const permissions = new UserPermissions(user.app_metadata && user.app_metadata.datatools ? user.app_metadata.datatools : null) const creatorIsApplicationAdmin = creatingUser.permissions.isApplicationAdmin() const userOrganization = organizations.find(o => o.id === permissions.getOrganizationId()) // return null if creating user is not app admin and list item user is part of a different org diff --git a/src/main/client/admin/components/UserSettings.js b/src/main/client/admin/components/UserSettings.js index be5a26473..4caea9061 100644 --- a/src/main/client/admin/components/UserSettings.js +++ b/src/main/client/admin/components/UserSettings.js @@ -113,7 +113,7 @@ export default class UserSettings extends Component { } appAdminClicked (value) { - let stateUpdate = { + const stateUpdate = { appAdminChecked: value } if (value) { @@ -127,7 +127,7 @@ export default class UserSettings extends Component { this.setState({orgAdminChecked: value}) } orgUpdated = (val) => { - let stateUpdate = { + const stateUpdate = { organization: val && val.organization || null, orgAdminChecked: false, projectSettings: {} @@ -365,9 +365,9 @@ class ProjectSettings extends Component {

    {getMessage(messages, 'project.feeds')}

    {feedSources ? feedSources.map((feed, i) => { - let name = (feed.name === '') ? '(unnamed feed)' : feed.name - let refName = 'feed-' + feed.id - let checked = settings.defaultFeeds.indexOf(feed.id) !== -1 + const name = (feed.name === '') ? '(unnamed feed)' : feed.name + const refName = 'feed-' + feed.id + const checked = settings.defaultFeeds.indexOf(feed.id) !== -1 return { this[refName] = ref }} key={feed.id} @@ -383,8 +383,8 @@ class ProjectSettings extends Component {

    {getMessage(messages, 'project.permissions')}

    {allPermissions.map((permission, i) => { - let refName = 'permission-' + permission.type - let checked = settings.permissions.indexOf(permission.type) !== -1 + const refName = 'permission-' + permission.type + const checked = settings.permissions.indexOf(permission.type) !== -1 return { this[refName] = ref }} key={permission.type} diff --git a/src/main/client/manager/components/FeedSourcePanel.js b/src/main/client/manager/components/FeedSourcePanel.js index c2a604e65..df410ded5 100644 --- a/src/main/client/manager/components/FeedSourcePanel.js +++ b/src/main/client/manager/components/FeedSourcePanel.js @@ -37,7 +37,7 @@ export default class FeedSourcePanel extends Component { } const feedVisibilityFilter = (feed) => { const name = feed.name || 'unnamed' - let visible = name.toLowerCase().indexOf((visibilityFilter.searchText || '').toLowerCase()) !== -1 + const visible = name.toLowerCase().indexOf((visibilityFilter.searchText || '').toLowerCase()) !== -1 switch (visibilityFilter.filter) { case 'ALL': return visible diff --git a/src/main/client/manager/components/HomeProjectDropdown.js b/src/main/client/manager/components/HomeProjectDropdown.js index 7a78ab748..a21084cef 100644 --- a/src/main/client/manager/components/HomeProjectDropdown.js +++ b/src/main/client/manager/components/HomeProjectDropdown.js @@ -26,7 +26,7 @@ export default class HomeProjectDropdown extends Component { id='context-dropdown' title={activeProject ? {activeProject.name} - : {user.profile.nickname} + : {user.email} {user.profile.nickname} } // onSelect={(eventKey) => { // setActiveProject(eventKey) @@ -35,7 +35,7 @@ export default class HomeProjectDropdown extends Component { {activeProject && ( - {user.profile.nickname} + {user.email} {user.profile.nickname} )} diff --git a/src/main/client/manager/components/NotesViewer.js b/src/main/client/manager/components/NotesViewer.js index 3b9e28bf1..15415a88e 100644 --- a/src/main/client/manager/components/NotesViewer.js +++ b/src/main/client/manager/components/NotesViewer.js @@ -97,7 +97,7 @@ export default class NotesViewer extends Component {

    {getMessage(messages, 'postComment')}

    - + {user.email} {userLink}}> diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index dc1c52afa..ef6424cb6 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -23,7 +23,7 @@ export default class UserAccount extends Component { } const messages = getComponentMessages('UserAccount') - let subscriptions = this.props.user.profile.app_metadata.datatools.find(dt => dt.client_id === getConfigProperty('auth0.client_id')).subscriptions + const subscriptions = this.props.user.profile.app_metadata.datatools.find(dt => dt.client_id === getConfigProperty('auth0.client_id')).subscriptions const accountSections = [ { id: 'profile', @@ -69,56 +69,56 @@ export default class UserAccount extends Component { id: 'notifications', hidden: !getConfigProperty('application.notifications_enabled'), component: -
    - Notification methods} +
    + Notification methods} > - - -

    Watching

    -

    Receive updates to any feed sources or comments you are watching.

    - Email{' '}Web -
    -
    -
    - - - {getMessage(messages, 'notifications.subscriptions')} - + {getMessage(messages, 'notifications.unsubscribeAll')} + + {getMessage(messages, 'notifications.subscriptions')} + } > -
      - {subscriptions && subscriptions.length ? subscriptions.map(sub => { - return ( -
    • - {sub.type.replace('-', ' ')}{' '} - { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} +
        + {subscriptions && subscriptions.length ? subscriptions.map(sub => { + return ( +
      • + {sub.type.replace('-', ' ')}{' '} + { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} /> -
          - {sub.target.length ? sub.target.map(target => { - let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null - if (this.props.projects) { - for (var i = 0; i < this.props.projects.length; i++) { - let feed = this.props.projects[i].feedSources +
            + {sub.target.length ? sub.target.map(target => { + let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null + if (this.props.projects) { + for (var i = 0; i < this.props.projects.length; i++) { + const feed = this.props.projects[i].feedSources ? this.props.projects[i].feedSources.find(fs => fs.id === target) : null fs = feed || fs } - } - return ( -
          • + } + return ( +
          • { fs ? {fs.name} : {target} @@ -130,18 +130,18 @@ export default class UserAccount extends Component { onClick={() => { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} />
          • - ) - }) :
          • No feeds subscribed to.
          • - } -
          - ) - }) + }) :
        • No feeds subscribed to.
        • + } +
        +
      • + ) + }) :
      • No subscriptions.
      • } -
      - -
    + +
    +
    }, { id: 'billing', From 291b7cac24c552c86050ae2f60a5bc040be05dd2 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Tue, 21 Feb 2017 17:26:57 -0500 Subject: [PATCH 316/323] lint fixes --- src/main/client/editor/actions/editor.js | 37 +++++ .../client/editor/components/EditorInput.js | 16 +- .../editor/containers/ActiveGtfsEditor.js | 40 +---- .../validation/GtfsValidationViewer.js | 8 +- .../client/public/components/UserAccount.js | 145 +++++++++--------- 5 files changed, 127 insertions(+), 119 deletions(-) diff --git a/src/main/client/editor/actions/editor.js b/src/main/client/editor/actions/editor.js index 5598f4a82..c6bffbd25 100644 --- a/src/main/client/editor/actions/editor.js +++ b/src/main/client/editor/actions/editor.js @@ -3,11 +3,14 @@ import fetch from 'isomorphic-fetch' import { secureFetch, generateUID, generateRandomInt, generateRandomColor, idealTextColor } from '../../common/util/util' import { fetchFeedInfo } from './feedInfo' import { fetchAgencies } from './agency' +import { fetchTripsForCalendar } from './trip' import { fetchStops } from './stop' import { fetchRoutes } from './route' import { fetchFares } from './fare' +import { fetchTripPatternsForRoute } from './tripPattern' import { fetchCalendars, fetchScheduleExceptions } from './calendar' import { saveActiveGtfsEntity, setActiveGtfsEntity } from './active' +import { componentList, findEntityByGtfsId } from '../util/gtfs' export function createGtfsEntity (feedSourceId, component, props) { return { @@ -173,3 +176,37 @@ export function receiveGtfsEntities (gtfsEntities) { gtfsEntities } } + +export function fetchActiveTable (activeTable, newId, activeEntityId, feedSourceId, subComponent, subEntityId, subSubComponent, activeSubSubEntity) { + return function (dispatch, getState) { + if (componentList.indexOf(activeTable) !== -1) { + dispatch(getGtfsTable(activeTable, feedSourceId)) + // FETCH trip patterns if route selected + .then((entities) => { + if (activeEntityId === 'new') { + dispatch(newGtfsEntity(feedSourceId, activeTable)) + } else if (activeEntityId && entities.findIndex(e => e.id === activeEntityId) === -1) { + console.log('could not find ID... trying to map to GTFS ID') + // Attempt to match against gtfsRouteId / gtfsStopId / gtfsAgencyId / etc. + newId = findEntityByGtfsId(activeTable, activeEntityId, entities) + if (newId === -1) { + console.log('bad entity id, going back to ' + activeTable) + return dispatch(setActiveGtfsEntity(feedSourceId, activeTable)) + } + } + dispatch(setActiveGtfsEntity(feedSourceId, activeTable, newId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) + if (activeTable === 'route' && newId) { + dispatch(fetchTripPatternsForRoute(feedSourceId, newId)) + .then((tripPatterns) => { + const pattern = tripPatterns && tripPatterns.find(p => p.id === subEntityId) + if (subSubComponent === 'timetable' && activeSubSubEntity) { + dispatch(fetchTripsForCalendar(feedSourceId, pattern, activeSubSubEntity)) + } + }) + } + }) + } else { + dispatch(setActiveGtfsEntity(feedSourceId)) + } + } +} diff --git a/src/main/client/editor/components/EditorInput.js b/src/main/client/editor/components/EditorInput.js index 618f53dc3..d56e0c26a 100644 --- a/src/main/client/editor/components/EditorInput.js +++ b/src/main/client/editor/components/EditorInput.js @@ -43,7 +43,6 @@ export default class EditorInput extends Component { uploadBrandingAsset, feedSource, getGtfsEntity, - key, fieldEdited, gtfsEntitySelected, tableData, @@ -57,7 +56,6 @@ export default class EditorInput extends Component { } = this.props const formProps = { controlId: `${editorField}`, - key: key, className: `col-xs-${field.columnWidth}` } if (isNotValid) { @@ -67,7 +65,6 @@ export default class EditorInput extends Component { } const simpleFormProps = { controlId: `${editorField}`, - key: key, className: `col-xs-${field.columnWidth}` } const styles = reactCSS({ @@ -139,10 +136,10 @@ export default class EditorInput extends Component { ) case 'URL': const elements = [ - + {basicLabel} { const props = {} @@ -199,7 +196,6 @@ export default class EditorInput extends Component { placeholder='Select zone ID...' clearable noResultsText={`No zones found. Specify zones in stop.`} - key={Math.random()} value={currentValue} onChange={(input) => { const props = {} @@ -417,13 +413,13 @@ export default class EditorInput extends Component { ) case 'DAY_OF_WEEK_BOOLEAN': return ( - - + + {editorField === 'monday' ?
    Days of service
    : null }
    - + {basicLabel} @@ -475,7 +470,6 @@ export default class EditorInput extends Component { { } }) .then(() => { - console.log('here') var newId = activeEntityId - if (componentList.indexOf(activeComponent) !== -1) { - dispatch(getGtfsTable(activeComponent, feedSourceId)) - // FETCH trip patterns if route selected - .then((entities) => { - if (activeEntityId === 'new') { - dispatch(newGtfsEntity(feedSourceId, activeComponent)) - } else if (activeEntityId && entities.findIndex(e => e.id === activeEntityId) === -1) { - console.log('could not find ID... trying to map to GTFS ID') - // TODO: try to match against gtfsRouteId / gtfsStopId / gtfsAgencyId / etc. - newId = findEntityByGtfsId(activeComponent, activeEntityId, entities) - if (newId === -1) { - console.log('bad entity id, going back to ' + activeComponent) - return dispatch(setActiveGtfsEntity(feedSourceId, activeComponent)) - } - } - dispatch(setActiveGtfsEntity(feedSourceId, activeComponent, newId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) - if (activeComponent === 'route' && newId) { - dispatch(fetchTripPatternsForRoute(feedSourceId, newId)) - .then((tripPatterns) => { - const pattern = tripPatterns && tripPatterns.find(p => p.id === subEntityId) - if (subSubComponent === 'timetable' && activeSubSubEntity) { - dispatch(fetchTripsForCalendar(feedSourceId, pattern, activeSubSubEntity)) - } - }) - } - }) - } else { - dispatch(setActiveGtfsEntity(feedSourceId)) - } + dispatch(fetchActiveTable(activeComponent, newId, activeEntityId, feedSourceId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) }) } else { dispatch(fetchFeedInfo(feedSourceId)) for (var i = 0; i < tablesToFetch.length; i++) { + console.log(activeComponent, initialProps.tableData[activeComponent]) if (tablesToFetch[i] !== activeComponent) { dispatch(getGtfsTable(tablesToFetch[i], feedSourceId)) // TODO: add setActive here (e.g., for redirections from validation summary) } } + var newId = activeEntityId + dispatch(fetchActiveTable(activeComponent, newId, activeEntityId, feedSourceId, subComponent, subEntityId, subSubComponent, activeSubSubEntity)) } // TODO: replace fetch trip patterns with map layer diff --git a/src/main/client/manager/components/validation/GtfsValidationViewer.js b/src/main/client/manager/components/validation/GtfsValidationViewer.js index 3a524c9d8..141db8e55 100644 --- a/src/main/client/manager/components/validation/GtfsValidationViewer.js +++ b/src/main/client/manager/components/validation/GtfsValidationViewer.js @@ -1,10 +1,12 @@ import React from 'react' -import { Panel, Table, Badge, Button } from 'react-bootstrap' +import { Panel, Table, + // Badge, + Button } from 'react-bootstrap' import { LinkContainer } from 'react-router-bootstrap' -import {Icon} from '@conveyal/woonerf' +// import {Icon} from '@conveyal/woonerf' import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table' -import { getComponentMessages, getMessage } from '../../../common/util/config' +// import { getComponentMessages, getMessage } from '../../../common/util/config' export default class GtfsValidationViewer extends React.Component { diff --git a/src/main/client/public/components/UserAccount.js b/src/main/client/public/components/UserAccount.js index ef6424cb6..7dfd30a67 100644 --- a/src/main/client/public/components/UserAccount.js +++ b/src/main/client/public/components/UserAccount.js @@ -69,79 +69,82 @@ export default class UserAccount extends Component { id: 'notifications', hidden: !getConfigProperty('application.notifications_enabled'), component: -
    - Notification methods} - > - - -

    Watching

    -

    Receive updates to any feed sources or comments you are watching.

    - Email{' '}Web -
    -
    -
    - - - {getMessage(messages, 'notifications.subscriptions')} - - } - > -
      - {subscriptions && subscriptions.length ? subscriptions.map(sub => { - return ( -
    • - {sub.type.replace('-', ' ')}{' '} - { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} - /> -
        - {sub.target.length ? sub.target.map(target => { - let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null - if (this.props.projects) { - for (var i = 0; i < this.props.projects.length; i++) { - const feed = this.props.projects[i].feedSources - ? this.props.projects[i].feedSources.find(fs => fs.id === target) - : null - fs = feed || fs - } - } - return ( -
      • - { - fs ? {fs.name} - : {target} - } {' '} - { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} - /> -
      • - ) - }) :
      • No feeds subscribed to.
      • +
        + Notification methods} + > + + +

        Watching

        +

        Receive updates to any feed sources or comments you are watching.

        + Email{' '}Web +
        +
        +
        + + + {getMessage(messages, 'notifications.subscriptions')} + } + > +
          + {subscriptions && subscriptions.length + ? subscriptions.map(sub => { + return ( +
        • + {sub.type.replace('-', ' ')}{' '} + { this.props.removeUserSubscription(this.props.user.profile, sub.type) }} + /> +
            + {sub.target.length + ? sub.target.map(target => { + let fs = null // this.props.projects ? this.props.projects.reduce(proj => proj.feedSources.filter(fs => fs.id === target)) : null + if (this.props.projects) { + for (var i = 0; i < this.props.projects.length; i++) { + const feed = this.props.projects[i].feedSources + ? this.props.projects[i].feedSources.find(fs => fs.id === target) + : null + fs = feed || fs + } + } + return ( +
          • + { + fs ? {fs.name} + : {target} + } {' '} + { this.props.updateUserSubscription(this.props.user.profile, target, sub.type) }} + /> +
          • + ) + }) + :
          • No feeds subscribed to.
          • + } +
          +
        • + ) + }) + :
        • No subscriptions.
        • + }
        - - ) - }) - :
      • No subscriptions.
      • - } -
      - -
    +
    +
    }, { id: 'billing', From 86a74bedd0482377cad21b863d86bb6aaff13307 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 22 Feb 2017 12:03:17 -0500 Subject: [PATCH 317/323] fix path for osm storage --- .../datatools/manager/models/FeedVersion.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java index bab9d2839..eb1010c6f 100644 --- a/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java +++ b/src/main/java/com/conveyal/datatools/manager/models/FeedVersion.java @@ -521,7 +521,13 @@ public TransportNetwork buildTransportNetwork(EventBus eventBus) { @JsonIgnore public static File getOSMFile(Rectangle2D bounds) { if (bounds != null) { - return new File(String.format("%s%.6f_%.6f_%.6f_%.6f.osm.pbf", getOSMPath(), bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY())); + String baseDir = FeedStore.basePath.getAbsolutePath() + File.separator + "osm"; + File osmPath = new File(String.format("%s/%.6f_%.6f_%.6f_%.6f", baseDir, bounds.getMaxX(), bounds.getMaxY(), bounds.getMinX(), bounds.getMinY())); + if (!osmPath.exists()) { + osmPath.mkdirs(); + } + File osmFile = new File(osmPath.getAbsolutePath() + "/data.osm.pbf"); + return osmFile; } else { return null; @@ -635,13 +641,4 @@ public String getR5Path () { public File getTransportNetworkPath () { return new File(String.join(File.separator, getR5Path(), id + "_" + R5Version.describe + "_network.dat")); } - - @JsonIgnore - public static String getOSMPath () { - File osm = new File(FeedStore.basePath.getAbsolutePath() + File.separator + "osm"); - if (!osm.exists()) { - osm.mkdirs(); - } - return osm.getAbsolutePath(); - } } From 6641ffc91536f02d91360c69b5f2b99950cc832a Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Wed, 22 Feb 2017 14:11:38 -0500 Subject: [PATCH 318/323] feat(AlertEditor): add more detail to alert editor for mtc closes #23 --- package.json | 1 + .../alerts/components/AffectedEntity.js | 3 +- .../alerts/components/AffectedServices.js | 29 ++++++----- .../client/alerts/components/AlertEditor.js | 49 ++++++++++++------- .../client/alerts/components/ModeSelector.js | 2 +- src/main/client/index.css | 2 + yarn.lock | 6 +++ 7 files changed, 60 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index a9d4f89ae..8e1106c96 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "react-router-redux": "^4.0.0", "react-select": "^1.0.0-beta14", "react-sidebar": "^2.1.2", + "react-toggle": "^3.0.0", "react-virtualized": "^8.5.0", "react-virtualized-select": "^2.1.0", "reactcss": "^1.0.4", diff --git a/src/main/client/alerts/components/AffectedEntity.js b/src/main/client/alerts/components/AffectedEntity.js index 92e0c304d..af1288159 100644 --- a/src/main/client/alerts/components/AffectedEntity.js +++ b/src/main/client/alerts/components/AffectedEntity.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' import {Icon} from '@conveyal/woonerf' -import { ListGroupItem, Row, Col, Button, Collapse, Glyphicon } from 'react-bootstrap' +import { ListGroupItem, Row, Col, Button, Collapse, Glyphicon, Label } from 'react-bootstrap' import { getFeed } from '../../common/util/modules' import { getRouteNameAlerts } from '../../editor/util/gtfs' import AgencySelector from './AgencySelector' @@ -35,6 +35,7 @@ export default class AffectedEntity extends Component { case 'AGENCY' : return ( + {' '} {agencyName}
    Note: this selection will apply to all stops and routes for {agencyName}.
    diff --git a/src/main/client/alerts/components/AffectedServices.js b/src/main/client/alerts/components/AffectedServices.js index c081f520e..9cb90d977 100644 --- a/src/main/client/alerts/components/AffectedServices.js +++ b/src/main/client/alerts/components/AffectedServices.js @@ -2,6 +2,7 @@ import React, {Component} from 'react' import { Panel, Label, ListGroup, ListGroupItem, Row, Col, Button } from 'react-bootstrap' import AffectedEntity from './AffectedEntity' +import { isExtensionEnabled } from '../../common/util/config' import GtfsSearch from '../../gtfs/components/gtfssearch' export default class AffectedServices extends Component { @@ -21,9 +22,11 @@ export default class AffectedServices extends Component { }}> Add Agency - + {!isExtensionEnabled('mtc') && + + } e.type === 'MODE').length } ] + const summary = counts.map(c => { + return c.count + ? + : null + }).filter(c => c !== null) return ( - Affected Service{counts.map(c => { - return c.count - ? - : null - })} + Alert applies to:{' '} + {summary.length ? summary : [make selection below]} ) } diff --git a/src/main/client/alerts/components/AlertEditor.js b/src/main/client/alerts/components/AlertEditor.js index ef1bf0cf2..d4239a5be 100644 --- a/src/main/client/alerts/components/AlertEditor.js +++ b/src/main/client/alerts/components/AlertEditor.js @@ -2,8 +2,10 @@ import {Icon} from '@conveyal/woonerf' import React from 'react' import Helmet from 'react-helmet' import { sentence as toSentenceCase } from 'change-case' -import { Grid, Row, Col, ButtonGroup, Button, FormControl, ControlLabel, FormGroup } from 'react-bootstrap' +import { Grid, Row, Col, ButtonToolbar, Button, FormControl, ControlLabel, FormGroup } from 'react-bootstrap' import DateTimeField from 'react-bootstrap-datetimepicker' +import Toggle from 'react-toggle' +// import Switch from 'rc-switch' import ManagerPage from '../../common/components/ManagerPage' import Loading from '../../common/components/Loading' @@ -104,28 +106,19 @@ export default class AlertEditor extends React.Component { /> - + - - + + - - + > Save - + > Delete + + + } + // unCheckedChildren={} + checked={alert.published} + onChange={(evt) => onPublishClick(alert, !alert.published)} /> + + @@ -149,7 +159,10 @@ export default class AlertEditor extends React.Component { - Alert Title + + Alert Title +
    Note: alert title serves as text for eTID alerts. Use descriptive language so it can serve as a standalone alert.
    +
    - {modes.map((mode) => ())} + {modes.map((mode, i) => ())}
    ) diff --git a/src/main/client/index.css b/src/main/client/index.css index 79e555c19..91ee5f5b4 100644 --- a/src/main/client/index.css +++ b/src/main/client/index.css @@ -8,9 +8,11 @@ @import url(node_modules/react-bootstrap-datetimepicker/css/bootstrap-datetimepicker.min.css); @import url(node_modules/react-select/dist/react-select.css); + @import url(node_modules/react-virtualized/styles.css); @import url(node_modules/react-virtualized-select/styles.css); @import url(node_modules/rc-slider/assets/index.css); +@import url(node_modules/react-toggle/style.css); @import url(src/main/client/common/style.css); @import url(src/main/client/alerts/style.css); diff --git a/yarn.lock b/yarn.lock index fc5688a39..fb04e9ae1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5661,6 +5661,12 @@ react-sidebar@^2.1.2: version "2.2.1" resolved "https://registry.yarnpkg.com/react-sidebar/-/react-sidebar-2.2.1.tgz#a8faf6a3c62ddc562c70680d5d016fe9741b585f" +react-toggle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-toggle/-/react-toggle-3.0.0.tgz#4929b2a0903c67f3d3db0cb68ee42be88c36ffaa" + dependencies: + classnames "^2.2.5" + react-virtualized-select@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-2.1.0.tgz#dbb05a700e198fcf0f99e59166065750f63c5685" From cbaccf9fa63fe87ed496e98efc030651aea5c786 Mon Sep 17 00:00:00 2001 From: Landon Reed Date: Fri, 24 Feb 2017 13:57:54 -0500 Subject: [PATCH 319/323] hide orgs on enterprise --- src/main/client/admin/components/UserAdmin.js | 2 +- src/main/client/admin/components/UserSettings.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/client/admin/components/UserAdmin.js b/src/main/client/admin/components/UserAdmin.js index 79acf0462..d7b2258e7 100644 --- a/src/main/client/admin/components/UserAdmin.js +++ b/src/main/client/admin/components/UserAdmin.js @@ -76,7 +76,7 @@ export default class UserAdmin extends Component { User management {/* Do not show non-appAdmin users these application-level settings */} - {isApplicationAdmin && Organizations} + {!isModuleEnabled('enterprise') && isApplicationAdmin && Organizations} {isApplicationAdmin && Application logs} {/* Regions diff --git a/src/main/client/admin/components/UserSettings.js b/src/main/client/admin/components/UserSettings.js index 4caea9061..15924cbc7 100644 --- a/src/main/client/admin/components/UserSettings.js +++ b/src/main/client/admin/components/UserSettings.js @@ -234,8 +234,8 @@ export default class UserSettings extends Component {
    } {/* Organizations selector. Only show if there exist organizations already. */} - {!this.state.appAdminChecked && organizations && -
    + {!this.state.appAdminChecked && organizations && organizations.length + ?