From cf94429e059ad31ca56731224f95068feb99d404 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Tue, 29 Nov 2016 16:04:20 +0000 Subject: [PATCH 1/5] Add a `user_email` option to the nav "Account" dropdown. --- services/web/app/views/layout/navbar.jade | 3 +++ services/web/config/settings.defaults.coffee | 4 ++++ .../web/public/stylesheets/components/dropdowns.less | 9 ++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/services/web/app/views/layout/navbar.jade b/services/web/app/views/layout/navbar.jade index e0a89fdea7..3cd6587592 100644 --- a/services/web/app/views/layout/navbar.jade +++ b/services/web/app/views/layout/navbar.jade @@ -35,6 +35,9 @@ nav.navbar.navbar-default each child in item.dropdown if child.divider li.divider + else if child.user_email + li + div.subdued #{getUserEmail()} else li if child.url diff --git a/services/web/config/settings.defaults.coffee b/services/web/config/settings.defaults.coffee index 1e858253e4..e3b842a0d2 100644 --- a/services/web/config/settings.defaults.coffee +++ b/services/web/config/settings.defaults.coffee @@ -347,6 +347,10 @@ module.exports = settings = text: "Account" only_when_logged_in: true dropdown: [{ + user_email: true + },{ + divider: true + }, { text: "Account Settings" url: "/user/settings" }, { diff --git a/services/web/public/stylesheets/components/dropdowns.less b/services/web/public/stylesheets/components/dropdowns.less index 2b718026a1..7adbfe49d7 100755 --- a/services/web/public/stylesheets/components/dropdowns.less +++ b/services/web/public/stylesheets/components/dropdowns.less @@ -58,8 +58,8 @@ .nav-divider(@dropdown-divider-bg); } - // Links within the dropdown menu - > li > a { + // Links and other items within the dropdown menu + > li > a,div { display: block; padding: 3px 20px; clear: both; @@ -67,8 +67,11 @@ line-height: @line-height-base; color: @dropdown-link-color; white-space: nowrap; // prevent links from randomly breaking onto new lines + &.subdued { + color: #7a7a7a + } .subdued { - color: #7a7a7a + color: #7a7a7a } } } From eb648b9bc8c4f02e66693c7fa2a33c198b292c1c Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 29 Nov 2016 17:16:56 +0000 Subject: [PATCH 2/5] Proxy version number to and from docstore --- .../Features/Docstore/DocstoreManager.coffee | 7 ++++--- .../Documents/DocumentController.coffee | 7 ++++--- .../Project/ProjectEntityHandler.coffee | 6 +++--- .../coffee/Docstore/DocstoreManagerTests.coffee | 11 +++++++---- .../Documents/DocumentControllerTests.coffee | 10 ++++++---- .../Project/ProjectEntityHandlerTests.coffee | 17 +++++++++-------- 6 files changed, 33 insertions(+), 25 deletions(-) diff --git a/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee b/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee index a755c47422..772d927d78 100644 --- a/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee +++ b/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee @@ -30,7 +30,7 @@ module.exports = DocstoreManager = logger.error err: error, project_id: project_id, "error getting all docs from docstore" callback(error) - getDoc: (project_id, doc_id, options = {}, callback = (error, lines, rev) ->) -> + getDoc: (project_id, doc_id, options = {}, callback = (error, lines, rev, version) ->) -> if typeof(options) == "function" callback = options options = {} @@ -45,19 +45,20 @@ module.exports = DocstoreManager = return callback(error) if error? if 200 <= res.statusCode < 300 logger.log doc_id: doc_id, project_id: project_id, version: doc.version, rev: doc.rev, "got doc from docstore api" - callback(null, doc.lines, doc.rev) + callback(null, doc.lines, doc.rev, doc.version) else error = new Error("docstore api responded with non-success code: #{res.statusCode}") logger.error err: error, project_id: project_id, doc_id: doc_id, "error getting doc from docstore" callback(error) - updateDoc: (project_id, doc_id, lines, callback = (error, modified, rev) ->) -> + updateDoc: (project_id, doc_id, lines, version, callback = (error, modified, rev) ->) -> logger.log project_id: project_id, doc_id: doc_id, "updating doc in docstore api" url = "#{settings.apis.docstore.url}/project/#{project_id}/doc/#{doc_id}" request.post { url: url json: lines: lines + version: version }, (error, res, result) -> return callback(error) if error? if 200 <= res.statusCode < 300 diff --git a/services/web/app/coffee/Features/Documents/DocumentController.coffee b/services/web/app/coffee/Features/Documents/DocumentController.coffee index ba74fc47da..560f232ba1 100644 --- a/services/web/app/coffee/Features/Documents/DocumentController.coffee +++ b/services/web/app/coffee/Features/Documents/DocumentController.coffee @@ -7,7 +7,7 @@ module.exports = doc_id = req.params.doc_id plain = req?.query?.plain == 'true' logger.log doc_id:doc_id, project_id:project_id, "receiving get document request from api (docupdater)" - ProjectEntityHandler.getDoc project_id, doc_id, (error, lines, rev) -> + ProjectEntityHandler.getDoc project_id, doc_id, (error, lines, rev, version) -> if error? logger.err err:error, doc_id:doc_id, project_id:project_id, "error finding element for getDocument" return next(error) @@ -18,14 +18,15 @@ module.exports = res.type "json" res.send JSON.stringify { lines: lines + version: version } setDocument: (req, res, next = (error) ->) -> project_id = req.params.Project_id doc_id = req.params.doc_id - lines = req.body.lines + {lines, version} = req.body logger.log doc_id:doc_id, project_id:project_id, "receiving set document request from api (docupdater)" - ProjectEntityHandler.updateDocLines project_id, doc_id, lines, (error) -> + ProjectEntityHandler.updateDocLines project_id, doc_id, lines, version, (error) -> if error? logger.err err:error, doc_id:doc_id, project_id:project_id, "error finding element for getDocument" return next(error) diff --git a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee index ca783a2cbf..21932cefc9 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee @@ -126,7 +126,7 @@ module.exports = ProjectEntityHandler = doc = new Doc name: docName # Put doc in docstore first, so that if it errors, we don't have a doc_id in the project # which hasn't been created in docstore. - DocstoreManager.updateDoc project_id.toString(), doc._id.toString(), docLines, (err, modified, rev) -> + DocstoreManager.updateDoc project_id.toString(), doc._id.toString(), docLines, 0, (err, modified, rev) -> return callback(err) if err? ProjectEntityHandler._putElement project, folder_id, doc, "doc", (err, result)=> @@ -292,7 +292,7 @@ module.exports = ProjectEntityHandler = return callback(err) callback(err, folder, parentFolder_id) - updateDocLines : (project_id, doc_id, lines, callback = (error) ->)-> + updateDocLines : (project_id, doc_id, lines, version, callback = (error) ->)-> ProjectGetter.getProjectWithoutDocLines project_id, (err, project)-> return callback(err) if err? return callback(new Errors.NotFoundError("project not found")) if !project? @@ -307,7 +307,7 @@ module.exports = ProjectEntityHandler = return callback(error) logger.log project_id: project_id, doc_id: doc_id, "telling docstore manager to update doc" - DocstoreManager.updateDoc project_id, doc_id, lines, (err, modified, rev) -> + DocstoreManager.updateDoc project_id, doc_id, lines, version, (err, modified, rev) -> if err? logger.error err: err, doc_id: doc_id, project_id:project_id, lines: lines, "error sending doc to docstore" return callback(err) diff --git a/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee b/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee index f32d48fdc1..e288c46aea 100644 --- a/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee @@ -56,12 +56,13 @@ describe "DocstoreManager", -> beforeEach -> @lines = ["mock", "doc", "lines"] @rev = 5 + @version = 42 @modified = true describe "with a successful response code", -> beforeEach -> @request.post = sinon.stub().callsArgWith(1, null, statusCode: 204, { modified: @modified, rev: @rev }) - @DocstoreManager.updateDoc @project_id, @doc_id, @lines, @callback + @DocstoreManager.updateDoc @project_id, @doc_id, @lines, @version, @callback it "should update the doc in the docstore api", -> @request.post @@ -69,6 +70,7 @@ describe "DocstoreManager", -> url: "#{@settings.apis.docstore.url}/project/#{@project_id}/doc/#{@doc_id}" json: lines: @lines + version: @version }) .should.equal true @@ -78,7 +80,7 @@ describe "DocstoreManager", -> describe "with a failed response code", -> beforeEach -> @request.post = sinon.stub().callsArgWith(1, null, statusCode: 500, "") - @DocstoreManager.updateDoc @project_id, @doc_id, @lines, @callback + @DocstoreManager.updateDoc @project_id, @doc_id, @lines, @version, @callback it "should call the callback with an error", -> @callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true @@ -97,6 +99,7 @@ describe "DocstoreManager", -> @doc = lines: @lines = ["mock", "doc", "lines"] rev: @rev = 5 + version: @version = 42 describe "with a successful response code", -> beforeEach -> @@ -112,7 +115,7 @@ describe "DocstoreManager", -> .should.equal true it "should call the callback with the lines, version and rev", -> - @callback.calledWith(null, @lines, @rev).should.equal true + @callback.calledWith(null, @lines, @rev, @version).should.equal true describe "with a failed response code", -> beforeEach -> @@ -145,7 +148,7 @@ describe "DocstoreManager", -> .should.equal true it "should call the callback with the lines, version and rev", -> - @callback.calledWith(null, @lines, @rev).should.equal true + @callback.calledWith(null, @lines, @rev, @version).should.equal true describe "getAllDocs", -> describe "with a successful response code", -> diff --git a/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee b/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee index 1a7e2fb500..a554319baa 100644 --- a/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Documents/DocumentControllerTests.coffee @@ -33,7 +33,7 @@ describe "DocumentController", -> describe "when the document exists", -> beforeEach -> - @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @doc_lines, @rev) + @ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @doc_lines, @rev, @version) @DocumentController.getDocument(@req, @res, @next) it "should get the document from Mongo", -> @@ -45,6 +45,7 @@ describe "DocumentController", -> @res.type.should.equal "json" @res.body.should.equal JSON.stringify lines: @doc_lines + version: @version describe "when the document doesn't exist", -> beforeEach -> @@ -63,14 +64,15 @@ describe "DocumentController", -> describe "when the document exists", -> beforeEach -> - @ProjectEntityHandler.updateDocLines = sinon.stub().callsArg(3) + @ProjectEntityHandler.updateDocLines = sinon.stub().yields() @req.body = lines: @doc_lines + version: @version @DocumentController.setDocument(@req, @res, @next) it "should update the document in Mongo", -> @ProjectEntityHandler.updateDocLines - .calledWith(@project_id, @doc_id, @doc_lines) + .calledWith(@project_id, @doc_id, @doc_lines, @version) .should.equal true it "should return a successful response", -> @@ -78,7 +80,7 @@ describe "DocumentController", -> describe "when the document doesn't exist", -> beforeEach -> - @ProjectEntityHandler.updateDocLines = sinon.stub().callsArgWith(3, new Errors.NotFoundError("document does not exist")) + @ProjectEntityHandler.updateDocLines = sinon.stub().yields(new Errors.NotFoundError("document does not exist")) @req.body = lines: @doc_lines @DocumentController.setDocument(@req, @res, @next) diff --git a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee index e946850828..5a0c860ab2 100644 --- a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee +++ b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee @@ -402,7 +402,7 @@ describe 'ProjectEntityHandler', -> @ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, {path:{fileSystem:@path}}) @callback = sinon.stub() @tpdsUpdateSender.addDoc = sinon.stub().callsArg(1) - @DocstoreManager.updateDoc = sinon.stub().callsArgWith(3, null, true, 0) + @DocstoreManager.updateDoc = sinon.stub().yields(null, true, 0) @ProjectEntityHandler.addDoc project_id, folder_id, @name, @lines, @callback @@ -589,6 +589,7 @@ describe 'ProjectEntityHandler', -> @doc = { _id: doc_id } + @version = 42 @ProjectGetter.getProjectWithoutDocLines = sinon.stub().callsArgWith(1, null, @project) @projectLocator.findElement = sinon.stub().callsArgWith(1, null, @doc, {fileSystem: @path}) @tpdsUpdateSender.addDoc = sinon.stub().callsArg(1) @@ -597,8 +598,8 @@ describe 'ProjectEntityHandler', -> describe "when the doc has been modified", -> beforeEach -> - @DocstoreManager.updateDoc = sinon.stub().callsArgWith(3, null, true, @rev = 5) - @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @callback + @DocstoreManager.updateDoc = sinon.stub().yields(null, true, @rev = 5) + @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @version, @callback it "should get the project without doc lines", -> @ProjectGetter.getProjectWithoutDocLines @@ -616,7 +617,7 @@ describe 'ProjectEntityHandler', -> it "should update the doc in the docstore", -> @DocstoreManager.updateDoc - .calledWith(project_id, doc_id, @lines) + .calledWith(project_id, doc_id, @lines, @version) .should.equal true it "should mark the project as updated", -> @@ -640,8 +641,8 @@ describe 'ProjectEntityHandler', -> describe "when the doc has not been modified", -> beforeEach -> - @DocstoreManager.updateDoc = sinon.stub().callsArgWith(3, null, false, @rev = 5) - @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @callback + @DocstoreManager.updateDoc = sinon.stub().yields(null, false, @rev = 5) + @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @version, @callback it "should not mark the project as updated", -> @projectUpdater.markAsUpdated.called.should.equal false @@ -655,7 +656,7 @@ describe 'ProjectEntityHandler', -> describe "when the project is not found", -> beforeEach -> @ProjectGetter.getProjectWithoutDocLines = sinon.stub().callsArgWith(1, null, null) - @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @callback + @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @version, @callback it "should return a not found error", -> @callback.calledWith(new Errors.NotFoundError()).should.equal true @@ -663,7 +664,7 @@ describe 'ProjectEntityHandler', -> describe "when the doc is not found", -> beforeEach -> @projectLocator.findElement = sinon.stub().callsArgWith(1, null, null, null) - @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @callback + @ProjectEntityHandler.updateDocLines project_id, doc_id, @lines, @version, @callback it "should log out the error", -> @logger.error From d38890e9f43eaa3cc5fa530e12bdf1fa53a43457 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Wed, 30 Nov 2016 09:41:58 +0000 Subject: [PATCH 3/5] Add the `rolling` option to session --- services/web/app/coffee/infrastructure/Server.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/services/web/app/coffee/infrastructure/Server.coffee b/services/web/app/coffee/infrastructure/Server.coffee index 6ee9390b93..43683bdd4e 100644 --- a/services/web/app/coffee/infrastructure/Server.coffee +++ b/services/web/app/coffee/infrastructure/Server.coffee @@ -90,6 +90,7 @@ webRouter.use session secure: Settings.secureCookie store: sessionStore key: Settings.cookieName + rolling: true # passport webRouter.use passport.initialize() From ef115e2f91c004e1d26fa8768720179dcbf005a6 Mon Sep 17 00:00:00 2001 From: Shane Kilkelly Date: Wed, 30 Nov 2016 11:25:47 +0000 Subject: [PATCH 4/5] add a 2x version of the spellcheck-underline image. --- .../web/public/img/spellcheck-underline@2x.png | Bin 0 -> 1074 bytes services/web/public/stylesheets/app/editor.less | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 services/web/public/img/spellcheck-underline@2x.png diff --git a/services/web/public/img/spellcheck-underline@2x.png b/services/web/public/img/spellcheck-underline@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..957225b752ea0799932d3121274db2a5625044c5 GIT binary patch literal 1074 zcmZ{kv2W8r6vi(Cg%HRD10awrGq4b!oiv4<919wkCK4%82$HkaO)g+z`z+s8ait2W z-4F{iVgQMi3W_Gfo$WWGI3wzp-txi{Jk`PNZWl2$GqbNy| z9*)MSXWmP2-fPHD7z+@DlgUJxR233FhFZN|hiU~@DrN2{(`}#GQQ4>0r<2U)F)%#SF%noI11#`BrBdDjO+kviV7D&%o)#@_a7xjPQ-BJoPddu zDHa!Fx<;K>6m`hR8E>05!$w8bmdlz}uBv@aMVf|c%R0<><{8FxGsHF{VV@A!=sFR0 zJ<*k_Ne%SJqR7l%PWd#XgWNmW&KU!1tF$X_-lzjt!4c^Oo_WCrhnu+%%~(t zM$S@$`M7aw#-zn8U@ZdDVhRwS0mXH)Ovl51oXc$;F4VJJlei>&7}!HB$`vCiDeI1- z4CYam(os@+!s$@# Date: Wed, 30 Nov 2016 14:34:50 +0000 Subject: [PATCH 5/5] Update to modern images --- .../web/public/img/spellcheck-underline.png | Bin 111 -> 2872 bytes .../public/img/spellcheck-underline@2x.png | Bin 1074 -> 3003 bytes .../web/public/stylesheets/app/editor.less | 1 + 3 files changed, 1 insertion(+) diff --git a/services/web/public/img/spellcheck-underline.png b/services/web/public/img/spellcheck-underline.png index 07d7c5ce0e0a1078c644422400f343cb0e73f45e..0b3b38904afcc2e7b2599cb6e63196db2696ebbe 100644 GIT binary patch literal 2872 zcmV-83&-?{P)EX>4Tx07!|QmUmQB*%pV-y*Is3k`RiN&}(Q?0!R(L zNRcioF$oY#z>okUHbhi#L{X8Z2r?+(fTKf^u_B6v0a3B*1Q|rsac~qHmPur-8Q;8l z@6DUvANPK1pS{oBXYYO1x&V;;g9XA&SP6g(p;#2*=f#MPi)Ua50Sxc}18e}`aI>>Q z7WhU2nF4&+jBJ?`_!qsp4j}paD$_rV!2tiCl(|_VF#u4QjOX(B*<2YH$v8b%oF%tU z$(Xh@P0lb%&LUZYGFFpw@+@0?_L*f5IrB1vJQ>S#&f;b8cV}o=_hCs$|GJ-ARc>v%@$zSl&FIdda6Uz_9 z&dgda5+tXH875p)hK-XGi{a1DP3Mcn%rFi&jU(bQ*qIqw9N}^RX3zXt6nSkKvLZX! zI5{{lZ7prSDAa#l{F{>Zc9vd*f9@GXANa%eSALld0I;TIwb}ZIZD|z%UF!i*yZwjF zU@riQvc7c=eQ_STd|pz-;w)z?tK8gNO97v2DKF^n`kxMeLtlK)Qoh~qM8wF>;&Ay4=AVc79|!(*9u^V&B)*6*lto0#rc5AAmbF{R6Nm+wLWV&2 zpPKj&!~Ue%xt59A_z}>SSOTRX8bE#?04OREAPIY9E70$K3&uwS`OS;bnV6mX&w~Da zSGY|6$QC4jj$=neGPn{^&g`1}S^_j607XCp>OdRl0~5dmw!jg%01w~;0zoK<1aV+7 z;DQv80Yo4d6o9p$7?gsoU?->sb)XS6gEnv&bb({wG&lz?fy-b7+yPQB4xWH1@CwX8 z5QK%u5EW8~bRa{>9I}O2kQ?L!1w#=~9FzzpLqbRb6+r8tQm7oNhU%ea=v(M0bQ-z< z4MVq}QD_qS6?z9FFbSr?TCfpp1+!pJI0%k}7s1K!GB_VDg15kxa07f0?u1Xnm*5dt z3O|9T5r7a8I--j(5f;KmLXmhR2@xTykP@TC$XgT!MMW`COq2`C z9~Fh-qL!gnp*EwcQ3p_+s6NzH)F^5S^$|@*Yog83&gcMiEIJvTi!Mf2pqtPg=(Fe% z^f>wz27{qvj4_TFe@q-E6|(}f8M7PHjyZ)H#*AU6u~@7+)*S1K4aIV>Vr((C3VRTH z5_<(Zj(vk8;&gDfIA2^mPKYbSRp451CvaDA6Sx_?65bH+j1R^0@XPUK_(psWeh5E~ zpCKp{j0vuUNJ1)MEuoUoMmS5jOL##f67`5q#Bid3xQ19sJVZQC93{RbQAlPaHYtH5 zA#EY;C!HeQBE2A!$wp)kay(f~-a>9BpCR8TzfqtnSSkc4@Dx@n)F^Z+Tv2$Yh*vaJ z^i*7|n6Fr&ctmkX@u?DC$w-N<#8FzMRHJlM>4ws@GF90|IaE1Ad9!kh@&)Bb6fDJv z;zQw4iYWUiXDDM-gsM+vQ@PZ2)JE!A>NpKUGo}U5QfZ~MZ)k(GDHV!}ol3Myo=T0% zaTO^Yp&QWy=;`z_`eFKY`a4xERZmsE>L%4T)hnv6)#j*qsPWZG)Y{cX)ZVEx)P2;` z)VHa3so&E;X_#q*YvgL|(KxH|bPjEf%N*{Uk~xRx+}4CO%`_u4S7`3j9MGKB($@0R z%F?RRI-~Veo38DlovOV<`-JwS4pqlZN1(Gq=cLYKh6=-zkLZ@rEqJ6vJJH{f4iNjE!Q9 zHW+moJu+4^4lvF)ZZ*DZLN;+XS!U8;a?KQD$}&we-EDf=3^ubjOEIf48#0H@9n1yh zyUm9!&=yV>LW>5A8%z?@lbOS8WsX|XErTr!ExRnASs7TxTWz!IxB6&pZ=G)4Xnn_q zViRanXwzf!tF4(W*S5y?+FbHn-?^*jcF%ooXKu&0+hcdro@yUrzrnuO{)2;~gUF%H zVbamSG10Ns@dk^=3S(_%op(Yzc{#0iI_C7&*}+-teAxLH7p6;^ON+~+dB*ej^BU)k zx$3!cTZVb0Xx4mvscU^amdxQG}4}A}wN0Y~dr>SSE=RwbBUe;bBuMV%*Y-jdL z_9<_~+t0hid(emC6XjFwbKh6bH`%w{0a^jvfaZXyK*zw9 zfqg-wpantIK@Wn>fV8I z2F~=-fTgudr?_nHF76Ya2X6;&lJCkd=T9WLCY2{WN_I`&o;;c2o>GzWRKONg3!bO? zr`DyuP76)jpY|y|CcQlamywupR7eq~3Hvg&GxIWsv&^%Kv!u(Mm+f3OB?=NXWkcDE zvb)7J+0WE~#6+@QGMeL-QhTd=lZ zbfxFY`c=@XrK@^Z>#r_aJ-)_o&4IOqwP|aAD6}ptFMPQ!W?fH_R?(WGvGsoITZV0)e z^+=6ZO?$0o?WWq-yLr2>?D5#sR;N{0TK8_RVDHU(zxvJwqlSuon0-0>9yUfd_J7U# zy17ZCskG_Ce&K%UfrtZr&5q5@Et)N5t#GTPb@E`s!OP!xf79K@Y^!glx0fCQha`s{ zf1CL2^}|7jdylY=w0&pzU2O-oqofn+T;4g=mC_~cj_V#i8hEs~$EBy^d&}?lAJaWn zb6n+k*$Kjlq7$D^=AWECm38Xr>EzR6y-RxUoQXYituMT9@NCf8^XGieo$2@NKY8Bu z{ILtp7mi+JUF^E#aH(^^exTzA`yV<69R@px9EZ9uJ6-M>o;Q5riu;w*SG}*EyB2Wm z(#ZUg;pqt>?FMZqM9Va~FNLGD$lbNT*KP&%S`^@Co zcfWZ2GB6c8HU3=m{L`|I+Sd?{wJo{Z|>UW?q-PQGavbE$eOnyO?(qGr8}v z?<+r;e(3oa^zrVej8C6_1NVgU`*8t=>i_@%AY({UO#lFTCIA3{ga82g0001h=l}q9 zFaQARU;qF*m;eA5aGbhPJOBUy24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV z0GgZ_00007bV*G`2jB}H4i_TF`$*>i002x$L_t&t*JE7upMjZyfq@~^fsfq`N5 z35Kf-tNt@E|IuTJ`)|(h42ty_|L8HqF>^9>FfcH9{Qb^gz|6_e!2pw8^`C(mrV9Wp Wh#LD*fGueN0000~aQWCrB(dAc};a6~63m?RaXurM%&F_h((EX>4Tx07!|QmUmQB*%pV-y*Is3k`RiN&}(Q?0!R(L zNRcioF$oY#z>okUHbhi#L{X8Z2r?+(fTKf^u_B6v0a3B*1Q|rsac~qHmPur-8Q;8l z@6DUvANPK1pS{oBXYYO1x&V;;g9XA&SP6g(p;#2*=f#MPi)Ua50Sxc}18e}`aI>>Q z7WhU2nF4&+jBJ?`_!qsp4j}paD$_rV!2tiCl(|_VF#u4QjOX(B*<2YH$v8b%oF%tU z$(Xh@P0lb%&LUZYGFFpw@+@0?_L*f5IrB1vJQ>S#&f;b8cV}o=_hCs$|GJ-ARc>v%@$zSl&FIdda6Uz_9 z&dgda5+tXH875p)hK-XGi{a1DP3Mcn%rFi&jU(bQ*qIqw9N}^RX3zXt6nSkKvLZX! zI5{{lZ7prSDAa#l{F{>Zc9vd*f9@GXANa%eSALld0I;TIwb}ZIZD|z%UF!i*yZwjF zU@riQvc7c=eQ_STd|pz-;w)z?tK8gNO97v2DKF^n`kxMeLtlK)Qoh~qM8wF>;&Ay4=AVc79|!(*9u^V&B)*6*lto0#rc5AAmbF{R6Nm+wLWV&2 zpPKj&!~Ue%xt59A_z}>SSOTRX8bE#?04OREAPIY9E70$K3&uwS`OS;bnV6mX&w~Da zSGY|6$QC4jj$=neGPn{^&g`1}S^_j607XCp>OdRl0~5dmw!jg%01w~;0zoK<1aV+7 z;DQv80Yo4d6o9p$7?gsoU?->sb)XS6gEnv&bb({wG&lz?fy-b7+yPQB4xWH1@CwX8 z5QK%u5EW8~bRa{>9I}O2kQ?L!1w#=~9FzzpLqbRb6+r8tQm7oNhU%ea=v(M0bQ-z< z4MVq}QD_qS6?z9FFbSr?TCfpp1+!pJI0%k}7s1K!GB_VDg15kxa07f0?u1Xnm*5dt z3O|9T5r7a8I--j(5f;KmLXmhR2@xTykP@TC$XgT!MMW`COq2`C z9~Fh-qL!gnp*EwcQ3p_+s6NzH)F^5S^$|@*Yog83&gcMiEIJvTi!Mf2pqtPg=(Fe% z^f>wz27{qvj4_TFe@q-E6|(}f8M7PHjyZ)H#*AU6u~@7+)*S1K4aIV>Vr((C3VRTH z5_<(Zj(vk8;&gDfIA2^mPKYbSRp451CvaDA6Sx_?65bH+j1R^0@XPUK_(psWeh5E~ zpCKp{j0vuUNJ1)MEuoUoMmS5jOL##f67`5q#Bid3xQ19sJVZQC93{RbQAlPaHYtH5 zA#EY;C!HeQBE2A!$wp)kay(f~-a>9BpCR8TzfqtnSSkc4@Dx@n)F^Z+Tv2$Yh*vaJ z^i*7|n6Fr&ctmkX@u?DC$w-N<#8FzMRHJlM>4ws@GF90|IaE1Ad9!kh@&)Bb6fDJv z;zQw4iYWUiXDDM-gsM+vQ@PZ2)JE!A>NpKUGo}U5QfZ~MZ)k(GDHV!}ol3Myo=T0% zaTO^Yp&QWy=;`z_`eFKY`a4xERZmsE>L%4T)hnv6)#j*qsPWZG)Y{cX)ZVEx)P2;` z)VHa3so&E;X_#q*YvgL|(KxH|bPjEf%N*{Uk~xRx+}4CO%`_u4S7`3j9MGKB($@0R z%F?RRI-~Veo38DlovOV<`-JwS4pqlZN1(Gq=cLYKh6=-zkLZ@rEqJ6vJJH{f4iNjE!Q9 zHW+moJu+4^4lvF)ZZ*DZLN;+XS!U8;a?KQD$}&we-EDf=3^ubjOEIf48#0H@9n1yh zyUm9!&=yV>LW>5A8%z?@lbOS8WsX|XErTr!ExRnASs7TxTWz!IxB6&pZ=G)4Xnn_q zViRanXwzf!tF4(W*S5y?+FbHn-?^*jcF%ooXKu&0+hcdro@yUrzrnuO{)2;~gUF%H zVbamSG10Ns@dk^=3S(_%op(Yzc{#0iI_C7&*}+-teAxLH7p6;^ON+~+dB*ej^BU)k zx$3!cTZVb0Xx4mvscU^amdxQG}4}A}wN0Y~dr>SSE=RwbBUe;bBuMV%*Y-jdL z_9<_~+t0hid(emC6XjFwbKh6bH`%w{0a^jvfaZXyK*zw9 zfqg-wpantIK@Wn>fV8I z2F~=-fTgudr?_nHF76Ya2X6;&lJCkd=T9WLCY2{WN_I`&o;;c2o>GzWRKONg3!bO? zr`DyuP76)jpY|y|CcQlamywupR7eq~3Hvg&GxIWsv&^%Kv!u(Mm+f3OB?=NXWkcDE zvb)7J+0WE~#6+@QGMeL-QhTd=lZ zbfxFY`c=@XrK@^Z>#r_aJ-)_o&4IOqwP|aAD6}ptFMPQ!W?fH_R?(WGvGsoITZV0)e z^+=6ZO?$0o?WWq-yLr2>?D5#sR;N{0TK8_RVDHU(zxvJwqlSuon0-0>9yUfd_J7U# zy17ZCskG_Ce&K%UfrtZr&5q5@Et)N5t#GTPb@E`s!OP!xf79K@Y^!glx0fCQha`s{ zf1CL2^}|7jdylY=w0&pzU2O-oqofn+T;4g=mC_~cj_V#i8hEs~$EBy^d&}?lAJaWn zb6n+k*$Kjlq7$D^=AWECm38Xr>EzR6y-RxUoQXYituMT9@NCf8^XGieo$2@NKY8Bu z{ILtp7mi+JUF^E#aH(^^exTzA`yV<69R@px9EZ9uJ6-M>o;Q5riu;w*SG}*EyB2Wm z(#ZUg;pqt>?FMZqM9Va~FNLGD$lbNT*KP&%S`^@Co zcfWZ2GB6c8HU3=m{L`|I+Sd?{wJo{Z|>UW?q-PQGavbE$eOnyO?(qGr8}v z?<+r;e(3oa^zrVej8C6_1NVgU`*8t=>i_@%AY({UO#lFTCIA3{ga82g0001h=l}q9 zFaQARU;qF*m;eA5aGbhPJOBUy24YJ`L;wH)0002_L%V+f000SaNLh0L02Ul@Ott%IPy3mez2mdJk(vHe5k$WqjX!C}p(>7T z#?dbY(YWkK+3RJk6T7DN6)J_MsRvcm26>ufWG36OXQEQ-Dy>K#YGdh57#ZYgWXFz) z18Gfq#*5O}+@jDm_l5I@Z(9nh(nJ`U8)|y@WnHbMw5nDWLeI>rbYa1byS#qe@NB`8 xbY<#Msnw8Pach`ozn9A=`H6e&Si(%2{{VbGQOOeo2H*ey002ovPDHLkV1kx>sS*GH literal 1074 zcmZ{kv2W8r6vi(Cg%HRD10awrGq4b!oiv4<919wkCK4%82$HkaO)g+z`z+s8ait2W z-4F{iVgQMi3W_Gfo$WWGI3wzp-txi{Jk`PNZWl2$GqbNy| z9*)MSXWmP2-fPHD7z+@DlgUJxR233FhFZN|hiU~@DrN2{(`}#GQQ4>0r<2U)F)%#SF%noI11#`BrBdDjO+kviV7D&%o)#@_a7xjPQ-BJoPddu zDHa!Fx<;K>6m`hR8E>05!$w8bmdlz}uBv@aMVf|c%R0<><{8FxGsHF{VV@A!=sFR0 zJ<*k_Ne%SJqR7l%PWd#XgWNmW&KU!1tF$X_-lzjt!4c^Oo_WCrhnu+%%~(t zM$S@$`M7aw#-zn8U@ZdDVhRwS0mXH)Ovl51oXc$;F4VJJlei>&7}!HB$`vCiDeI1- z4CYam(os@+!s$@#