1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 17:33:14 +01:00

Merge branch 'AD-1313-Attach-thumbnail-to-piece-in-register-form' into AD-1360-show-common-upload-errors-to-user

This commit is contained in:
Brett Sun 2015-11-25 10:18:40 +01:00
commit 4b91b58adf
78 changed files with 969 additions and 1199 deletions

View File

@ -1,63 +0,0 @@
# Feature list
This list specifies all features in Onion that ought to be tested before actually pushing live:
- sign up & email activation
- login
- log out
- form input
+ reset
+ save
+ disabled state
- form checkbox
+ reset
+ save
+ disabled state
- create app
+ refresh token
- loan contract
+ upload
+ download
+ delete
- register work
+ with edition
+ without edition
+ correct encoding of video upload
- fineuploader
+ upload file
+ upload multiple files
+ delete file
+ cancel upload of file
- create editions
+ in piece list
+ in piece detail
- all notes in edition/piece detail
- transfer & consign & loan & share & delete
+ bulk
+ single
+ withdraw
- piece list
+ filter (also check for correct filtering of opened edition tables)
+ order
+ search
+ pagination
+ expandable edition list for piece
- download coa
## sluice
- hero landing page
- activation email
- submission (also check extra form fields)
+ of existing pieces
+ newly registered pieces
- rating
+ in piece list
+ in piece detail
- short listing (not yet implemented)
- piece list
+ order by rating
## Cyland
- hero landing page
- activation email
- submission (check states of submission (1,2,3))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,7 @@
<svg xmlns="http://www.w3.org/2000/svg"> <svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata> <metadata>Generated by IcoMoon</metadata>
<defs> <defs>
<font id="ascribe-logo" horiz-adv-x="1024"> <font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" /> <font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" /> <missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" /> <glyph unicode="&#x20;" horiz-adv-x="512" d="" />
@ -12,8 +12,9 @@
<glyph unicode="&#xe802;" glyph-name="search" d="M1021.1-36.711l-305.914 305.583c67.615 73.406 108.95 171.391 108.95 279.060 0 227.579-184.49 412.068-412.068 412.068s-412.068-184.489-412.068-412.068c0-227.578 184.489-412.068 412.068-412.068 107.625 0 205.576 41.3 278.972 108.866l305.927-305.597 24.133 24.156zM412.068 169.997c-208.394 0-377.935 169.541-377.935 377.935s169.541 377.935 377.935 377.935 377.935-169.54 377.935-377.935c0-208.394-169.541-377.935-377.935-377.935z" /> <glyph unicode="&#xe802;" glyph-name="search" d="M1021.1-36.711l-305.914 305.583c67.615 73.406 108.95 171.391 108.95 279.060 0 227.579-184.49 412.068-412.068 412.068s-412.068-184.489-412.068-412.068c0-227.578 184.489-412.068 412.068-412.068 107.625 0 205.576 41.3 278.972 108.866l305.927-305.597 24.133 24.156zM412.068 169.997c-208.394 0-377.935 169.541-377.935 377.935s169.541 377.935 377.935 377.935 377.935-169.54 377.935-377.935c0-208.394-169.541-377.935-377.935-377.935z" />
<glyph unicode="&#xe803;" glyph-name="filter" d="M0 960l384.89-534.756 8.722-489.244 184.119 174.521 0.324 314.724 445.947 534.756h-1024zM551.839 447.106c-5.109-6.126-7.91-13.849-7.919-21.826l-0.308-300.068-117.253-111.141-7.341 411.782c-0.124 6.948-2.365 13.692-6.424 19.331l-345.97 480.683h884.467l-399.251-478.761z" /> <glyph unicode="&#xe803;" glyph-name="filter" d="M0 960l384.89-534.756 8.722-489.244 184.119 174.521 0.324 314.724 445.947 534.756h-1024zM551.839 447.106c-5.109-6.126-7.91-13.849-7.919-21.826l-0.308-300.068-117.253-111.141-7.341 411.782c-0.124 6.948-2.365 13.692-6.424 19.331l-345.97 480.683h884.467l-399.251-478.761z" />
<glyph unicode="&#xe804;" glyph-name="add-white" d="M510.103 923.822c263.415 0 477.719-214.304 477.719-477.719s-214.303-477.718-477.719-477.718c-263.415 0-477.718 214.304-477.718 477.718s214.303 477.719 477.718 477.719zM510.103 957.955c-282.688 0-511.851-229.164-511.851-511.852s229.163-511.851 511.851-511.851 511.852 229.164 511.852 511.851-229.164 511.852-511.852 511.852v0zM796.444 459.378h-273.067v273.067h-34.133v-273.067h-261.689v-34.133h261.689v-261.689h34.133v261.689h273.067z" /> <glyph unicode="&#xe804;" glyph-name="add-white" d="M510.103 923.822c263.415 0 477.719-214.304 477.719-477.719s-214.303-477.718-477.719-477.718c-263.415 0-477.718 214.304-477.718 477.718s214.303 477.719 477.718 477.719zM510.103 957.955c-282.688 0-511.851-229.164-511.851-511.852s229.163-511.851 511.851-511.851 511.852 229.164 511.852 511.851-229.164 511.852-511.852 511.852v0zM796.444 459.378h-273.067v273.067h-34.133v-273.067h-261.689v-34.133h261.689v-261.689h34.133v261.689h273.067z" />
<glyph unicode="&#xe805;" d="M512.148 959.852c-282.688 0-511.851-229.164-511.851-511.852s229.164-511.851 511.851-511.851 511.852 229.163 511.852 511.851-229.164 511.852-511.852 511.852z" /> <glyph unicode="&#xe805;" glyph-name="uniE805" d="M512.148 959.852c-282.688 0-511.851-229.164-511.851-511.852s229.164-511.851 511.851-511.851 511.852 229.163 511.852 511.851-229.164 511.852-511.852 511.852z" />
<glyph unicode="&#xe806;" d="M796.444 459.378h-273.067v273.067h-34.133v-273.067h-261.689v-34.133h261.689v-261.689h34.133v261.689h273.067z" /> <glyph unicode="&#xe806;" glyph-name="uniE806" d="M796.444 459.378h-273.067v273.067h-34.133v-273.067h-261.689v-34.133h261.689v-261.689h34.133v261.689h273.067z" />
<glyph unicode="&#xe807;" glyph-name="icon" d="M550.306 782.458h-75.373l-249.184-613.64h90.453l62.951 159.627h262.477l62.974-159.627h95.755l-250.053 613.64zM403.098 400.255l107.305 274.897 107.28-274.897h-214.586zM1024 448c0 286.204-225.796 512-511.999 512s-512.001-225.796-512.001-512c0-286.204 225.797-512 512.001-512s511.999 225.797 511.999 512v0zM962.165 448c0-245.94-204.249-450.164-450.164-450.164-245.941 0-450.161 204.224-450.161 450.164s204.221 450.164 450.161 450.164c245.915 0 450.164-204.224 450.164-450.164v0z" /> <glyph unicode="&#xe807;" glyph-name="icon" d="M550.306 782.458h-75.373l-249.184-613.64h90.453l62.951 159.627h262.477l62.974-159.627h95.755l-250.053 613.64zM403.098 400.255l107.305 274.897 107.28-274.897h-214.586zM1024 448c0 286.204-225.796 512-511.999 512s-512.001-225.796-512.001-512c0-286.204 225.797-512 512.001-512s511.999 225.797 511.999 512v0zM962.165 448c0-245.94-204.249-450.164-450.164-450.164-245.941 0-450.161 204.224-450.161 450.164s204.221 450.164 450.161 450.164c245.915 0 450.164-204.224 450.164-450.164v0z" />
<glyph unicode="&#xe808;" glyph-name="logo" horiz-adv-x="4195" d="M499.718 326.19c0 109.528-24.641 157.448-61.607 198.517-38.336 41.077-95.832 71.191-191.673 71.191-95.832 0-171.135-36.957-212.212-64.34l27.382-52.031c13.695 10.954 88.998 54.764 187.571 54.764 99.943 0 177.978-57.505 177.978-173.876v-34.225l-173.876-6.843c-171.135-6.852-253.281-82.146-253.281-191.673s88.989-191.674 212.212-191.674c123.214 0 191.674 75.294 214.944 102.676v-88.989h72.562v376.503zM427.156 113.978c-30.114-47.92-98.573-116.371-201.258-116.371-102.676 0-154.707 61.607-154.707 130.066 0 68.45 42.448 125.955 175.246 132.798l180.719 10.955v-157.448zM1063.784 123.562c0 120.482-119.12 161.551-198.525 188.933-78.035 27.382-146.494 56.134-146.494 121.853s50.661 101.314 121.853 101.314c71.191 0 115.001-24.641 158.819-72.561l43.809 43.809c-49.291 56.134-106.795 88.989-199.887 88.989-93.1 0-193.044-54.764-193.044-169.765s119.112-156.078 173.876-175.246c54.764-19.168 168.394-47.92 168.394-130.066s-69.821-120.482-149.226-120.482c-79.413 0-142.391 39.707-183.46 99.952l-50.661-41.077c39.707-65.718 113.639-123.215 231.38-123.215s223.166 68.451 223.166 187.562v0zM1679.873 93.44c0 0-68.451-93.1-212.212-93.1-143.753 0-242.326 113.639-242.326 271.087s112.268 260.132 239.594 260.132c125.955 0 180.719-61.616 208.101-93.1l45.18 47.912c-13.687 17.798-82.146 109.528-250.548 109.528-167.024 0-317.628-132.798-317.628-328.583 0-194.406 139.65-331.315 310.785-331.315s236.853 82.146 260.133 109.528l-41.077 47.912zM2142.607 586.323c0 0-27.382 9.576-68.45 9.576-68.459 0-138.28-43.81-161.551-130.058v119.112h-71.2v-635.266h71.2v342.278c0 109.528 34.225 162.921 45.18 177.978 10.946 15.066 47.912 57.505 109.528 57.505 34.225 0 53.394-5.473 68.451-10.955l6.843 69.83zM2353.432 810.851c0 32.855-26.012 58.875-58.867 58.875-32.863 0-58.875-26.020-58.875-58.875s26.011-58.875 58.875-58.875c32.855 0 58.867 26.020 58.867 58.875v0zM2330.161 584.953h-71.191v-635.266h71.191v635.266zM3144.767 267.315c0 188.941-123.223 328.583-312.155 328.583-91.73 0-177.987-36.957-239.594-132.798v431.267h-71.191v-944.68h71.191v120.482c53.394-93.1 141.012-134.169 236.853-134.169 191.673 0 314.896 143.753 314.896 331.315v0zM3069.464 267.315c0-146.494-86.257-266.976-239.594-266.976-154.707 0-239.594 120.482-239.594 266.976 0 147.864 84.887 266.976 239.594 266.976 153.337 0 239.594-119.112 239.594-266.976v0zM3836.158 298.808c0 171.135-120.482 297.090-287.514 297.090-168.402 0-308.044-132.798-308.044-328.583 0-194.406 119.112-331.315 303.933-331.315 184.83 0 264.244 95.833 264.244 95.833l-34.234 50.661c0 0-79.405-82.154-223.158-82.154-143.761 0-228.64 99.952-235.491 250.548h516.154c0 0 4.111 27.382 4.111 47.92v0zM3318.633 312.495c4.111 88.998 68.459 221.796 225.899 221.796 157.449 0 219.065-139.65 219.065-221.796h-444.963zM4027.755 897.194h-26.663l-88.15-217.077h31.998l22.269 56.468h92.852l22.277-56.468h33.874l-88.457 217.077zM3975.68 761.989l37.96 97.245 37.951-97.245h-75.91zM4195.326 778.878c0 101.246-79.876 181.122-181.121 181.122s-181.122-79.876-181.122-181.122c0-101.245 79.876-181.122 181.122-181.122s181.121 79.876 181.121 181.122v0zM4173.452 778.878c0-87.002-72.254-159.247-159.247-159.247-87.002 0-159.246 72.245-159.246 159.247s72.244 159.247 159.246 159.247c86.993 0 159.247-72.245 159.247-159.247v0z" /> <glyph unicode="&#xe808;" glyph-name="logo" horiz-adv-x="4195" d="M499.718 326.19c0 109.528-24.641 157.448-61.607 198.517-38.336 41.077-95.832 71.191-191.673 71.191-95.832 0-171.135-36.957-212.212-64.34l27.382-52.031c13.695 10.954 88.998 54.764 187.571 54.764 99.943 0 177.978-57.505 177.978-173.876v-34.225l-173.876-6.843c-171.135-6.852-253.281-82.146-253.281-191.673s88.989-191.674 212.212-191.674c123.214 0 191.674 75.294 214.944 102.676v-88.989h72.562v376.503zM427.156 113.978c-30.114-47.92-98.573-116.371-201.258-116.371-102.676 0-154.707 61.607-154.707 130.066 0 68.45 42.448 125.955 175.246 132.798l180.719 10.955v-157.448zM1063.784 123.562c0 120.482-119.12 161.551-198.525 188.933-78.035 27.382-146.494 56.134-146.494 121.853s50.661 101.314 121.853 101.314c71.191 0 115.001-24.641 158.819-72.561l43.809 43.809c-49.291 56.134-106.795 88.989-199.887 88.989-93.1 0-193.044-54.764-193.044-169.765s119.112-156.078 173.876-175.246c54.764-19.168 168.394-47.92 168.394-130.066s-69.821-120.482-149.226-120.482c-79.413 0-142.391 39.707-183.46 99.952l-50.661-41.077c39.707-65.718 113.639-123.215 231.38-123.215s223.166 68.451 223.166 187.562v0zM1679.873 93.44c0 0-68.451-93.1-212.212-93.1-143.753 0-242.326 113.639-242.326 271.087s112.268 260.132 239.594 260.132c125.955 0 180.719-61.616 208.101-93.1l45.18 47.912c-13.687 17.798-82.146 109.528-250.548 109.528-167.024 0-317.628-132.798-317.628-328.583 0-194.406 139.65-331.315 310.785-331.315s236.853 82.146 260.133 109.528l-41.077 47.912zM2142.607 586.323c0 0-27.382 9.576-68.45 9.576-68.459 0-138.28-43.81-161.551-130.058v119.112h-71.2v-635.266h71.2v342.278c0 109.528 34.225 162.921 45.18 177.978 10.946 15.066 47.912 57.505 109.528 57.505 34.225 0 53.394-5.473 68.451-10.955l6.843 69.83zM2353.432 810.851c0 32.855-26.012 58.875-58.867 58.875-32.863 0-58.875-26.020-58.875-58.875s26.011-58.875 58.875-58.875c32.855 0 58.867 26.020 58.867 58.875v0zM2330.161 584.953h-71.191v-635.266h71.191v635.266zM3144.767 267.315c0 188.941-123.223 328.583-312.155 328.583-91.73 0-177.987-36.957-239.594-132.798v431.267h-71.191v-944.68h71.191v120.482c53.394-93.1 141.012-134.169 236.853-134.169 191.673 0 314.896 143.753 314.896 331.315v0zM3069.464 267.315c0-146.494-86.257-266.976-239.594-266.976-154.707 0-239.594 120.482-239.594 266.976 0 147.864 84.887 266.976 239.594 266.976 153.337 0 239.594-119.112 239.594-266.976v0zM3836.158 298.808c0 171.135-120.482 297.090-287.514 297.090-168.402 0-308.044-132.798-308.044-328.583 0-194.406 119.112-331.315 303.933-331.315 184.83 0 264.244 95.833 264.244 95.833l-34.234 50.661c0 0-79.405-82.154-223.158-82.154-143.761 0-228.64 99.952-235.491 250.548h516.154c0 0 4.111 27.382 4.111 47.92v0zM3318.633 312.495c4.111 88.998 68.459 221.796 225.899 221.796 157.449 0 219.065-139.65 219.065-221.796h-444.963zM4027.755 897.194h-26.663l-88.15-217.077h31.998l22.269 56.468h92.852l22.277-56.468h33.874l-88.457 217.077zM3975.68 761.989l37.96 97.245 37.951-97.245h-75.91zM4195.326 778.878c0 101.246-79.876 181.122-181.121 181.122s-181.122-79.876-181.122-181.122c0-101.245 79.876-181.122 181.122-181.122s181.121 79.876 181.121 181.122v0zM4173.452 778.878c0-87.002-72.254-159.247-159.247-159.247-87.002 0-159.246 72.245-159.246 159.247s72.244 159.247 159.246 159.247c86.993 0 159.247-72.245 159.247-159.247v0z" />
<glyph unicode="&#xe809;" glyph-name="ok" horiz-adv-x="1348" d="M436.706-64l-406.588 399.059 112.941 105.412 293.647-293.647 768 760.471 105.412-105.412z" />
</font></defs></svg> </font></defs></svg>

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Binary file not shown.

View File

@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="595.28px" height="419.53px" viewBox="0 0 595.28 419.53" enable-background="new 0 0 595.28 419.53" xml:space="preserve">
<g>
<defs>
<rect id="SVGID_1_" x="6.973" y="169.602" width="580.536" height="116.927"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" overflow="visible"/>
</clipPath>
<path clip-path="url(#SVGID_2_)" d="M415.848,234.446c1.263-7.152,7.572-14.584,17.247-14.584c9.114,0,15.564,6.59,15.705,14.584
H415.848z M448.519,258.002c-2.664,5.891-8.272,8.833-15.144,8.833c-10.797,0-18.509-8.833-18.368-20.47h42.767
c3.085,0,5.188-1.823,5.188-5.189v-4.486c-0.42-15.986-12.198-29.167-29.727-29.167c-19.91,0-32.11,15.986-32.11,35.476
c0,20.894,11.639,36.598,32.251,36.598c13.461,0,24.258-7.151,28.745-18.931c1.263-3.364-0.562-6.17-3.926-6.17h-4.207
C451.604,254.496,449.781,255.338,448.519,258.002 M340.692,229.818c3.225-4.627,8.833-9.255,15.984-9.255
c10.798,0,17.809,10.656,17.809,22.996c0,12.901-6.45,22.855-17.809,22.855c-7.151,0-12.76-5.188-15.984-10.096V229.818z
M340.552,272.585c4.628,4.067,10.096,7.012,16.266,7.012c19.49,0.14,31.409-15.565,31.55-36.178
c0.141-20.472-11.779-35.896-31.41-35.896c-6.31,0-12.059,2.945-16.265,6.311v-32.251c0-2.804-2.243-5.048-5.188-5.048h-3.645
c-2.806,0-4.909,2.244-4.909,5.048v91.843c0,2.806,2.104,5.048,4.909,5.048h3.505c2.944,0,4.627-2.104,5.048-4.907L340.552,272.585
z M285.587,187.612c0,5.329,3.926,9.114,9.115,9.114c5.188,0,9.255-3.785,9.255-9.114c0-5.328-4.067-9.254-9.255-9.254
C289.513,178.358,285.587,182.284,285.587,187.612 M287.971,273.426c0,2.806,2.104,5.048,4.908,5.048h3.505
c2.804,0,5.048-2.242,5.048-5.048v-59.733c0-2.804-1.823-5.047-4.908-5.047h-3.646c-2.804,0-4.908,2.244-4.908,5.047V273.426z
M233.566,273.426c0,2.806,2.244,5.048,5.048,5.048h3.646c3.226,0,5.048-2.242,5.048-5.048v-42.064
c2.665-5.189,8.694-10.377,16.827-10.377h2.524c2.944,0,5.188-2.103,5.188-5.048v-3.786c0-2.804-1.963-4.627-4.908-4.627h-2.524
c-6.45,0-13.602,4.346-17.107,9.254l-0.14-3.225c-0.28-2.804-1.963-4.908-4.908-4.908h-3.646c-2.804,0-5.048,2.244-5.048,5.047
V273.426z M201.036,231.361c0.982,2.805,3.646,3.646,6.311,2.805l3.785-1.263c3.085-0.981,3.787-3.505,2.945-5.89
c-3.926-11.498-13.32-19.491-28.043-19.491c-19.491,0-31.83,15.845-31.83,36.037c0,20.472,11.638,36.037,31.549,36.037
c14.864,0,26.923-9.815,30.007-22.716c0.702-2.945-1.122-5.328-4.066-5.328h-4.346c-2.665,0-4.768,1.401-5.469,3.926
c-2.104,6.73-8.133,10.937-15.565,11.078c-11.498,0.14-18.509-10.096-18.509-22.997c0-13.04,6.871-23.417,18.229-23.276
C193.324,220.424,198.513,224.63,201.036,231.361 M98.117,257.302c-0.281-2.664-2.384-4.768-5.188-4.768h-4.066
c-2.664,0-4.627,2.104-4.487,4.768c0.421,12.899,10.938,22.295,28.043,22.295c17.948,0,28.466-9.815,28.466-21.875
c0-10.937-7.012-17.247-17.107-19.21c-5.328-0.981-11.638-2.244-16.686-3.646c-3.646-0.982-6.45-3.366-6.45-6.591
c0-4.487,3.786-8.553,11.639-8.553c7.151,0,11.218,4.207,12.059,7.571c0.701,2.665,3.085,4.206,5.468,4.206h3.926
c2.804,0,4.908-1.823,4.487-4.767c-1.542-11.078-11.217-19.21-25.801-19.21c-15.003,0-25.519,8.553-25.519,20.893
c0,10.516,7.993,16.126,16.125,18.51c6.029,1.823,11.638,3.084,17.668,4.205c3.786,0.702,6.59,3.086,6.59,6.871
c0,5.328-5.048,9.255-14.723,9.115C104.146,266.977,98.537,262.63,98.117,257.302 M31.091,266.274
c-6.311-0.561-10.096-3.505-10.096-8.132c0-4.908,3.786-7.853,9.675-7.853c4.066,0,7.432-0.14,11.217-0.42
c3.225-0.28,6.59-0.843,8.834-2.805v10.656c-0.702,4.768-4.767,7.853-11.218,8.553C36.839,266.557,33.895,266.557,31.091,266.274
M8.095,228.836c-0.561,2.946,1.542,4.77,4.207,4.77h4.206c2.945,0,5.048-1.543,5.609-4.207c1.122-5.469,5.609-9.256,14.303-9.256
c9.675,0,14.443,5.749,14.302,13.741v0.702c-1.402,2.244-5.468,3.225-8.974,3.365c-3.506,0.14-6.45,0.28-10.797,0.28
c-14.022,0-23.978,6.73-23.978,20.051c0,13.04,9.535,20.332,24.118,21.034c2.664,0.14,5.188,0,7.572-0.142
c5.468-0.28,11.357-2.805,14.442-6.87c1.402,3.085,6.59,6.169,10.937,6.169h2.804c2.945,0,5.048-1.821,5.048-4.768v-3.925
c0-2.805-1.963-4.628-4.627-4.628c-1.682,0-2.944-1.122-2.944-2.804v-27.483c0-16.266-11.779-27.343-28.044-27.343
C21.696,207.523,10.338,215.655,8.095,228.836"/>
<path clip-path="url(#SVGID_2_)" d="M585.82,216.386c-1.271-5.163-3.195-10.098-5.719-14.665
c-2.625-4.757-5.823-9.102-9.505-12.903c-3.819-3.955-8.108-7.37-12.743-10.145c-4.785-2.876-9.9-5.098-15.215-6.607
c-5.469-1.545-11.072-2.373-16.666-2.459c-5.722-0.064-11.451,0.593-17.018,2c-3.65,0.935-7.178,2.193-10.572,3.696l5.607,11.476
c2.613-1.129,5.32-2.083,8.114-2.798c4.465-1.129,9.058-1.667,13.683-1.606c4.475,0.069,8.976,0.736,13.373,1.977
c4.221,1.199,8.297,2.972,12.125,5.271c3.681,2.204,7.086,4.916,10.131,8.07c2.902,2.998,5.428,6.429,7.508,10.198
c1.982,3.589,3.495,7.474,4.498,11.55c0.956,3.874,1.396,7.922,1.308,12.035c-0.081,3.898-0.695,7.834-1.822,11.685
c-1.088,3.69-2.67,7.248-4.702,10.571c-1.945,3.18-4.339,6.126-7.115,8.755c-2.651,2.512-5.652,4.679-8.921,6.443
c-3.127,1.688-6.502,2.961-10.041,3.791c-3.353,0.79-6.844,1.13-10.398,1.019c-3.349-0.108-6.726-0.675-10.024-1.679
c-3.139-0.96-6.166-2.348-8.997-4.125c-2.679-1.684-5.172-3.759-7.399-6.157c-2.093-2.262-3.897-4.834-5.366-7.643
c-1.388-2.658-2.427-5.525-3.092-8.532c-0.629-2.822-0.877-5.763-0.738-8.747c0.129-2.789,0.646-5.604,1.541-8.384
c0.809-2.536,2.031-5.097,3.538-7.408c1.419-2.182,3.214-4.268,5.194-6.04c1.871-1.675,4.012-3.124,6.349-4.3
c2.169-1.084,4.589-1.914,7.003-2.399c2.257-0.456,4.722-0.617,7.112-0.461c2.199,0.145,4.52,0.62,6.707,1.371
c1.995,0.688,4.024,1.712,5.873,2.966c1.666,1.129,3.276,2.582,4.669,4.218c1.261,1.469,2.379,3.212,3.244,5.052
c0.789,1.672,1.382,3.562,1.716,5.489c0.303,1.7,0.371,3.583,0.199,5.436c-0.155,1.646-0.567,3.397-1.178,5.021
c-0.578,1.512-1.393,3.004-2.357,4.319c-0.887,1.213-1.999,2.364-3.221,3.333c-1.078,0.854-2.41,1.643-3.735,2.207
c-1.185,0.5-2.586,0.877-3.921,1.057c-1.205,0.153-2.557,0.142-3.816-0.036c-1.102-0.155-2.298-0.497-3.362-0.963
c-0.92-0.404-1.919-1.023-2.747-1.704c-0.726-0.596-1.454-1.387-2.009-2.186c-0.47-0.672-0.905-1.536-1.206-2.409
c-0.23-0.66-0.395-1.526-0.438-2.34c-0.035-0.613,0.039-1.427,0.18-2.057c0.123-0.527,0.391-1.201,0.689-1.733
c0.202-0.363,0.578-0.844,0.977-1.229c0.217-0.215,0.638-0.514,0.948-0.675c0.198-0.104,0.651-0.261,0.896-0.311
c0.141-0.027,0.507-0.028,0.719-0.006c3.501,0.422,6.669-2.109,7.072-5.612c0.401-3.504-2.111-6.67-5.614-7.073
c-0.692-0.079-2.517-0.227-4.562,0.147c-1.505,0.3-3.101,0.852-4.392,1.52c-0.86,0.447-2.531,1.426-4.003,2.888
c-1.245,1.203-2.38,2.672-3.188,4.124c-0.876,1.564-1.578,3.355-1.988,5.111c-0.413,1.846-0.583,3.846-0.483,5.609
c0.075,1.411,0.334,3.571,1.127,5.829c0.678,1.971,1.648,3.886,2.795,5.523c1.221,1.756,2.735,3.396,4.372,4.742
c1.74,1.432,3.721,2.653,5.735,3.538c2.137,0.933,4.456,1.593,6.696,1.908c2.387,0.342,4.89,0.358,7.266,0.053
c2.495-0.334,4.997-1.01,7.251-1.964c2.396-1.023,4.706-2.392,6.678-3.955c2.104-1.67,4.038-3.674,5.588-5.793
c1.624-2.216,3.001-4.74,3.993-7.336c1.017-2.705,1.69-5.591,1.953-8.362c0.28-3.029,0.165-6.008-0.337-8.834
c-0.525-3.018-1.474-6.037-2.74-8.716c-1.358-2.896-3.076-5.563-5.088-7.908c-2.145-2.518-4.571-4.704-7.214-6.492
c-2.801-1.901-5.788-3.405-8.88-4.471c-3.294-1.132-6.666-1.818-10.036-2.041c-3.552-0.223-7.076,0.003-10.462,0.688
c-3.555,0.715-6.982,1.891-10.204,3.503c-3.358,1.688-6.436,3.777-9.141,6.2c-2.845,2.544-5.328,5.434-7.379,8.587
c-2.15,3.299-3.833,6.829-4.997,10.477c-1.237,3.84-1.956,7.774-2.138,11.692c-0.191,4.113,0.156,8.188,1.028,12.104
c0.906,4.095,2.332,8.022,4.24,11.679c1.992,3.812,4.453,7.311,7.319,10.409c2.993,3.225,6.348,6.012,9.974,8.29
c3.784,2.376,7.838,4.234,12.062,5.527c4.381,1.332,8.866,2.079,13.336,2.227c0.582,0.019,1.165,0.027,1.746,0.027
c4.078,0,8.107-0.464,11.988-1.38c4.626-1.085,9.061-2.761,13.182-4.981c4.257-2.297,8.174-5.128,11.641-8.411
c3.59-3.402,6.694-7.226,9.227-11.362c2.615-4.277,4.652-8.861,6.061-13.637c1.445-4.943,2.231-9.996,2.335-15.011
C587.607,226.516,587.045,221.347,585.82,216.386"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -1,110 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="595.279px" height="419.531px" viewBox="0 0 595.279 419.531" enable-background="new 0 0 595.279 419.531"
xml:space="preserve">
<g>
<defs>
<rect id="SVGID_1_" x="6.973" y="169.602" width="580.536" height="116.927"/>
</defs>
<clipPath id="SVGID_2_">
<use xlink:href="#SVGID_1_" overflow="visible"/>
</clipPath>
<path clip-path="url(#SVGID_2_)" d="M415.848,234.446c1.263-7.151,7.572-14.584,17.247-14.584c9.114,0,15.563,6.591,15.705,14.584
H415.848z M448.519,258.001c-2.664,5.892-8.272,8.834-15.144,8.834c-10.798,0-18.51-8.834-18.368-20.471h42.767
c3.085,0,5.188-1.822,5.188-5.189v-4.485c-0.421-15.985-12.198-29.167-29.728-29.167c-19.91,0-32.109,15.986-32.109,35.477
c0,20.894,11.639,36.598,32.25,36.598c13.462,0,24.259-7.15,28.745-18.932c1.264-3.363-0.562-6.17-3.926-6.17h-4.207
C451.604,254.496,449.781,255.337,448.519,258.001 M340.692,229.818c3.226-4.627,8.833-9.255,15.984-9.255
c10.798,0,17.809,10.655,17.809,22.996c0,12.901-6.449,22.854-17.809,22.854c-7.151,0-12.76-5.188-15.984-10.096V229.818z
M340.552,272.585c4.628,4.066,10.096,7.012,16.266,7.012c19.49,0.14,31.409-15.564,31.551-36.178
c0.141-20.473-11.779-35.896-31.41-35.896c-6.311,0-12.06,2.945-16.266,6.311v-32.25c0-2.804-2.243-5.048-5.188-5.048h-3.646
c-2.806,0-4.908,2.244-4.908,5.048v91.842c0,2.807,2.104,5.049,4.908,5.049h3.506c2.943,0,4.627-2.104,5.048-4.907L340.552,272.585
z M285.587,187.612c0,5.329,3.926,9.114,9.115,9.114c5.188,0,9.255-3.785,9.255-9.114c0-5.328-4.067-9.254-9.255-9.254
C289.513,178.358,285.587,182.284,285.587,187.612 M287.971,273.425c0,2.807,2.104,5.049,4.908,5.049h3.505
c2.804,0,5.048-2.242,5.048-5.049v-59.732c0-2.804-1.823-5.047-4.908-5.047h-3.646c-2.804,0-4.908,2.244-4.908,5.047
L287.971,273.425L287.971,273.425z M233.566,273.425c0,2.807,2.244,5.049,5.048,5.049h3.646c3.226,0,5.048-2.242,5.048-5.049
v-42.063c2.665-5.188,8.694-10.377,16.827-10.377h2.524c2.943,0,5.188-2.103,5.188-5.048v-3.786c0-2.804-1.963-4.627-4.908-4.627
h-2.523c-6.45,0-13.603,4.347-17.107,9.254l-0.14-3.226c-0.28-2.803-1.963-4.907-4.908-4.907h-3.646
c-2.804,0-5.048,2.244-5.048,5.047V273.425L233.566,273.425z M201.036,231.361c0.981,2.805,3.646,3.646,6.311,2.805l3.785-1.263
c3.085-0.981,3.787-3.505,2.945-5.89c-3.927-11.498-13.32-19.491-28.043-19.491c-19.491,0-31.83,15.845-31.83,36.037
c0,20.472,11.638,36.037,31.549,36.037c14.864,0,26.923-9.815,30.007-22.716c0.702-2.945-1.122-5.328-4.066-5.328h-4.346
c-2.665,0-4.768,1.4-5.469,3.926c-2.104,6.73-8.133,10.938-15.565,11.078c-11.498,0.14-18.509-10.096-18.509-22.997
c0-13.04,6.871-23.417,18.229-23.276C193.324,220.423,198.513,224.63,201.036,231.361 M98.117,257.302
c-0.281-2.664-2.385-4.768-5.188-4.768h-4.066c-2.664,0-4.627,2.104-4.486,4.768c0.421,12.898,10.938,22.295,28.043,22.295
c17.948,0,28.466-9.814,28.466-21.875c0-10.938-7.012-17.247-17.107-19.21c-5.327-0.981-11.638-2.244-16.686-3.646
c-3.646-0.982-6.45-3.367-6.45-6.592c0-4.486,3.786-8.553,11.639-8.553c7.151,0,11.219,4.207,12.06,7.57
c0.701,2.666,3.085,4.207,5.468,4.207h3.926c2.805,0,4.908-1.824,4.487-4.768c-1.542-11.078-11.217-19.21-25.801-19.21
c-15.003,0-25.52,8.554-25.52,20.894c0,10.516,7.993,16.125,16.125,18.51c6.029,1.822,11.639,3.084,17.668,4.205
c3.786,0.701,6.591,3.086,6.591,6.871c0,5.328-5.049,9.254-14.724,9.115C104.146,266.977,98.537,262.63,98.117,257.302
M31.091,266.274c-6.312-0.562-10.096-3.505-10.096-8.132c0-4.908,3.785-7.854,9.675-7.854c4.065,0,7.432-0.14,11.217-0.42
c3.225-0.279,6.59-0.843,8.834-2.805v10.656c-0.702,4.768-4.767,7.854-11.218,8.553C36.839,266.557,33.895,266.557,31.091,266.274
M8.095,228.835c-0.561,2.947,1.542,4.771,4.207,4.771h4.206c2.945,0,5.048-1.543,5.609-4.207
c1.121-5.469,5.608-9.256,14.303-9.256c9.675,0,14.442,5.749,14.302,13.741v0.701c-1.402,2.244-5.468,3.226-8.974,3.365
c-3.506,0.141-6.45,0.28-10.797,0.28c-14.022,0-23.979,6.729-23.979,20.052c0,13.039,9.535,20.332,24.118,21.033
c2.664,0.141,5.188,0,7.572-0.143c5.468-0.279,11.356-2.805,14.441-6.869c1.402,3.085,6.59,6.169,10.938,6.169h2.804
c2.945,0,5.048-1.821,5.048-4.769v-3.924c0-2.806-1.963-4.629-4.627-4.629c-1.682,0-2.944-1.121-2.944-2.804v-27.483
c0-16.266-11.778-27.343-28.044-27.343C21.696,207.523,10.338,215.655,8.095,228.835"/>
</g>
<g>
<defs>
<rect id="SVGID_3_" x="6.973" y="169.602" width="580.536" height="116.927"/>
</defs>
<clipPath id="SVGID_4_">
<use xlink:href="#SVGID_3_" overflow="visible"/>
</clipPath>
</g>
<g>
<path d="M519.088,182.75c1.375-0.154,2.759-0.25,4.148-0.297l-0.362-12.773c-1.799,0.062-3.593,0.197-5.381,0.404L519.088,182.75z
M527.134,220.031c-0.267-0.029-0.7-0.071-1.249-0.087c-0.88-0.028-2.051,0.002-3.311,0.233c-1.508,0.298-3.103,0.854-4.393,1.523
c-0.861,0.445-2.532,1.424-4.005,2.886c-1.244,1.203-2.38,2.673-3.188,4.123c-0.496,0.885-0.924,1.843-1.286,2.822l12.254,3.784
c0.061-0.126,0.114-0.264,0.182-0.379c0.2-0.364,0.574-0.844,0.976-1.228c0.217-0.217,0.636-0.514,0.948-0.676
c0.197-0.105,0.648-0.263,0.895-0.31c0.114-0.023,0.382-0.028,0.59-0.015c0.047,0.002,0.093,0.002,0.129,0.007
c3.502,0.422,6.671-2.107,7.073-5.613C533.15,223.599,530.636,220.434,527.134,220.031 M509.485,171.556
c-0.965,0.248-1.918,0.52-2.867,0.811l3.677,12.229c0.774-0.238,1.552-0.465,2.342-0.666c2.006-0.507,5.735-1.088,5.557-1.064
l-1.659-12.666C516.711,170.178,511.983,170.925,509.485,171.556 M574.729,231.505c-0.075,3.552-0.609,7.126-1.555,10.646
l12.34,3.361c1.215-4.536,1.887-9.148,1.982-13.733c0.025-1.19,0.01-2.376-0.033-3.558l-12.763,0.422
C574.735,229.595,574.749,230.547,574.729,231.505 M498.38,175.332l5.607,11.479c0.501-0.219,1.01-0.424,1.519-0.627l-4.727-11.859
C499.972,174.647,499.171,174.982,498.38,175.332 M561.09,262.52c-2.652,2.512-5.652,4.679-8.923,6.44
c-3.127,1.689-6.503,2.963-10.041,3.793c-3.351,0.79-6.843,1.133-10.397,1.018c-1.92-0.06-3.849-0.292-5.767-0.65l-2.308,12.554
c2.544,0.472,5.104,0.776,7.664,0.862c0.58,0.019,1.162,0.024,1.742,0.024c4.08,0,8.11-0.465,11.988-1.378
c4.625-1.086,9.062-2.764,13.184-4.982c4.258-2.295,8.175-5.127,11.641-8.409c0.533-0.507,1.034-1.042,1.55-1.567l-9.139-8.914
C561.889,261.717,561.502,262.127,561.09,262.52 M568.204,253.764c-1.188,1.943-2.562,3.789-4.072,5.536l9.695,8.305
c1.949-2.264,3.73-4.653,5.274-7.177c1.848-3.021,3.401-6.199,4.653-9.488l-11.935-4.569
C570.85,248.925,569.647,251.407,568.204,253.764 M585.971,217.079l-12.446,2.881c0.341,1.46,0.604,2.943,0.799,4.444l12.673-1.697
C586.747,220.808,586.404,218.927,585.971,217.079 M542.637,172.095c-1.34-0.377-2.687-0.713-4.038-1.004l-2.693,12.482
c1.091,0.237,2.175,0.505,3.253,0.809c0.679,0.194,1.349,0.426,2.023,0.649l4.046-12.131
C544.368,172.616,543.507,172.343,542.637,172.095 M528.501,169.732l-0.751,12.769c1.538,0.083,3.074,0.214,4.607,0.436
l1.805-12.643C532.281,170.021,530.393,169.835,528.501,169.732 M580.102,201.75c-2.626-4.757-5.824-9.101-9.507-12.903
c-3.818-3.953-8.107-7.369-12.74-10.146c-2.381-1.429-4.843-2.696-7.369-3.796l-5.08,11.717c2.012,0.878,3.975,1.888,5.878,3.032
c3.683,2.203,7.086,4.916,10.133,8.068c2.9,2.998,5.427,6.43,7.507,10.2c1.355,2.449,2.471,5.05,3.375,7.742l12.098-4.121
C583.249,208.139,581.818,204.859,580.102,201.75 M531.03,248.33c-0.688-0.098-1.407-0.274-2.113-0.499l-3.888,12.154
c1.395,0.446,2.816,0.789,4.215,0.987c0.933,0.136,1.886,0.204,2.842,0.236l0.135-12.767
C531.818,248.416,531.422,248.384,531.03,248.33 M538.768,247.306c-1.088,0.461-2.356,0.807-3.59,1.002l1.932,12.615
c2.294-0.358,4.577-0.985,6.653-1.863c0.313-0.135,0.623-0.288,0.931-0.436l-5.345-11.588
C539.156,247.128,538.963,247.225,538.768,247.306 M521.705,272.095c-3.141-0.959-6.169-2.349-8.997-4.127
c-2.683-1.684-5.173-3.757-7.402-6.157c-0.494-0.532-0.968-1.089-1.432-1.655l-9.938,8.017c0.644,0.794,1.312,1.569,2.007,2.318
c2.994,3.229,6.351,6.016,9.975,8.294c3.776,2.37,7.824,4.225,12.037,5.518l3.779-12.197
C521.725,272.098,521.715,272.096,521.705,272.095 M524.923,245.663c-0.729-0.596-1.46-1.387-2.013-2.187
c-0.47-0.671-0.903-1.533-1.205-2.407c-0.229-0.66-0.394-1.527-0.438-2.339c-0.002-0.009,0-0.016,0-0.021l-12.76,0.114
c0.005,0.202,0,0.41,0.011,0.608c0.076,1.411,0.333,3.571,1.128,5.828c0.677,1.972,1.648,3.887,2.795,5.524
c1.222,1.754,2.734,3.396,4.371,4.743c0.592,0.483,1.215,0.935,1.851,1.365l7.059-10.633
C525.446,246.069,525.173,245.869,524.923,245.663 M499.942,254.168c-1.387-2.659-2.426-5.527-3.093-8.531
c-0.631-2.822-0.876-5.763-0.738-8.749c0.13-2.788,0.646-5.605,1.542-8.384c0.81-2.537,2.03-5.098,3.537-7.408
c0.197-0.303,0.418-0.596,0.627-0.894l-10.385-7.42c-0.317,0.447-0.645,0.887-0.942,1.346c-2.149,3.297-3.832,6.831-4.995,10.477
c-1.237,3.84-1.958,7.774-2.139,11.692c-0.191,4.115,0.156,8.188,1.027,12.105c0.907,4.096,2.33,8.021,4.239,11.682
c0.576,1.099,1.197,2.159,1.851,3.204l10.814-6.779C500.815,255.746,500.361,254.972,499.942,254.168 M506.385,215.056
c1.871-1.675,4.013-3.124,6.35-4.301c0.679-0.338,1.389-0.642,2.109-0.93l-4.726-11.859c-1.057,0.419-2.097,0.868-3.108,1.375
c-3.357,1.688-6.437,3.778-9.142,6.199c-1.511,1.353-2.915,2.81-4.212,4.345l9.735,8.265
C504.323,217.053,505.324,216.006,506.385,215.056 M538.66,211.746l6.869-10.757c-1.771-1.12-3.602-2.104-5.488-2.904
l-4.987,11.759C536.281,210.365,537.496,211.015,538.66,211.746 M544.099,216.451c1.263,1.47,2.38,3.214,3.243,5.053
c0.788,1.67,1.383,3.564,1.719,5.489c0.302,1.7,0.369,3.582,0.196,5.438c-0.154,1.644-0.565,3.395-1.177,5.018
c-0.578,1.511-1.393,3.005-2.356,4.319c-0.889,1.215-2,2.365-3.224,3.335c-0.011,0.007-0.022,0.017-0.034,0.022l7.857,10.063
c0.039-0.026,0.079-0.057,0.116-0.084c2.104-1.669,4.038-3.673,5.587-5.794c1.624-2.215,3.002-4.738,3.994-7.335
c1.015-2.705,1.69-5.59,1.952-8.362c0.28-3.029,0.166-6.008-0.337-8.834c-0.525-3.018-1.476-6.039-2.74-8.718
c-1.358-2.895-3.075-5.562-5.09-7.907c-1.05-1.238-2.173-2.39-3.354-3.457l-8.577,9.463
C542.658,214.867,543.405,215.637,544.099,216.451 M518.809,208.576l-2.863-12.439c0.422-0.098,0.842-0.213,1.27-0.297
c3.387-0.686,6.912-0.911,10.459-0.687c2.167,0.142,4.326,0.497,6.471,1.016l-2.969,12.417c-1.45-0.353-2.91-0.595-4.326-0.69
c-2.391-0.156-4.854,0.005-7.113,0.461C519.429,208.419,519.117,208.503,518.809,208.576"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square70x70logo src="images/hq-favicons/mstile-70x70.png"/>
<square150x150logo src="images/hq-favicons/mstile-150x150.png"/>
<square310x310logo src="images/hq-favicons/mstile-310x310.png"/>
<wide310x150logo src="images/hq-favicons/mstile-310x150.png"/>
<TileColor>#00aba9</TileColor>
</tile>
</msapplication>
</browserconfig>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,35 +0,0 @@
{
"name": "Ascribe",
"icons": [
{
"src": "images\/hq-favicons\/android-chrome-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "images\/hq-favicons\/android-chrome-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "images\/hq-favicons\/android-chrome-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "images\/hq-favicons\/android-chrome-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "images\/hq-favicons\/android-chrome-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,58 @@
'use strict';
import React from 'react/addons';
const { string, object } = React.PropTypes;
/**
* Originally, this button was introduced for `RegisterPieceForm`.
* The underlying problem that lead to the implementation was that multiple `InputFineuploader`s
* where displayed in one form.
* For the form's submit button always to represent an applicable state we had to save
* the ready states of the multiple uploaders in `RegisterPieceForm`.
* However, on each invocation of `this.setState`, we were re-rendering the form again,
* which caused (if the user had the file-selector open) a detachment between user input and
* displayed page.
*
* This button helps to outsource the state of the form's readiness to another component,
* to solve the problem.
*/
const FormSubmitButton = React.createClass({
propTypes: {
defaultReadyStates: object.isRequired,
label: string.isRequired
},
getInitialState() {
return {
readyStates: this.props.defaultReadyStates
};
},
setReadyStateForKey(key, state) {
const readyStates = React.addons.update(this.state.readyStates, { [key]: { $set: state } });
this.setState({ readyStates });
},
render() {
const { label } = this.props;
const { readyStates } = this.state;
// Reduce all readyStates to a single boolean
const ready = Object.keys(readyStates)
.reduce((defaultState, stateKey) => {
return defaultState && readyStates[stateKey];
}, true);
return (
<button
type="submit"
className="btn btn-default btn-wide"
disabled={!ready}>
{label}
</button>
);
}
});
export default FormSubmitButton;

View File

@ -119,7 +119,7 @@ let EditionActionPanel = React.createClass({
isInline={true}> isInline={true}>
<Property <Property
name="bitcoin_id" name="bitcoin_id"
hidden={true}> expanded={false}>
<input <input
type="text" type="text"
value={edition.bitcoin_id} /> value={edition.bitcoin_id} />
@ -139,7 +139,7 @@ let EditionActionPanel = React.createClass({
isInline={true}> isInline={true}>
<Property <Property
name="bitcoin_id" name="bitcoin_id"
hidden={true}> expanded={false}>
<input <input
type="text" type="text"
value={edition.bitcoin_id} /> value={edition.bitcoin_id} />

View File

@ -13,7 +13,6 @@ import { getCookie } from '../../utils/fetch_api_utils';
let FurtherDetailsFileuploader = React.createClass({ let FurtherDetailsFileuploader = React.createClass({
propTypes: { propTypes: {
uploadStarted: React.PropTypes.func,
pieceId: React.PropTypes.number, pieceId: React.PropTypes.number,
otherData: React.PropTypes.arrayOf(React.PropTypes.object), otherData: React.PropTypes.arrayOf(React.PropTypes.object),
setIsUploadReady: React.PropTypes.func, setIsUploadReady: React.PropTypes.func,
@ -46,7 +45,6 @@ let FurtherDetailsFileuploader = React.createClass({
name="other_data_key" name="other_data_key"
label="Additional files"> label="Additional files">
<ReactS3FineUploader <ReactS3FineUploader
uploadStarted={this.props.uploadStarted}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'otherdata', fileClass: 'otherdata',

View File

@ -238,7 +238,16 @@ let Form = React.createClass({
renderChildren() { renderChildren() {
return ReactAddons.Children.map(this.props.children, (child, i) => { return ReactAddons.Children.map(this.props.children, (child, i) => {
if (child) { if (child) {
return ReactAddons.addons.cloneWithProps(child, {
// Since refs will be overwritten by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overwriting it.
if(typeof child.ref === 'function' && this.refs[child.props.name]) {
child.ref(this.refs[child.props.name]);
}
return React.cloneElement(child, {
handleChange: this.handleChangeChild, handleChange: this.handleChangeChild,
ref: child.props.name, ref: child.props.name,
key: i, key: i,

View File

@ -11,7 +11,6 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import PropertyCollapsible from './property_collapsible';
import InputTextAreaToggable from './input_textarea_toggable'; import InputTextAreaToggable from './input_textarea_toggable';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
@ -122,9 +121,10 @@ let ContractAgreementForm = React.createClass({
required/> required/>
</Property> </Property>
{this.getContracts()} {this.getContracts()}
<PropertyCollapsible <Property
name='appendix' name='appendix'
checkboxLabel={getLangText('Add appendix to the contract')}> checkboxLabel={getLangText('Add appendix to the contract')}
expanded={false}>
<span>{getLangText('Appendix')}</span> <span>{getLangText('Appendix')}</span>
{/* We're using disabled on a form here as PropertyCollapsible currently {/* We're using disabled on a form here as PropertyCollapsible currently
does not support the disabled + overrideForm functionality */} does not support the disabled + overrideForm functionality */}
@ -132,7 +132,7 @@ let ContractAgreementForm = React.createClass({
rows={1} rows={1}
disabled={false} disabled={false}
placeholder={getLangText('This will be appended to the contract selected above')}/> placeholder={getLangText('This will be appended to the contract selected above')}/>
</PropertyCollapsible> </Property>
</Form> </Form>
); );
} }

View File

@ -51,9 +51,9 @@ let CreateContractForm = React.createClass({
this.refs.form.reset(); this.refs.form.reset();
}, },
submitFileName(fileName) { submitFile({ originalName }) {
this.setState({ this.setState({
contractName: fileName contractName: originalName
}); });
this.refs.form.submit(); this.refs.form.submit();
@ -69,7 +69,7 @@ let CreateContractForm = React.createClass({
name="blob" name="blob"
label={getLangText('Contract file (*.pdf only, max. 50MB per contract)')}> label={getLangText('Contract file (*.pdf only, max. 50MB per contract)')}>
<InputFineUploader <InputFineUploader
submitFileName={this.submitFileName} submitFile={this.submitFile}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'contract' fileClass: 'contract'
@ -91,14 +91,14 @@ let CreateContractForm = React.createClass({
<Property <Property
name='name' name='name'
label={getLangText('Contract name')} label={getLangText('Contract name')}
hidden={true}> expanded={false}>
<input <input
type="text" type="text"
value={this.state.contractName}/> value={this.state.contractName}/>
</Property> </Property>
<Property <Property
name="is_public" name="is_public"
hidden={true}> expanded={false}>
<input <input
type="checkbox" type="checkbox"
value={this.props.isPublic} /> value={this.props.isPublic} />

View File

@ -123,7 +123,7 @@ let LoanForm = React.createClass({
<Property <Property
name="terms" name="terms"
label={getLangText('Loan Contract')} label={getLangText('Loan Contract')}
hidden={false} expanded={true}
className="notification-contract-pdf"> className="notification-contract-pdf">
<embed <embed
className="loan-form" className="loan-form"
@ -164,7 +164,7 @@ let LoanForm = React.createClass({
<Property <Property
name="terms" name="terms"
style={{paddingBottom: 0}} style={{paddingBottom: 0}}
hidden={true}> expanded={false}>
<InputCheckbox <InputCheckbox
key="terms_implicitly" key="terms_implicitly"
defaultChecked={true} /> defaultChecked={true} />
@ -260,7 +260,7 @@ let LoanForm = React.createClass({
label={getLangText('Start date')} label={getLangText('Start date')}
editable={!this.props.startdate} editable={!this.props.startdate}
overrideForm={!!this.props.startdate} overrideForm={!!this.props.startdate}
hidden={!this.props.showStartDate}> expanded={this.props.showStartDate}>
<InputDate <InputDate
defaultValue={this.props.startdate} defaultValue={this.props.startdate}
placeholderText={getLangText('Loan start date')} /> placeholderText={getLangText('Loan start date')} />
@ -270,7 +270,7 @@ let LoanForm = React.createClass({
editable={!this.props.enddate} editable={!this.props.enddate}
overrideForm={!!this.props.enddate} overrideForm={!!this.props.enddate}
label={getLangText('End date')} label={getLangText('End date')}
hidden={!this.props.showEndDate}> expanded={this.props.showEndDate}>
<InputDate <InputDate
defaultValue={this.props.enddate} defaultValue={this.props.enddate}
placeholderText={getLangText('Loan end date')} /> placeholderText={getLangText('Loan end date')} />
@ -280,7 +280,7 @@ let LoanForm = React.createClass({
label={getLangText('Personal Message')} label={getLangText('Personal Message')}
editable={true} editable={true}
overrideForm={true} overrideForm={true}
hidden={!this.props.showPersonalMessage}> expanded={this.props.showPersonalMessage}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.message} defaultValue={this.props.message}
@ -292,7 +292,7 @@ let LoanForm = React.createClass({
<Property <Property
name='password' name='password'
label={getLangText('Password')} label={getLangText('Password')}
hidden={!this.props.showPassword}> expanded={this.props.showPassword}>
<input <input
type="password" type="password"
placeholder={getLangText('Enter your password')} placeholder={getLangText('Enter your password')}

View File

@ -8,6 +8,8 @@ import UserActions from '../../actions/user_actions';
import Form from './form'; import Form from './form';
import Property from './property'; import Property from './property';
import InputFineUploader from './input_fineuploader'; import InputFineUploader from './input_fineuploader';
import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button';
import FormSubmitButton from '../ascribe_buttons/form_submit_button';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
@ -15,7 +17,9 @@ import AscribeSpinner from '../ascribe_spinner';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; import { formSubmissionValidation,
displayValidFilesFilter,
displayRemovedFilesFilter } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let RegisterPieceForm = React.createClass({ let RegisterPieceForm = React.createClass({
@ -48,7 +52,7 @@ let RegisterPieceForm = React.createClass({
getInitialState(){ getInitialState(){
return mergeOptions( return mergeOptions(
{ {
isUploadReady: false digitalWorkFile: null
}, },
UserStore.getState() UserStore.getState()
); );
@ -67,31 +71,80 @@ let RegisterPieceForm = React.createClass({
this.setState(state); this.setState(state);
}, },
setIsUploadReady(isReady) { /**
this.setState({ * This method is overloaded so that we can track the ready-state
isUploadReady: isReady * of each uploader in the component
}); * @param {string} uploaderKey Name of the uploader's key to track
*/
setIsUploadReady(uploaderKey) {
return (isUploadReady) => {
// See documentation of `FormSubmitButton` for more detailed information
// on this.
this.refs.submitButton.setReadyStateForKey(uploaderKey, isUploadReady);
};
},
handleChangedDigitalWork(digitalWorkFile) {
if (digitalWorkFile &&
(digitalWorkFile.status === 'deleted' || digitalWorkFile.status === 'canceled')) {
this.refs.form.refs.thumbnail_file.reset();
this.setState({ digitalWorkFile: null });
} else {
this.setState({ digitalWorkFile });
}
},
handleChangedThumbnail(thumbnailFile) {
const { digitalWorkFile } = this.state;
const { fineuploader } = this.refs.digitalWorkFineUploader.refs;
fineuploader.setThumbnailForFileId(digitalWorkFile.id, thumbnailFile.url);
},
isThumbnailDialogExpanded() {
const { digitalWorkFile } = this.state;
if(digitalWorkFile) {
const { type: mimeType } = digitalWorkFile;
const mimeSubType = mimeType && mimeType.split('/').length ? mimeType.split('/')[1]
: 'unknown';
return AppConstants.supportedThumbnailFileFormats.indexOf(mimeSubType) === -1;
} else {
return false;
}
}, },
render() { render() {
let currentUser = this.state.currentUser; const { disabled,
let enableLocalHashing = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false; handleSuccess,
enableLocalHashing = enableLocalHashing && this.props.enableLocalHashing; submitMessage,
headerMessage,
isFineUploaderActive,
onLoggedOut,
isFineUploaderEditable,
location,
children,
enableLocalHashing } = this.props;
const { currentUser} = this.state;
const profileHashLocally = currentUser && currentUser.profile ? currentUser.profile.hash_locally : false;
const hashLocally = profileHashLocally && enableLocalHashing;
return ( return (
<Form <Form
disabled={this.props.disabled} disabled={disabled}
className="ascribe-form-bordered" className="ascribe-form-bordered"
ref='form' ref='form'
url={ApiUrls.pieces_list} url={ApiUrls.pieces_list}
handleSuccess={this.props.handleSuccess} handleSuccess={handleSuccess}
buttons={ buttons={
<button <FormSubmitButton
type="submit" ref="submitButton"
className="btn btn-default btn-wide" defaultReadyStates={{
disabled={!this.state.isUploadReady || this.props.disabled}> digitalWorkKeyReady: false,
{this.props.submitMessage} thumbnailKeyReady: true
</button> }}
label={submitMessage}/>
} }
spinner={ spinner={
<span className="btn btn-default btn-wide btn-spinner"> <span className="btn btn-default btn-wide btn-spinner">
@ -99,12 +152,14 @@ let RegisterPieceForm = React.createClass({
</span> </span>
}> }>
<div className="ascribe-form-header"> <div className="ascribe-form-header">
<h3>{this.props.headerMessage}</h3> <h3>{headerMessage}</h3>
</div> </div>
<Property <Property
name="digital_work_key" name="digital_work_key"
ignoreFocus={true}> ignoreFocus={true}
label={getLangText('Your Work')}>
<InputFineUploader <InputFineUploader
ref={ref => this.refs.digitalWorkFineUploader = ref}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork' fileClass: 'digitalwork'
@ -113,13 +168,41 @@ let RegisterPieceForm = React.createClass({
url: ApiUrls.blob_digitalworks url: ApiUrls.blob_digitalworks
}} }}
validation={AppConstants.fineUploader.validation.registerWork} validation={AppConstants.fineUploader.validation.registerWork}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady('digitalWorkKeyReady')}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
isFineUploaderActive={this.props.isFineUploaderActive} isFineUploaderActive={isFineUploaderActive}
onLoggedOut={this.props.onLoggedOut} onLoggedOut={onLoggedOut}
disabled={!this.props.isFineUploaderEditable} disabled={!isFineUploaderEditable}
enableLocalHashing={enableLocalHashing} enableLocalHashing={hashLocally}
uploadMethod={this.props.location.query.method} /> uploadMethod={location.query.method}
handleChangedFile={this.handleChangedDigitalWork}/>
</Property>
<Property
name="thumbnail_file"
expanded={this.isThumbnailDialogExpanded()}>
<InputFineUploader
ref={ref => this.refs.thumbnailFineUploader = ref}
fileInputElement={UploadButton({ className: 'btn btn-secondary btn-sm' })}
createBlobRoutine={{
url: ApiUrls.blob_thumbnails
}}
handleChangedFile={this.handleChangedThumbnail}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'thumbnail'
}}
validation={{
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
allowedExtensions: ['png', 'jpg', 'jpeg', 'gif']
}}
setIsUploadReady={this.setIsUploadReady('thumbnailKeyReady')}
location={location}
fileClassToUpload={{
singular: getLangText('Select representative image'),
plural: getLangText('Select representative images')
}}/>
</Property> </Property>
<Property <Property
name='artist_name' name='artist_name'
@ -146,7 +229,7 @@ let RegisterPieceForm = React.createClass({
min={1} min={1}
required/> required/>
</Property> </Property>
{this.props.children} {children}
</Form> </Form>
); );
} }

View File

@ -10,13 +10,13 @@ import AppConstants from '../../constants/application_constants';
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
const { func, bool, object, shape, string, number, arrayOf } = React.PropTypes; const { func, bool, shape, string, number, arrayOf } = React.PropTypes;
const InputFineUploader = React.createClass({ const InputFineUploader = React.createClass({
propTypes: { propTypes: {
setIsUploadReady: func, setIsUploadReady: func,
isReadyForFormSubmission: func, isReadyForFormSubmission: func,
submitFileName: func, submitFile: func,
fileInputElement: func, fileInputElement: func,
areAssetsDownloadable: bool, areAssetsDownloadable: bool,
@ -51,7 +51,8 @@ const InputFineUploader = React.createClass({
fileClassToUpload: shape({ fileClassToUpload: shape({
singular: string, singular: string,
plural: string plural: string
}) }),
handleChangedFile: func
}, },
getDefaultProps() { getDefaultProps() {
@ -77,8 +78,8 @@ const InputFineUploader = React.createClass({
this.props.onChange({ target: { value: this.state.value } }); this.props.onChange({ target: { value: this.state.value } });
} }
if(typeof this.props.submitFileName === 'function') { if(typeof this.props.submitFile === 'function') {
this.props.submitFileName(file.originalName); this.props.submitFile(file);
} }
}, },
@ -104,7 +105,8 @@ const InputFineUploader = React.createClass({
onLoggedOut, onLoggedOut,
enableLocalHashing, enableLocalHashing,
fileClassToUpload, fileClassToUpload,
location } = this.props; uploadMethod,
handleChangedFile } = this.props;
let editable = this.props.isFineUploaderActive; let editable = this.props.isFineUploaderActive;
// if disabled is actually set by property, we want to override // if disabled is actually set by property, we want to override
@ -139,10 +141,11 @@ const InputFineUploader = React.createClass({
'X-CSRFToken': getCookie(AppConstants.csrftoken) 'X-CSRFToken': getCookie(AppConstants.csrftoken)
} }
}} }}
onInactive={this.props.onLoggedOut} onInactive={onLoggedOut}
enableLocalHashing={this.props.enableLocalHashing} enableLocalHashing={enableLocalHashing}
uploadMethod={this.props.uploadMethod} uploadMethod={uploadMethod}
fileClassToUpload={this.props.fileClassToUpload} /> fileClassToUpload={fileClassToUpload}
handleChangedFile={handleChangedFile}/>
); );
} }
}); });

View File

@ -3,58 +3,68 @@
import React from 'react'; import React from 'react';
import ReactAddons from 'react/addons'; import ReactAddons from 'react/addons';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger'; import Panel from 'react-bootstrap/lib/Panel';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
let Property = React.createClass({ const { bool, element, string, oneOfType, func, object, arrayOf } = React.PropTypes;
propTypes: {
hidden: React.PropTypes.bool,
editable: React.PropTypes.bool, const Property = React.createClass({
propTypes: {
editable: bool,
// If we want Form to have a different value for disabled as Property has one for // If we want Form to have a different value for disabled as Property has one for
// editable, we need to set overrideForm to true, as it will then override Form's // editable, we need to set overrideForm to true, as it will then override Form's
// disabled value for individual Properties // disabled value for individual Properties
overrideForm: React.PropTypes.bool, overrideForm: bool,
tooltip: React.PropTypes.element, label: string,
label: React.PropTypes.string, value: oneOfType([
value: React.PropTypes.oneOfType([ string,
React.PropTypes.string, element
React.PropTypes.element
]), ]),
footer: React.PropTypes.element, footer: element,
handleChange: React.PropTypes.func, handleChange: func,
ignoreFocus: React.PropTypes.bool, ignoreFocus: bool,
name: React.PropTypes.string.isRequired, name: string.isRequired,
className: React.PropTypes.string, className: string,
onClick: React.PropTypes.func, onClick: func,
onChange: React.PropTypes.func, onChange: func,
onBlur: React.PropTypes.func, onBlur: func,
children: React.PropTypes.oneOfType([ children: oneOfType([
React.PropTypes.arrayOf(React.PropTypes.element), arrayOf(element),
React.PropTypes.element element
]), ]),
style: React.PropTypes.object style: object,
expanded: bool,
checkboxLabel: string
}, },
getDefaultProps() { getDefaultProps() {
return { return {
editable: true, editable: true,
hidden: false, expanded: true,
className: '' className: ''
}; };
}, },
getInitialState() { getInitialState() {
const { expanded, ignoreFocus, checkboxLabel } = this.props;
return { return {
// We're mirroring expanded here as a state
// React's docs do NOT consider this an antipattern as long as it's
// not a "source of truth"-duplication
expanded,
// When a checkboxLabel is defined in the props, we want to set
// `ignoreFocus` to true
ignoreFocus: ignoreFocus || checkboxLabel,
// Please don't confuse initialValue with react's defaultValue. // Please don't confuse initialValue with react's defaultValue.
// initialValue is set by us to ensure that a user can reset a specific // initialValue is set by us to ensure that a user can reset a specific
// property (after editing) to its initial value // property (after editing) to its initial value
@ -65,9 +75,19 @@ let Property = React.createClass({
}; };
}, },
componentWillReceiveProps() { componentWillReceiveProps(nextProps) {
let childInput = this.refs.input; let childInput = this.refs.input;
// For expanded there are actually two use cases:
//
// 1. Control its value from the outside completely (do not define `checkboxLabel`)
// 2. Let it be controlled from the inside (default value can be set though via `expanded`)
//
// This handles case 1.
if(nextProps.expanded !== this.state.expanded && !this.props.checkboxLabel) {
this.setState({ expanded: nextProps.expanded });
}
// In order to set this.state.value from another component // In order to set this.state.value from another component
// the state of value should only be set if its not undefined and // the state of value should only be set if its not undefined and
// actually references something // actually references something
@ -80,13 +100,13 @@ let Property = React.createClass({
// from native HTML elements. // from native HTML elements.
// To enable developers to create input elements, they can expose a property called value // To enable developers to create input elements, they can expose a property called value
// in their state that will be picked up by property.js // in their state that will be picked up by property.js
} else if(childInput.state && typeof childInput.state.value !== 'undefined') { } else if(childInput && childInput.state && typeof childInput.state.value !== 'undefined') {
this.setState({ this.setState({
value: childInput.state.value value: childInput.state.value
}); });
} }
if(!this.state.initialValue && childInput.props.defaultValue) { if(!this.state.initialValue && childInput && childInput.props.defaultValue) {
this.setState({ this.setState({
initialValue: childInput.props.defaultValue initialValue: childInput.props.defaultValue
}); });
@ -138,7 +158,7 @@ let Property = React.createClass({
handleFocus() { handleFocus() {
// if ignoreFocus (bool) is defined, then just ignore focusing on // if ignoreFocus (bool) is defined, then just ignore focusing on
// the property and input // the property and input
if(this.props.ignoreFocus) { if(this.state.ignoreFocus) {
return; return;
} }
@ -190,7 +210,7 @@ let Property = React.createClass({
}, },
getClassName() { getClassName() {
if(this.props.hidden){ if(!this.state.expanded && !this.props.checkboxLabel){
return 'is-hidden'; return 'is-hidden';
} }
if(!this.props.editable){ if(!this.props.editable){
@ -207,8 +227,20 @@ let Property = React.createClass({
}, },
renderChildren(style) { renderChildren(style) {
// Input's props should only be cloned and propagated down the tree,
// if the component is actually being shown (!== 'expanded === false')
if((this.state.expanded && this.props.checkboxLabel) || !this.props.checkboxLabel) {
return ReactAddons.Children.map(this.props.children, (child) => { return ReactAddons.Children.map(this.props.children, (child) => {
return ReactAddons.addons.cloneWithProps(child, {
// Since refs will be overriden by this functions return statement,
// we still want to be able to define refs for nested `Form` or `Property`
// children, which is why we're upfront simply invoking the callback-ref-
// function before overriding it.
if(typeof child.ref === 'function' && this.refs.input) {
child.ref(this.refs.input);
}
return React.cloneElement(child, {
style, style,
onChange: this.handleChange, onChange: this.handleChange,
onFocus: this.handleFocus, onFocus: this.handleFocus,
@ -218,20 +250,51 @@ let Property = React.createClass({
name: this.props.name name: this.props.name
}); });
}); });
}
},
getLabelAndErrors() {
if(this.props.label || this.state.errors) {
return (
<p>
<span className="pull-left">{this.props.label}</span>
<span className="pull-right">{this.state.errors}</span>
</p>
);
} else {
return null;
}
},
handleCheckboxToggle() {
this.setState({expanded: !this.state.expanded});
},
getCheckbox() {
const { checkboxLabel } = this.props;
if(checkboxLabel) {
return (
<div
className="ascribe-property-collapsible-toggle"
onClick={this.handleCheckboxToggle}>
<input
onChange={this.handleCheckboxToggle}
type="checkbox"
checked={this.state.expanded}
ref="checkboxCollapsible"/>
<span className="checkbox">{' ' + checkboxLabel}</span>
</div>
);
} else {
return null;
}
}, },
render() { render() {
let footer = null; let footer = null;
let tooltip = <span/>;
let style = this.props.style ? mergeOptions({}, this.props.style) : {}; let style = this.props.style ? mergeOptions({}, this.props.style) : {};
if(this.props.tooltip){
tooltip = (
<Tooltip>
{this.props.tooltip}
</Tooltip>);
}
if(this.props.footer){ if(this.props.footer){
footer = ( footer = (
<div className="ascribe-property-footer"> <div className="ascribe-property-footer">
@ -239,28 +302,25 @@ let Property = React.createClass({
</div>); </div>);
} }
if(!this.props.editable) { style.paddingBottom = !this.state.expanded ? 0 : null;
style.cursor = 'not-allowed'; style.cursor = !this.props.editable ? 'not-allowed' : null;
}
return ( return (
<div <div
className={'ascribe-property-wrapper ' + this.getClassName()} className={'ascribe-property-wrapper ' + this.getClassName()}
onClick={this.handleFocus} onClick={this.handleFocus}
style={style}> style={style}>
<OverlayTrigger {this.getCheckbox()}
delay={500} <Panel
placement="top" collapsible
overlay={tooltip}> expanded={this.state.expanded}
className="bs-custom-panel">
<div className={'ascribe-property ' + this.props.className}> <div className={'ascribe-property ' + this.props.className}>
<p> {this.getLabelAndErrors()}
<span className="pull-left">{this.props.label}</span>
<span className="pull-right">{this.state.errors}</span>
</p>
{this.renderChildren(style)} {this.renderChildren(style)}
{footer} {footer}
</div> </div>
</OverlayTrigger> </Panel>
</div> </div>
); );
} }

View File

@ -1,96 +0,0 @@
'use strict';
import React from 'react';
import ReactAddons from 'react/addons';
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
import Tooltip from 'react-bootstrap/lib/Tooltip';
import Panel from 'react-bootstrap/lib/Panel';
let PropertyCollapsile = React.createClass({
propTypes: {
children: React.PropTypes.arrayOf(React.PropTypes.element),
checkboxLabel: React.PropTypes.string,
tooltip: React.PropTypes.string
},
getInitialState() {
return {
show: false
};
},
handleFocus() {
this.refs.checkboxCollapsible.getDOMNode().checked = !this.refs.checkboxCollapsible.getDOMNode().checked;
this.setState({
show: this.refs.checkboxCollapsible.getDOMNode().checked
});
},
handleChange(event) {
this.setState({value: event.target.value});
},
renderChildren() {
if(this.state.show) {
return ReactAddons.Children.map(this.props.children, (child) => {
return ReactAddons.addons.cloneWithProps(child, {
onChange: this.handleChange
});
});
}
},
reset() {
// If the child input is a native HTML element, it will be reset automatically
// by the DOM.
// However, we need to collapse this component again.
this.setState(this.getInitialState());
},
render() {
let tooltip = <span/>;
if (this.props.tooltip){
tooltip = (
<Tooltip>
{this.props.tooltip}
</Tooltip>);
}
let style = this.state.show ? {} : {paddingBottom: 0};
return (
<div
className={'ascribe-property-wrapper'}
style={style}>
<OverlayTrigger
delay={500}
placement="top"
overlay={tooltip}>
<div
className="ascribe-property-collapsible-toggle"
onClick={this.handleFocus}
onFocus={this.handleFocus}>
<input
onChange={this.handleChange}
type="checkbox"
ref="checkboxCollapsible"/>
{/* PLEASE LEAVE THE SPACE BETWEEN LABEL and this.props.label */}
<span className="checkbox"> {this.props.checkboxLabel}</span>
</div>
</OverlayTrigger>
<Panel
collapsible
expanded={this.state.show}
className="bs-custom-panel">
<div className="ascribe-property">
{this.renderChildren()}
</div>
</Panel>
</div>
);
}
});
export default PropertyCollapsile;

View File

@ -55,7 +55,7 @@ let ContractSettingsUpdateButton = React.createClass({
render() { render() {
return ( return (
<ReactS3FineUploader <ReactS3FineUploader
fileInputElement={UploadButton} fileInputElement={UploadButton()}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'contract' fileClass: 'contract'

View File

@ -5,22 +5,31 @@ import React from 'react';
import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image'; import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image';
import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other'; import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other';
import { getLangText } from '../../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
import { truncateTextAtCharIndex } from '../../../utils/general_utils';
import { extractFileExtensionFromString } from '../../../utils/file_utils';
let FileDragAndDropPreview = React.createClass({
const { shape, string, number, func, bool } = React.PropTypes;
const FileDragAndDropPreview = React.createClass({
propTypes: { propTypes: {
file: React.PropTypes.shape({ file: shape({
url: React.PropTypes.string, url: string,
type: React.PropTypes.string type: string,
progress: number,
id: number,
status: string,
s3Url: string,
s3UrlSafe: string
}).isRequired, }).isRequired,
handleDeleteFile: React.PropTypes.func, handleDeleteFile: func,
handleCancelFile: React.PropTypes.func, handleCancelFile: func,
handlePauseFile: React.PropTypes.func, handlePauseFile: func,
handleResumeFile: React.PropTypes.func, handleResumeFile: func,
areAssetsDownloadable: React.PropTypes.bool, areAssetsDownloadable: bool,
areAssetsEditable: React.PropTypes.bool areAssetsEditable: bool,
numberOfDisplayedFiles: number
}, },
toggleUploadProcess() { toggleUploadProcess() {
@ -32,14 +41,21 @@ let FileDragAndDropPreview = React.createClass({
}, },
handleDeleteFile() { handleDeleteFile() {
// handleDeleteFile is optional, so if its not submitted, const { handleDeleteFile,
// don't run it handleCancelFile,
// On the other hand, if the files progress is not yet at a 100%, file } = this.props;
// just run fineuploader.cancel // `handleDeleteFile` is optional, so if its not submitted, don't run it
if(this.props.handleDeleteFile && this.props.file.progress === 100) { //
this.props.handleDeleteFile(this.props.file.id); // For delete though, we only want to trigger it, when we're sure that
} else if(this.props.handleCancelFile && this.props.file.progress !== 100) { // the file has *completely* been uploaded to S3 and call now also be
this.props.handleCancelFile(this.props.file.id); // deleted using an HTTP DELETE request.
if (handleDeleteFile &&
file.progress === 100 &&
(file.status === 'upload successful' || file.status === 'online') &&
file.s3UrlSafe) {
handleDeleteFile(file.id);
} else if(handleCancelFile) {
handleCancelFile(file.id);
} }
}, },
@ -50,45 +66,81 @@ let FileDragAndDropPreview = React.createClass({
} }
}, },
render() { getFileName() {
let previewElement; const { numberOfDisplayedFiles, file } = this.props;
let removeBtn;
// Decide whether an image or a placeholder picture should be displayed if(numberOfDisplayedFiles === 1) {
if(this.props.file.type.split('/')[0] === 'image') { return (
previewElement = (<FileDragAndDropPreviewImage <span className="file-name">
onClick={this.handleDeleteFile} {truncateTextAtCharIndex(file.name, 30, '(...).' + extractFileExtensionFromString(file.name))}
progress={this.props.file.progress} </span>
url={this.props.file.url} );
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable}
downloadUrl={this.props.file.s3UrlSafe}/>);
} else { } else {
previewElement = (<FileDragAndDropPreviewOther return null;
onClick={this.handleDeleteFile}
progress={this.props.file.progress}
type={this.props.file.type.split('/')[1]}
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={this.props.areAssetsDownloadable}
downloadUrl={this.props.file.s3UrlSafe}/>);
} }
},
getRemoveButton() {
if(this.props.areAssetsEditable) { if(this.props.areAssetsEditable) {
removeBtn = (<div className="delete-file"> return (
<div className="delete-file">
<span <span
className="glyphicon glyphicon-remove text-center" className="glyphicon glyphicon-remove text-center"
aria-hidden="true" aria-hidden="true"
title={getLangText('Remove file')} title={getLangText('Remove file')}
onClick={this.handleDeleteFile}/> onClick={this.handleDeleteFile}/>
</div>); </div>
);
} else {
return null;
}
},
render() {
const { file,
areAssetsDownloadable,
numberOfDisplayedFiles } = this.props;
const innerStyle = numberOfDisplayedFiles === 1 ? { verticalAlign: 'middle' } : null;
const outerStyle = numberOfDisplayedFiles !== 1 ? { display: 'inline-block' } : null;
let previewElement;
// Decide whether an image or a placeholder picture should be displayed
// If a file has its `thumbnailUrl` defined, then we display it also as an image
if(file.type.split('/')[0] === 'image' || file.thumbnailUrl) {
previewElement = (
<FileDragAndDropPreviewImage
onClick={this.handleDeleteFile}
progress={file.progress}
url={file.thumbnailUrl || file.url}
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={areAssetsDownloadable}
downloadUrl={file.s3UrlSafe}
showProgress={numberOfDisplayedFiles > 1} />
);
} else {
previewElement = (
<FileDragAndDropPreviewOther
onClick={this.handleDeleteFile}
progress={file.progress}
type={file.type.split('/')[1]}
toggleUploadProcess={this.toggleUploadProcess}
areAssetsDownloadable={areAssetsDownloadable}
downloadUrl={file.s3UrlSafe}
showProgress={numberOfDisplayedFiles > 1} />
);
} }
return ( return (
<div style={outerStyle}>
<div <div
style={innerStyle}
className="file-drag-and-drop-position"> className="file-drag-and-drop-position">
{removeBtn} {this.getRemoveButton()}
{previewElement} {previewElement}
</div> </div>
{this.getFileName()}
</div>
); );
} }
}); });

View File

@ -3,16 +3,21 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AclProxy from '../../acl_proxy';
import AscribeSpinner from '../../ascribe_spinner'; import AscribeSpinner from '../../ascribe_spinner';
import { getLangText } from '../../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewImage = React.createClass({
const { number, string, func, bool } = React.PropTypes;
const FileDragAndDropPreviewImage = React.createClass({
propTypes: { propTypes: {
progress: React.PropTypes.number, progress: number,
url: React.PropTypes.string, url: string,
toggleUploadProcess: React.PropTypes.func, toggleUploadProcess: func,
downloadUrl: React.PropTypes.string, downloadUrl: string,
areAssetsDownloadable: React.PropTypes.bool areAssetsDownloadable: bool,
showProgress: bool
}, },
getInitialState() { getInitialState() {
@ -33,40 +38,44 @@ let FileDragAndDropPreviewImage = React.createClass({
}, },
render() { render() {
let imageStyle = { const { url,
backgroundImage: 'url("' + this.props.url + '")', progress,
areAssetsDownloadable,
downloadUrl,
showProgress } = this.props;
const imageStyle = {
backgroundImage: 'url("' + url + '")',
backgroundSize: 'cover' backgroundSize: 'cover'
}; };
let actionSymbol; let actionSymbol;
if(this.props.progress > 0 && this.props.progress < 99 && this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-pause action-file" aria-hidden="true" title={getLangText('Pause upload')} onClick={this.toggleUploadProcess}/>;
} else if(this.props.progress > 0 && this.props.progress < 99 && !this.state.paused) {
actionSymbol = <span className="glyphicon glyphicon-play action-file" aria-hidden="true" title={getLangText('Resume uploading')} onClick={this.toggleUploadProcess}/>;
} else if(this.props.progress === 100) {
// only if assets are actually downloadable, there should be a download icon if the process is already at // only if assets are actually downloadable, there should be a download icon if the process is already at
// 100%. If not, no actionSymbol should be displayed // 100%. If not, no actionSymbol should be displayed
if(this.props.areAssetsDownloadable) { if(progress === 100 && areAssetsDownloadable) {
actionSymbol = <a href={this.props.downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title={getLangText('Download file')}/>; actionSymbol = <a href={downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title={getLangText('Download file')}/>;
} } else if(progress >= 0 && progress < 100) {
} else {
actionSymbol = ( actionSymbol = (
<div className="spinner-file"> <div className="spinner-file">
<AscribeSpinner color='dark-blue' size='md' /> <AscribeSpinner color='dark-blue' size='md' />
</div> </div>
); );
} else {
actionSymbol = (
<span className='ascribe-icon icon-ascribe-ok action-file'/>
);
} }
return ( return (
<div <div
className="file-drag-and-drop-preview-image" className="file-drag-and-drop-preview-image"
style={imageStyle}> style={imageStyle}>
<AclProxy
show={showProgress}>
<ProgressBar <ProgressBar
now={Math.ceil(this.props.progress)} now={Math.ceil(progress)}
className="ascribe-progress-bar ascribe-progress-bar-xs"/> className="ascribe-progress-bar ascribe-progress-bar-xs"/>
</AclProxy>
{actionSymbol} {actionSymbol}
</div> </div>
); );

View File

@ -29,13 +29,11 @@ let FileDragAndDropPreviewIterator = React.createClass({
areAssetsDownloadable, areAssetsDownloadable,
areAssetsEditable areAssetsEditable
} = this.props; } = this.props;
files = files.filter(displayValidFilesFilter); files = files.filter(displayValidFilesFilter);
if(files && files.length > 0) { if(files && files.length > 0) {
return ( return (
<div className="file-drag-and-drop-preview-iterator"> <div>
<div className="file-drag-and-drop-preview-iterator-spacing">
{files.map((file, i) => { {files.map((file, i) => {
return ( return (
<FileDragAndDropPreview <FileDragAndDropPreview
@ -46,10 +44,10 @@ let FileDragAndDropPreviewIterator = React.createClass({
handlePauseFile={handlePauseFile} handlePauseFile={handlePauseFile}
handleResumeFile={handleResumeFile} handleResumeFile={handleResumeFile}
areAssetsDownloadable={areAssetsDownloadable} areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={areAssetsEditable}/> areAssetsEditable={areAssetsEditable}
numberOfDisplayedFiles={files.length}/>
); );
})} })}
</div>
<FileDragAndDropPreviewProgress files={files} /> <FileDragAndDropPreviewProgress files={files} />
</div> </div>
); );

View File

@ -3,16 +3,21 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AclProxy from '../../acl_proxy';
import AscribeSpinner from '../../ascribe_spinner'; import AscribeSpinner from '../../ascribe_spinner';
import { getLangText } from '../../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewOther = React.createClass({
const { string, number, bool, func } = React.PropTypes;
const FileDragAndDropPreviewOther = React.createClass({
propTypes: { propTypes: {
type: React.PropTypes.string, type: string,
progress: React.PropTypes.number, progress: number,
areAssetsDownloadable: React.PropTypes.bool, areAssetsDownloadable: bool,
toggleUploadProcess: React.PropTypes.func, toggleUploadProcess: func,
downloadUrl: React.PropTypes.string downloadUrl: string,
showProgress: bool
}, },
getInitialState() { getInitialState() {
@ -33,39 +38,49 @@ let FileDragAndDropPreviewOther = React.createClass({
}, },
render() { render() {
const { progress,
areAssetsDownloadable,
downloadUrl,
type,
showProgress } = this.props;
const style = !showProgress ? { visibility: 'hidden' } : null;
let actionSymbol; let actionSymbol;
if(this.props.progress > 0 && this.props.progress < 99 && this.state.paused) { // only if assets are actually downloadable, there should be a
actionSymbol = <span className="glyphicon glyphicon-pause action-file" aria-hidden="true" title={getLangText('Pause upload')} onClick={this.toggleUploadProcess}/>; // download icon if the process is already at 100%.
} else if(this.props.progress > 0 && this.props.progress < 99 && !this.state.paused) { // If not, no actionSymbol should be displayed
actionSymbol = <span className="glyphicon glyphicon-play action-file" aria-hidden="true" title={getLangText('Resume uploading')} onClick={this.toggleUploadProcess}/>; if (progress === 100 && areAssetsDownloadable) {
} else if(this.props.progress === 100) { actionSymbol = (
<a
// only if assets are actually downloadable, there should be a download icon if the process is already at href={downloadUrl}
// 100%. If not, no actionSymbol should be displayed target="_blank"
if(this.props.areAssetsDownloadable) { className="glyphicon glyphicon-download action-file"
actionSymbol = <a href={this.props.downloadUrl} target="_blank" className="glyphicon glyphicon-download action-file" aria-hidden="true" title={getLangText('Download file')}/>; aria-hidden="true"
} title={getLangText('Download file')}/>
);
} else { } else if(progress >= 0 && progress < 100) {
actionSymbol = ( actionSymbol = (
<div className="spinner-file"> <div className="spinner-file">
<AscribeSpinner color='dark-blue' size='md' /> <AscribeSpinner color='dark-blue' size='md' />
</div> </div>
); );
} else {
actionSymbol = (
<span className='ascribe-icon icon-ascribe-ok action-file'/>
);
} }
return ( return (
<div <div
className="file-drag-and-drop-preview"> className="file-drag-and-drop-preview">
<ProgressBar <ProgressBar
now={Math.ceil(this.props.progress)} now={Math.ceil(progress)}
style={style}
className="ascribe-progress-bar ascribe-progress-bar-xs"/> className="ascribe-progress-bar ascribe-progress-bar-xs"/>
<div className="file-drag-and-drop-preview-table-wrapper"> <div className="file-drag-and-drop-preview-table-wrapper">
<div className="file-drag-and-drop-preview-other"> <div className="file-drag-and-drop-preview-other">
{actionSymbol} {actionSymbol}
<p>{'.' + this.props.type}</p> <p style={style}>{'.' + type}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,10 +5,9 @@ import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewProgress = React.createClass({ const FileDragAndDropPreviewProgress = React.createClass({
propTypes: { propTypes: {
files: React.PropTypes.array files: React.PropTypes.array
}, },
@ -40,24 +39,18 @@ let FileDragAndDropPreviewProgress = React.createClass({
}, },
render() { render() {
const files = this.props.files.filter(displayValidProgressFilesFilter);
const style = !files.length ? { display: 'none' } : null;
let overallProgress = this.calcOverallProgress(); let overallProgress = this.calcOverallProgress();
let overallFileSize = this.calcOverallFileSize();
let style = {
visibility: 'hidden'
};
// only visible if overallProgress is over zero
// or the overallFileSize is greater than 10MB
if(overallProgress !== 0 && overallFileSize > 10000000) {
style.visibility = 'visible';
}
return ( return (
<div style={{marginTop: '1.3em'}}>
<ProgressBar <ProgressBar
now={Math.ceil(overallProgress)} now={Math.ceil(overallProgress)}
label={getLangText('Overall progress%s', ': %(percent)s%')} label={'%(percent)s%'}
className="ascribe-progress-bar" className="ascribe-progress-bar"
style={style} /> style={style} />
</div>
); );
} }
}); });

View File

@ -2,15 +2,17 @@
import React from 'react'; import React from 'react';
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils'; import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
import { truncateTextAtCharIndex } from '../../../utils/general_utils'; import { truncateTextAtCharIndex } from '../../../utils/general_utils';
const { func, array, bool, shape, string } = React.PropTypes; const { func, array, bool, shape, string } = React.PropTypes;
let UploadButton = React.createClass({
export default function UploadButton({ className = 'btn btn-default btn-sm' } = {}) {
return React.createClass({
displayName: 'UploadButton',
propTypes: { propTypes: {
onDrop: func.isRequired, onDrop: func.isRequired,
filesToUpload: array, filesToUpload: array,
@ -25,7 +27,9 @@ let UploadButton = React.createClass({
allowedExtensions: string, allowedExtensions: string,
handleCancelFile: func // provided by ReactS3FineUploader // provided by ReactS3FineUploader
handleCancelFile: func,
handleDeleteFile: func
}, },
handleDrop(event) { handleDrop(event) {
@ -36,7 +40,6 @@ let UploadButton = React.createClass({
if(typeof this.props.onDrop === 'function' && files) { if(typeof this.props.onDrop === 'function' && files) {
this.props.onDrop(files); this.props.onDrop(files);
} }
}, },
getUploadingFiles() { getUploadingFiles() {
@ -71,6 +74,11 @@ let UploadButton = React.createClass({
} }
}, },
onClickRemove() {
const uploadedFile = this.getUploadedFile();
this.props.handleDeleteFile(uploadedFile.id);
},
getButtonLabel() { getButtonLabel() {
let { filesToUpload, fileClassToUpload } = this.props; let { filesToUpload, fileClassToUpload } = this.props;
@ -90,8 +98,9 @@ let UploadButton = React.createClass({
if(uploadedFile) { if(uploadedFile) {
return ( return (
<span> <span>
<Glyphicon glyph="ok" /> <span className='ascribe-icon icon-ascribe-ok'/>
{' ' + truncateTextAtCharIndex(uploadedFile.name, 40)} {' ' + truncateTextAtCharIndex(uploadedFile.name, 40) + ' '}
[<a onClick={this.onClickRemove}>{getLangText('remove')}</a>]
</span> </span>
); );
} else { } else {
@ -115,7 +124,7 @@ let UploadButton = React.createClass({
<div className="upload-button-wrapper"> <div className="upload-button-wrapper">
<a <a
onClick={this.handleOnClick} onClick={this.handleOnClick}
className="btn btn-default btn-sm margin-left-2px" className={className}
disabled={this.getUploadingFiles().length !== 0}> disabled={this.getUploadingFiles().length !== 0}>
{this.getButtonLabel()} {this.getButtonLabel()}
<input <input
@ -135,5 +144,4 @@ let UploadButton = React.createClass({
); );
} }
}); });
}
export default UploadButton;

View File

@ -18,88 +18,101 @@ import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp }
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let ReactS3FineUploader = React.createClass({
const { shape,
string,
oneOfType,
number,
func,
bool,
any,
object,
oneOf,
element,
arrayOf } = React.PropTypes;
const ReactS3FineUploader = React.createClass({
propTypes: { propTypes: {
keyRoutine: React.PropTypes.shape({ keyRoutine: shape({
url: React.PropTypes.string, url: string,
fileClass: React.PropTypes.string, fileClass: string,
pieceId: React.PropTypes.oneOfType([ pieceId: oneOfType([
React.PropTypes.string, string,
React.PropTypes.number number
]) ])
}), }),
createBlobRoutine: React.PropTypes.shape({ createBlobRoutine: shape({
url: React.PropTypes.string, url: string,
pieceId: React.PropTypes.oneOfType([ pieceId: oneOfType([
React.PropTypes.string, string,
React.PropTypes.number number
]) ])
}), }),
submitFile: React.PropTypes.func, handleChangedFile: func, // is for when a file is dropped or selected
autoUpload: React.PropTypes.bool, submitFile: func, // is for when a file has been successfully uploaded, TODO: rename to handleSubmitFile
debug: React.PropTypes.bool, autoUpload: bool,
objectProperties: React.PropTypes.shape({ debug: bool,
acl: React.PropTypes.string objectProperties: shape({
acl: string
}), }),
request: React.PropTypes.shape({ request: shape({
endpoint: React.PropTypes.string, endpoint: string,
accessKey: React.PropTypes.string, accessKey: string,
params: React.PropTypes.shape({ params: shape({
csrfmiddlewaretoken: React.PropTypes.string csrfmiddlewaretoken: string
}) })
}), }),
signature: React.PropTypes.shape({ signature: shape({
endpoint: React.PropTypes.string endpoint: string
}).isRequired, }).isRequired,
uploadSuccess: React.PropTypes.shape({ uploadSuccess: shape({
method: React.PropTypes.string, method: string,
endpoint: React.PropTypes.string, endpoint: string,
params: React.PropTypes.shape({ params: shape({
isBrowserPreviewCapable: React.PropTypes.any, // maybe fix this later isBrowserPreviewCapable: any, // maybe fix this later
bitcoin_ID_noPrefix: React.PropTypes.string bitcoin_ID_noPrefix: string
}) })
}), }),
cors: React.PropTypes.shape({ cors: shape({
expected: React.PropTypes.bool expected: bool
}), }),
chunking: React.PropTypes.shape({ chunking: shape({
enabled: React.PropTypes.bool enabled: bool
}), }),
resume: React.PropTypes.shape({ resume: shape({
enabled: React.PropTypes.bool enabled: bool
}), }),
deleteFile: React.PropTypes.shape({ deleteFile: shape({
enabled: React.PropTypes.bool, enabled: bool,
method: React.PropTypes.string, method: string,
endpoint: React.PropTypes.string, endpoint: string,
customHeaders: React.PropTypes.object customHeaders: object
}).isRequired, }).isRequired,
session: React.PropTypes.shape({ session: shape({
customHeaders: React.PropTypes.object, customHeaders: object,
endpoint: React.PropTypes.string, endpoint: string,
params: React.PropTypes.object, params: object,
refreshOnRequests: React.PropTypes.bool refreshOnRequests: bool
}), }),
validation: React.PropTypes.shape({ validation: shape({
itemLimit: React.PropTypes.number, itemLimit: number,
sizeLimit: React.PropTypes.string, sizeLimit: string,
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string) allowedExtensions: arrayOf(string)
}), }),
messages: React.PropTypes.shape({ messages: shape({
unsupportedBrowser: React.PropTypes.string unsupportedBrowser: string
}), }),
formatFileName: React.PropTypes.func, formatFileName: func,
multiple: React.PropTypes.bool, multiple: bool,
retry: React.PropTypes.shape({ retry: shape({
enableAuto: React.PropTypes.bool enableAuto: bool
}), }),
uploadStarted: React.PropTypes.func, setIsUploadReady: func,
setIsUploadReady: React.PropTypes.func, isReadyForFormSubmission: func,
isReadyForFormSubmission: React.PropTypes.func, areAssetsDownloadable: bool,
areAssetsDownloadable: React.PropTypes.bool, areAssetsEditable: bool,
areAssetsEditable: React.PropTypes.bool, defaultErrorMessage: string,
defaultErrorMessage: React.PropTypes.string, onInactive: func,
onInactive: React.PropTypes.func,
// We encountered some cases where people had difficulties to upload their // We encountered some cases where people had difficulties to upload their
// works to ascribe due to a slow internet connection. // works to ascribe due to a slow internet connection.
@ -112,22 +125,22 @@ let ReactS3FineUploader = React.createClass({
// which should be passed into 'uploadMethod': // which should be passed into 'uploadMethod':
// 'hash': upload using the hash // 'hash': upload using the hash
// 'upload': upload full file (default if not specified) // 'upload': upload full file (default if not specified)
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: bool,
uploadMethod: React.PropTypes.oneOf(['hash', 'upload']), uploadMethod: oneOf(['hash', 'upload']),
// A class of a file the user has to upload // A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural // Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({ fileClassToUpload: shape({
singular: React.PropTypes.string, singular: string,
plural: React.PropTypes.string plural: string
}), }),
// Uploading functionality of react fineuploader is disconnected from its UI // Uploading functionality of react fineuploader is disconnected from its UI
// layer, which means that literally every (properly adjusted) react element // layer, which means that literally every (properly adjusted) react element
// can handle the UI handling. // can handle the UI handling.
fileInputElement: React.PropTypes.oneOfType([ fileInputElement: oneOfType([
React.PropTypes.func, func,
React.PropTypes.element element
]) ])
}, },
@ -376,6 +389,19 @@ let ReactS3FineUploader = React.createClass({
}); });
}, },
setThumbnailForFileId(fileId, url) {
const { filesToUpload } = this.state;
if(fileId < filesToUpload.length) {
const changeSet = { $set: url };
const newFilesToUpload = React.addons.update(filesToUpload, { [fileId]: { thumbnailUrl: changeSet } });
this.setState({ filesToUpload: newFilesToUpload });
} else {
throw new Error("You're accessing an index out of range of filesToUpload");
}
},
/* FineUploader specific callback function handlers */ /* FineUploader specific callback function handlers */
onUploadChunk(id, name, chunkData) { onUploadChunk(id, name, chunkData) {
@ -510,7 +536,12 @@ let ReactS3FineUploader = React.createClass({
onCancel(id) { onCancel(id) {
// when a upload is canceled, we need to update this components file array // when a upload is canceled, we need to update this components file array
this.setStatusOfFile(id, 'canceled'); this.setStatusOfFile(id, 'canceled')
.then(() => {
if(typeof this.props.handleChangedFile === 'function') {
this.props.handleChangedFile(this.state.filesToUpload[id]);
}
});
let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000); let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
@ -604,7 +635,12 @@ let ReactS3FineUploader = React.createClass({
// //
// If there is an error during the deletion, we will just change the status back to 'online' // If there is an error during the deletion, we will just change the status back to 'online'
// and display an error message // and display an error message
this.setStatusOfFile(fileId, 'deleted'); this.setStatusOfFile(fileId, 'deleted')
.then(() => {
if(typeof this.props.handleChangedFile === 'function') {
this.props.handleChangedFile(this.state.filesToUpload[fileId]);
}
});
// In some instances (when the file was already uploaded and is just displayed to the user // In some instances (when the file was already uploaded and is just displayed to the user
// - for example in the contract or additional files dialog) // - for example in the contract or additional files dialog)
@ -672,11 +708,6 @@ let ReactS3FineUploader = React.createClass({
// override standard files list with only valid files // override standard files list with only valid files
files = validFiles; files = validFiles;
// Call this method to signal the outside component that an upload is in progress
if(typeof this.props.uploadStarted === 'function' && files.length > 0) {
this.props.uploadStarted();
}
// if multiple is set to false and user drops multiple files into the dropzone, // if multiple is set to false and user drops multiple files into the dropzone,
// take the first one and notify user that only one file can be submitted // take the first one and notify user that only one file can be submitted
if(!this.props.multiple && files.length > 1) { if(!this.props.multiple && files.length > 1) {
@ -849,10 +880,25 @@ let ReactS3FineUploader = React.createClass({
// set the new file array // set the new file array
let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles }); let filesToUpload = React.addons.update(this.state.filesToUpload, { $set: oldAndNewFiles });
this.setState({ filesToUpload }); this.setState({ filesToUpload }, () => {
// when files have been dropped or selected by a user, we want to propagate that
// information to the outside components, so they can act on it (in our case, because
// we want the user to define a thumbnail when the actual work is not renderable
// (like e.g. a .zip file))
if(typeof this.props.handleChangedFile === 'function') {
// its save to assume that the last file in `filesToUpload` is always
// the latest file added
this.props.handleChangedFile(this.state.filesToUpload.slice(-1)[0]);
}
});
}, },
// This method has been made promise-based to immediately afterwards
// call a callback function (instantly after this.setState went through)
// This is e.g. needed when showing/hiding the optional thumbnail upload
// field in the registration form
setStatusOfFile(fileId, status) { setStatusOfFile(fileId, status) {
return Q.Promise((resolve) => {
let changeSet = {}; let changeSet = {};
if(status === 'deleted' || status === 'canceled') { if(status === 'deleted' || status === 'canceled') {
@ -863,7 +909,8 @@ let ReactS3FineUploader = React.createClass({
let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet }); let filesToUpload = React.addons.update(this.state.filesToUpload, { [fileId]: changeSet });
this.setState({ filesToUpload }); this.setState({ filesToUpload }, resolve);
});
}, },
isDropzoneInactive() { isDropzoneInactive() {

View File

@ -42,6 +42,15 @@ export function displayValidFilesFilter(file) {
return file.status !== 'deleted' && file.status !== 'canceled'; return file.status !== 'deleted' && file.status !== 'canceled';
} }
/**
* Filter function for filtering all files except for deleted and canceled files
* @param {object} file A file from filesToUpload that has status as a prop.
* @return {boolean}
*/
export function displayRemovedFilesFilter(file) {
return file.status === 'deleted' || file.status === 'canceled';
}
/** /**
* Filter function for which files to integrate in the progress process * Filter function for which files to integrate in the progress process

View File

@ -17,7 +17,7 @@ import UserStore from '../stores/user_store';
import GlobalNotificationModel from '../models/global_notification_model'; import GlobalNotificationModel from '../models/global_notification_model';
import GlobalNotificationActions from '../actions/global_notification_actions'; import GlobalNotificationActions from '../actions/global_notification_actions';
import PropertyCollapsible from './ascribe_forms/property_collapsible'; import Property from './ascribe_forms/property';
import RegisterPieceForm from './ascribe_forms/form_register_piece'; import RegisterPieceForm from './ascribe_forms/form_register_piece';
import { mergeOptions } from '../utils/general_utils'; import { mergeOptions } from '../utils/general_utils';
@ -96,15 +96,16 @@ let RegisterPiece = React.createClass( {
getSpecifyEditions() { getSpecifyEditions() {
if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) { if(this.state.whitelabel && this.state.whitelabel.acl_create_editions || Object.keys(this.state.whitelabel).length === 0) {
return ( return (
<PropertyCollapsible <Property
name="num_editions" name="num_editions"
checkboxLabel={getLangText('Specify editions')}> checkboxLabel={getLangText('Specify editions')}
expanded={false}>
<span>{getLangText('Editions')}</span> <span>{getLangText('Editions')}</span>
<input <input
type="number" type="number"
placeholder="(e.g. 32)" placeholder="(e.g. 32)"
min={0}/> min={0}/>
</PropertyCollapsible> </Property>
); );
} }
}, },

View File

@ -253,7 +253,7 @@ const PRRegisterPieceForm = React.createClass({
name="digitalWorkKey" name="digitalWorkKey"
label={getLangText('Select the PDF with your work')}> label={getLangText('Select the PDF with your work')}>
<InputFineuploader <InputFineuploader
fileInputElement={UploadButton} fileInputElement={UploadButton()}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
setIsUploadReady={this.setIsUploadReady('digitalWorkKeyReady')} setIsUploadReady={this.setIsUploadReady('digitalWorkKeyReady')}
createBlobRoutine={{ createBlobRoutine={{
@ -279,7 +279,7 @@ const PRRegisterPieceForm = React.createClass({
name="thumbnailKey" name="thumbnailKey"
label={getLangText('Featured Cover photo')}> label={getLangText('Featured Cover photo')}>
<InputFineuploader <InputFineuploader
fileInputElement={UploadButton} fileInputElement={UploadButton()}
createBlobRoutine={{ createBlobRoutine={{
url: ApiUrls.blob_thumbnails url: ApiUrls.blob_thumbnails
}} }}
@ -305,7 +305,7 @@ const PRRegisterPieceForm = React.createClass({
name="supportingMaterials" name="supportingMaterials"
label={getLangText('Supporting Materials (Optional)')}> label={getLangText('Supporting Materials (Optional)')}>
<InputFineuploader <InputFineuploader
fileInputElement={UploadButton} fileInputElement={UploadButton()}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
setIsUploadReady={this.setIsUploadReady('supportingMaterialsReady')} setIsUploadReady={this.setIsUploadReady('supportingMaterialsReady')}
createBlobRoutine={this.getCreateBlobRoutine()} createBlobRoutine={this.getCreateBlobRoutine()}
@ -327,7 +327,7 @@ const PRRegisterPieceForm = React.createClass({
name="proofOfPayment" name="proofOfPayment"
label={getLangText('Proof of payment')}> label={getLangText('Proof of payment')}>
<InputFineuploader <InputFineuploader
fileInputElement={UploadButton} fileInputElement={UploadButton()}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
setIsUploadReady={this.setIsUploadReady('proofOfPaymentReady')} setIsUploadReady={this.setIsUploadReady('proofOfPaymentReady')}
createBlobRoutine={this.getCreateBlobRoutine()} createBlobRoutine={this.getCreateBlobRoutine()}

View File

@ -8,6 +8,7 @@ import PieceSubmitToPrizeForm from '../../../../../ascribe_forms/form_submit_to_
import { getLangText } from '../../../../../../utils/lang_utils'; import { getLangText } from '../../../../../../utils/lang_utils';
let SubmitToPrizeButton = React.createClass({ let SubmitToPrizeButton = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string, className: React.PropTypes.string,
@ -21,8 +22,8 @@ let SubmitToPrizeButton = React.createClass({
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Submitted to prize')} <span className="glyphicon glyphicon-ok" {getLangText('Submitted to prize') + ' '}
aria-hidden="true"></span> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
); );
} }

View File

@ -76,8 +76,8 @@ let CylandAccordionListItem = React.createClass({
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Submitted to Cyland')} <span className="glyphicon glyphicon-ok" {getLangText('Submitted to Cyland') + ' '}
aria-hidden="true"></span> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
@ -86,8 +86,8 @@ let CylandAccordionListItem = React.createClass({
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Loaned to Cyland')} <span className="glyphicon glyphicon-ok" {getLangText('Loaned to Cyland') + ' '}
aria-hidden="true"></span> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
</div> </div>

View File

@ -64,12 +64,6 @@ let CylandAdditionalDataForm = React.createClass({
}, },
uploadStarted() {
this.setState({
isUploadReady: false
});
},
setIsUploadReady(isReady) { setIsUploadReady(isReady) {
this.setState({ this.setState({
isUploadReady: isReady isUploadReady: isReady
@ -122,7 +116,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='artist_bio' name='artist_bio'
label={getLangText('Artist Biography')} label={getLangText('Artist Biography')}
hidden={disabled && !piece.extra_data.artist_bio}> expanded={!disabled && piece.extra_data.artist_bio}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.artist_bio} defaultValue={piece.extra_data.artist_bio}
@ -131,7 +125,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='artist_contact_information' name='artist_contact_information'
label={getLangText('Artist Contact Information')} label={getLangText('Artist Contact Information')}
hidden={disabled && !piece.extra_data.artist_contact_information}> expanded={!disabled && piece.extra_data.artist_contact_information}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.artist_contact_information} defaultValue={piece.extra_data.artist_contact_information}
@ -140,7 +134,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='conceptual_overview' name='conceptual_overview'
label={getLangText('Conceptual Overview')} label={getLangText('Conceptual Overview')}
hidden={disabled && !piece.extra_data.conceptual_overview}> expanded={!disabled && piece.extra_data.conceptual_overview}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.conceptual_overview} defaultValue={piece.extra_data.conceptual_overview}
@ -149,7 +143,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='medium' name='medium'
label={getLangText('Medium (technical specifications)')} label={getLangText('Medium (technical specifications)')}
hidden={disabled && !piece.extra_data.medium}> expanded={!disabled && piece.extra_data.medium}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.medium} defaultValue={piece.extra_data.medium}
@ -158,7 +152,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='size_duration' name='size_duration'
label={getLangText('Size / Duration')} label={getLangText('Size / Duration')}
hidden={disabled && !piece.extra_data.size_duration}> expanded={!disabled && piece.extra_data.size_duration}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.size_duration} defaultValue={piece.extra_data.size_duration}
@ -167,7 +161,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='display_instructions' name='display_instructions'
label={getLangText('Display instructions')} label={getLangText('Display instructions')}
hidden={disabled && !piece.extra_data.display_instructions}> expanded={!disabled && piece.extra_data.display_instructions}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.display_instructions} defaultValue={piece.extra_data.display_instructions}
@ -176,7 +170,7 @@ let CylandAdditionalDataForm = React.createClass({
<Property <Property
name='additional_details' name='additional_details'
label={getLangText('Additional details')} label={getLangText('Additional details')}
hidden={disabled && !piece.extra_data.additional_details}> expanded={!disabled && piece.extra_data.additional_details}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={piece.extra_data.additional_details} defaultValue={piece.extra_data.additional_details}
@ -184,7 +178,6 @@ let CylandAdditionalDataForm = React.createClass({
</Property> </Property>
<FurtherDetailsFileuploader <FurtherDetailsFileuploader
label={getLangText('Additional files (e.g. still images, pdf)')} label={getLangText('Additional files (e.g. still images, pdf)')}
uploadStarted={this.uploadStarted}
submitFile={this.submitFile} submitFile={this.submitFile}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.fileOptional} isReadyForFormSubmission={formSubmissionValidation.fileOptional}

View File

@ -81,8 +81,8 @@ let IkonotvAccordionListItem = React.createClass({
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Submitted to IkonoTV')} <span className="glyphicon glyphicon-ok" {getLangText('Submitted to IkonoTV') + ' '}
aria-hidden="true"></span> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
<AclProxy <AclProxy
@ -91,8 +91,8 @@ let IkonotvAccordionListItem = React.createClass({
<button <button
disabled disabled
className="btn btn-default btn-xs pull-right"> className="btn btn-default btn-xs pull-right">
{getLangText('Loaned to IkonoTV')} <span className="glyphicon glyphicon-ok" {getLangText('Loaned to IkonoTV') + ' '}
aria-hidden="true"></span> <span className='ascribe-icon icon-ascribe-ok'/>
</button> </button>
</AclProxy> </AclProxy>
</div> </div>

View File

@ -104,7 +104,7 @@ let IkonotvArtistDetailsForm = React.createClass({
<Property <Property
name='artist_website' name='artist_website'
label={getLangText('Artist Website')} label={getLangText('Artist Website')}
hidden={this.props.disabled && !this.props.piece.extra_data.artist_website}> expanded={!this.props.disabled && this.props.piece.extra_data.artist_website}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.artist_website} defaultValue={this.props.piece.extra_data.artist_website}
@ -113,7 +113,7 @@ let IkonotvArtistDetailsForm = React.createClass({
<Property <Property
name='gallery_website' name='gallery_website'
label={getLangText('Website of related Gallery, Museum, etc.')} label={getLangText('Website of related Gallery, Museum, etc.')}
hidden={this.props.disabled && !this.props.piece.extra_data.gallery_website}> expanded={!this.props.disabled && this.props.piece.extra_data.gallery_website}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.gallery_website} defaultValue={this.props.piece.extra_data.gallery_website}
@ -122,7 +122,7 @@ let IkonotvArtistDetailsForm = React.createClass({
<Property <Property
name='additional_websites' name='additional_websites'
label={getLangText('Additional Websites/Publications/Museums/Galleries')} label={getLangText('Additional Websites/Publications/Museums/Galleries')}
hidden={this.props.disabled && !this.props.piece.extra_data.additional_websites}> expanded={!this.props.disabled && this.props.piece.extra_data.additional_websites}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.additional_websites} defaultValue={this.props.piece.extra_data.additional_websites}
@ -131,7 +131,7 @@ let IkonotvArtistDetailsForm = React.createClass({
<Property <Property
name='conceptual_overview' name='conceptual_overview'
label={getLangText('Short text about the Artist')} label={getLangText('Short text about the Artist')}
hidden={this.props.disabled && !this.props.piece.extra_data.conceptual_overview}> expanded={!this.props.disabled && this.props.piece.extra_data.conceptual_overview}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.conceptual_overview} defaultValue={this.props.piece.extra_data.conceptual_overview}

View File

@ -103,7 +103,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='medium' name='medium'
label={getLangText('Medium')} label={getLangText('Medium')}
hidden={this.props.disabled && !this.props.piece.extra_data.medium}> expanded={!this.props.disabled && this.props.piece.extra_data.medium}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.medium} defaultValue={this.props.piece.extra_data.medium}
@ -112,7 +112,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='size_duration' name='size_duration'
label={getLangText('Size/Duration')} label={getLangText('Size/Duration')}
hidden={this.props.disabled && !this.props.piece.extra_data.size_duration}> expanded={!this.props.disabled && this.props.piece.extra_data.size_duration}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.size_duration} defaultValue={this.props.piece.extra_data.size_duration}
@ -121,7 +121,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='copyright' name='copyright'
label={getLangText('Copyright')} label={getLangText('Copyright')}
hidden={this.props.disabled && !this.props.piece.extra_data.copyright}> expanded={!this.props.disabled && this.props.piece.extra_data.copyright}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.copyright} defaultValue={this.props.piece.extra_data.copyright}
@ -130,7 +130,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='courtesy_of' name='courtesy_of'
label={getLangText('Courtesy of')} label={getLangText('Courtesy of')}
hidden={this.props.disabled && !this.props.piece.extra_data.courtesy_of}> expanded={!this.props.disabled && this.props.piece.extra_data.courtesy_of}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.courtesy_of} defaultValue={this.props.piece.extra_data.courtesy_of}
@ -139,7 +139,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='copyright_of_photography' name='copyright_of_photography'
label={getLangText('Copyright of Photography')} label={getLangText('Copyright of Photography')}
hidden={this.props.disabled && !this.props.piece.extra_data.copyright_of_photography}> expanded={!this.props.disabled && this.props.piece.extra_data.copyright_of_photography}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.copyright_of_photography} defaultValue={this.props.piece.extra_data.copyright_of_photography}
@ -148,7 +148,7 @@ let IkonotvArtworkDetailsForm = React.createClass({
<Property <Property
name='additional_details' name='additional_details'
label={getLangText('Additional Details about the artwork')} label={getLangText('Additional Details about the artwork')}
hidden={this.props.disabled && !this.props.piece.extra_data.additional_details}> expanded={!this.props.disabled && this.props.piece.extra_data.additional_details}>
<InputTextAreaToggable <InputTextAreaToggable
rows={1} rows={1}
defaultValue={this.props.piece.extra_data.additional_details} defaultValue={this.props.piece.extra_data.additional_details}

View File

@ -85,8 +85,18 @@ const constants = {
'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART', 'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART',
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV', 'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'], 'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'],
'searchThreshold': 500, 'searchThreshold': 500,
'supportedThumbnailFileFormats': [
'x-sgi-movie', 'x-msvideo', 'quicktime', 'mpeg', 'png', 'jpeg', 'gif',
'ogg', 'oga', 'ogv', 'ogx', 'wmv', 'wma', 'flv', '3gpp2', '3p2', '3pg',
'png', 'jpg', 'jpeg', 'gif', '264', '3g', '3g2', '3gp', '3gp2', '3gpp',
'mp4', 'm4a', 'm4v', 'f4v', 'f4a', 'm4b', 'm4r', 'f4b', 'mov', 'quicktime',
'webm', 'x264', 'mpeg', 'mpeg4', 'mpg4', 'bmp', 'eps', 'jp2', 'j2k', 'jpm',
'mj2'
],
// in case of whitelabel customization, we store stuff here // in case of whitelabel customization, we store stuff here
'whitelabel': {}, 'whitelabel': {},

View File

@ -85,3 +85,15 @@ export function computeHashOfFile(file) {
loadNext(); loadNext();
}); });
} }
/**
* Extracts a file extension from a string, by splitting by dot and taking
* the last substring
* @param {string} s file's name + extension
* @return {string} file extension
*
* Via: http://stackoverflow.com/a/190878/1263876
*/
export function extractFileExtensionFromString(s) {
return s.split('.').pop();
}

View File

@ -1,14 +1,3 @@
@font-face {
font-family: 'ascribe';
src: url('#{$BASE_URL}static/fonts/ascribe.eot?-oi6ttk');
src: url('#{$BASE_URL}static/fonts/ascribe.eot?#iefix-oi6ttk') format('embedded-opentype'),
url('#{$BASE_URL}static/fonts/ascribe.woff?-oi6ttk') format('woff'),
url('#{$BASE_URL}static/fonts/ascribe.ttf?-oi6ttk') format('truetype'),
url('#{$BASE_URL}static/fonts/ascribe.svg?-oi6ttk#ascribe') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-ascribe-"], [class*=" icon-ascribe-"] { [class^="icon-ascribe-"], [class*=" icon-ascribe-"] {
font-family: 'ascribe-logo'; font-family: 'ascribe-logo';
speak: none; speak: none;
@ -22,6 +11,20 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
/*
These glyphs are generated using: https://icomoon.io
Even though it seems radically complicated, check out the site,
its fairly straight forward.
If someone wants you to add a new glyph go to the site,
drop in the regular ascribe-logo font and select all icons.
Then also add the new glyph, name and address it correctly and download
the font again.
Once you replace all the fonts they gave you, you're done.
*/
@font-face { @font-face {
font-family: 'ascribe-logo'; font-family: 'ascribe-logo';
src:url('#{$BASE_URL}static/fonts/ascribe-logo.eot?q6qoae'); src:url('#{$BASE_URL}static/fonts/ascribe-logo.eot?q6qoae');
@ -78,170 +81,9 @@
content: "\e808"; content: "\e808";
} }
.icon-ascribe-ok:before {
content: "\e809";
.glyph-ascribe-logo-spool:before { font-size: .7em;
content: "\e600";
}
.glyph-ascribe-spool:before {
content: "\e601";
}
.glyph-ascribe-logo-spool-chunked:before {
content: "\e602";
}
.glyph-ascribe-spool-chunked:before {
content: "\e603";
}
.glyph-ascribe-home:before {
content: "\e900";
}
.glyph-ascribe-home2:before {
content: "\e901";
}
.glyph-ascribe-home3:before {
content: "\e902";
}
.glyph-ascribe-pencil:before {
content: "\e905";
}
.glyph-ascribe-pencil2:before {
content: "\e906";
}
.glyph-ascribe-quill:before {
content: "\e907";
}
.glyph-ascribe-image:before {
content: "\e90d";
}
.glyph-ascribe-camera:before {
content: "\e90f";
}
.glyph-ascribe-music:before {
content: "\e911";
}
.glyph-ascribe-play:before {
content: "\e912";
}
.glyph-ascribe-film:before {
content: "\e913";
}
.glyph-ascribe-credit-card:before {
content: "\e93f";
}
.glyph-ascribe-pushpin:before {
content: "\e946";
}
.glyph-ascribe-undo2:before {
content: "\e967";
}
.glyph-ascribe-redo2:before {
content: "\e968";
}
.glyph-ascribe-enlarge:before {
content: "\e989";
}
.glyph-ascribe-shrink:before {
content: "\e98a";
}
.glyph-ascribe-enlarge2:before {
content: "\e98b";
}
.glyph-ascribe-shrink2:before {
content: "\e98c";
}
.glyph-ascribe-share:before {
content: "\ea7d";
}
.glyph-ascribe-new-tab:before {
content: "\ea7e";
}
.glyph-ascribe-share2:before {
content: "\ea82";
}
.glyph-ascribe-google:before {
content: "\ea87";
}
.glyph-ascribe-google-plus:before {
content: "\ea88";
}
.glyph-ascribe-google-plus2:before {
content: "\ea89";
}
.glyph-ascribe-facebook:before {
content: "\ea8c";
}
.glyph-ascribe-facebook2:before {
content: "\ea8d";
}
.glyph-ascribe-twitter:before {
content: "\ea91";
}
.glyph-ascribe-twitter2:before {
content: "\ea92";
}
.glyph-ascribe-youtube3:before {
content: "\ea99";
}
.glyph-ascribe-dropbox:before {
content: "\eaaf";
}
.glyph-ascribe-file-pdf:before {
content: "\eada";
}
.glyph-ascribe-chrome:before {
content: "\eae5";
}
.glyph-ascribe-firefox:before {
content: "\eae6";
}
.glyph-ascribe-IE:before {
content: "\eae7";
}
.glyph-ascribe-opera:before {
content: "\eae8";
}
.glyph-ascribe-safari:before {
content: "\eae9";
} }
.btn-glyph-ascribe { .btn-glyph-ascribe {

View File

@ -1,3 +1,8 @@
.panel {
/* Here we are overriding bootstrap to show the is-focused background color */
background-color: transparent;
}
.ascribe-panel-wrapper { .ascribe-panel-wrapper {
border: 1px solid #ddd; border: 1px solid #ddd;
height: 5em; height: 5em;

View File

@ -89,6 +89,10 @@ $ascribe-red-error: rgb(169, 68, 66);
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
} }
> .file-drag-and-drop {
margin-top: .5em;
}
> p { > p {
height: 20px; height: 20px;
margin-bottom: 0; margin-bottom: 0;
@ -166,7 +170,7 @@ $ascribe-red-error: rgb(169, 68, 66);
.ascribe-property-collapsible-toggle { .ascribe-property-collapsible-toggle {
border-top: 1px solid rgba(0, 0, 0, .05); border-top: 1px solid rgba(0, 0, 0, .05);
display: inline-block; display: inline-block;
padding: .5em 1.5em; padding: .75em 0 .75em 1.5em;
text-align: left; text-align: left;
width: 100%; width: 100%;
} }

View File

@ -3,19 +3,12 @@
cursor: default !important; cursor: default !important;
display: block; display: block;
height: auto; height: auto;
margin-top: 1em; /* margin-top: 1em; */
outline: 1px dashed #9e9e9e; outline: 1px dashed #9e9e9e;
overflow: auto; overflow: auto;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
padding: 1.5em 0 1.5em 0; padding-top: 1.5em;
.file-drag-and-drop-dialog > p:first-child {
font-size: 1.5em !important;
margin-bottom: 0;
margin-top: 0;
padding-bottom: 0;
}
} }
.inactive-dropzone { .inactive-dropzone {
@ -34,21 +27,19 @@
} }
} }
.file-drag-and-drop-preview-iterator {
margin: 2.5em 0 0 0;
text-align: right;
> div:first-child {
text-align: center;
}
}
.file-drag-and-drop-preview-iterator-spacing {
margin-bottom: 1em;
}
.file-drag-and-drop-dialog { .file-drag-and-drop-dialog {
margin: 1.5em 0 1.5em 0; margin: 1.5em 0 1.5em 0;
> p:first-child {
font-size: 1.5em !important;
margin-bottom: 0;
margin-top: 0;
padding-bottom: 0;
}
> .btn {
margin-bottom: 2em;
}
} }
.file-drag-and-drop-hashing-dialog { .file-drag-and-drop-hashing-dialog {
@ -107,6 +98,7 @@
cursor: default; cursor: default;
overflow: hidden; overflow: hidden;
} }
.action-file { .action-file {
color: white; color: white;
cursor: pointer; cursor: pointer;
@ -125,6 +117,13 @@
&:hover { &:hover {
color: #d9534f; color: #d9534f;
} }
&.icon-ascribe-ok, &.icon-ascribe-ok:hover {
cursor: default;
color: lighten($ascribe--button-default-color, 20%);
font-size: 4.2em;
top: .2em;
}
} }
.spinner-file { .spinner-file {
@ -139,12 +138,16 @@
text-align: center; text-align: center;
width: 104px; width: 104px;
.action-file, .spinner-file { .action-file, .spinner-file, .icon-ascribe-ok {
margin-top: 1em;
line-height: 1.3;
}
.ascribe-progress-bar + .action-file, .ascribe-progress-bar + .spinner-file {
margin-top: .6em; margin-top: .6em;
} }
} }
.file-drag-and-drop-preview-other { .file-drag-and-drop-preview-other {
display: table-cell; display: table-cell;
text-align: center; text-align: center;
@ -154,7 +157,7 @@
margin-top: 5px; margin-top: 5px;
} }
.action-file, .spinner-file { .action-file:not(.icon-ascribe-ok), .spinner-file {
margin-top: 0; margin-top: 0;
position: relative; position: relative;
top: .3em; top: .3em;
@ -171,6 +174,12 @@
> .progress-bar { > .progress-bar {
background-color: $ascribe-dark-blue; background-color: $ascribe-dark-blue;
} }
span {
font-size: 1.25em;
color: white;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
}
} }
.ascribe-progress-bar-xs { .ascribe-progress-bar-xs {