├── .gitignore ├── README.md ├── example └── simple │ ├── .meteor │ ├── .finished-upgraders │ ├── .gitignore │ ├── .id │ ├── cordova-plugins │ ├── packages │ ├── platforms │ ├── release │ └── versions │ ├── simple.css │ ├── simple.html │ └── simple.js ├── package.js ├── src ├── client.js ├── helpers.js ├── server.js ├── template │ ├── editYourAvatarModal.css │ ├── editYourAvatarModal.html │ └── editYourAvatarModal.js └── vendor │ └── imgareaselect │ ├── border-anim-h.gif │ ├── border-anim-v.gif │ ├── imgareaselect-animated.css │ └── jquery.imgareaselect.pack.js └── tests ├── client.js └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | .build* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WARNING: This project is outdated and not maintained anymore. 2 | 3 | ### Install Meteor 4 | $ curl https://install.meteor.com | /bin/sh 5 | 6 | ### Create a project 7 | $ meteor create beautiful-avatar 8 | 9 | ### Install Account System 10 | $ meteor add accounts-base 11 | $ meteor add accounts-password 12 | 13 | ### Install our Avatar-Upload packages 14 | $ meteor add particle4dev:upload-avatar 15 | 16 | ### Create template and install on the application 17 | {{> editYourAvatarModal}} 18 | 19 | ### Demo 20 | 21 | [![ScreenShot](https://i1.ytimg.com/vi/GSaJPWG3vY8/mqdefault.jpg)](http://youtu.be/GSaJPWG3vY8) 22 | -------------------------------------------------------------------------------- /example/simple/.meteor/.finished-upgraders: -------------------------------------------------------------------------------- 1 | # This file contains information which helps Meteor properly upgrade your 2 | # app when you run 'meteor update'. You should check it into version control 3 | # with your project. 4 | 5 | notices-for-0.9.0 6 | notices-for-0.9.1 7 | 0.9.4-platform-file 8 | -------------------------------------------------------------------------------- /example/simple/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | -------------------------------------------------------------------------------- /example/simple/.meteor/.id: -------------------------------------------------------------------------------- 1 | # This file contains a token that is unique to your project. 2 | # Check it into your repository along with the rest of this directory. 3 | # It can be used for purposes such as: 4 | # - ensuring you don't accidentally deploy one app on top of another 5 | # - providing package authors with aggregated statistics 6 | 7 | fx28abkqv6fx1je7k0h 8 | -------------------------------------------------------------------------------- /example/simple/.meteor/cordova-plugins: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/simple/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | meteor-platform 7 | autopublish 8 | insecure 9 | accounts-password 10 | accounts-base 11 | 12 | particle4dev:upload-avatar 13 | -------------------------------------------------------------------------------- /example/simple/.meteor/platforms: -------------------------------------------------------------------------------- 1 | server 2 | browser 3 | -------------------------------------------------------------------------------- /example/simple/.meteor/release: -------------------------------------------------------------------------------- 1 | METEOR@0.9.4 2 | -------------------------------------------------------------------------------- /example/simple/.meteor/versions: -------------------------------------------------------------------------------- 1 | accounts-base@1.1.2 2 | accounts-password@1.0.3 3 | application-configuration@1.0.3 4 | autopublish@1.0.1 5 | autoupdate@1.1.2 6 | base64@1.0.1 7 | binary-heap@1.0.1 8 | blaze-tools@1.0.1 9 | blaze@2.0.2 10 | boilerplate-generator@1.0.1 11 | callback-hook@1.0.1 12 | check@1.0.2 13 | ctl-helper@1.0.4 14 | ctl@1.0.2 15 | ddp@1.0.10 16 | deps@1.0.5 17 | ejson@1.0.4 18 | email@1.0.4 19 | fastclick@1.0.1 20 | follower-livedata@1.0.2 21 | geojson-utils@1.0.1 22 | html-tools@1.0.2 23 | htmljs@1.0.2 24 | http@1.0.7 25 | id-map@1.0.1 26 | insecure@1.0.1 27 | jquery@1.0.1 28 | json@1.0.1 29 | livedata@1.0.11 30 | localstorage@1.0.1 31 | logging@1.0.4 32 | meteor-platform@1.1.2 33 | meteor@1.1.2 34 | minifiers@1.1.1 35 | minimongo@1.0.4 36 | mizzao:bootstrap-3@3.2.0_1 37 | mobile-status-bar@1.0.1 38 | mongo@1.0.7 39 | npm-bcrypt@0.7.7 40 | observe-sequence@1.0.3 41 | ordered-dict@1.0.1 42 | particle4dev:upload-avatar@1.1.0 43 | random@1.0.1 44 | reactive-dict@1.0.4 45 | reactive-var@1.0.3 46 | reload@1.1.1 47 | retry@1.0.1 48 | routepolicy@1.0.2 49 | service-configuration@1.0.2 50 | session@1.0.3 51 | sha@1.0.1 52 | spacebars-compiler@1.0.3 53 | spacebars@1.0.3 54 | srp@1.0.1 55 | templating@1.0.8 56 | tracker@1.0.3 57 | ui@1.0.4 58 | underscore@1.0.1 59 | url@1.0.1 60 | webapp-hashing@1.0.1 61 | webapp@1.1.3 62 | -------------------------------------------------------------------------------- /example/simple/simple.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ 2 | -------------------------------------------------------------------------------- /example/simple/simple.html: -------------------------------------------------------------------------------- 1 | 2 | Upload Avatar 3 | 4 | 5 | 6 |

Upload Avatar

7 | {{> test}} 8 | {{> editYourAvatarModal}} 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/simple/simple.js: -------------------------------------------------------------------------------- 1 | if (Meteor.isClient) { 2 | // counter starts at 0 3 | Session.setDefault("counter", 0); 4 | 5 | Meteor.startup(function () { 6 | Meteor.loginWithPassword('test', '123456', function(err){console.log(err);}); 7 | }); 8 | Template.test.events({ 9 | 'click button.inc': function () { 10 | $('#editYourAvatarModal').modal(); 11 | } 12 | }); 13 | } 14 | 15 | if (Meteor.isServer) { 16 | var Users = Meteor.users; 17 | Meteor.startup(function () { 18 | // code to run on server at startup 19 | AVATAR = "data:image/jpeg;base64,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"; 20 | // Users.remove({username: 'test'}); 21 | if(!Users.findOne({username: 'test'})) 22 | Accounts.createUser({ 23 | username: 'test', 24 | password: '123456', 25 | profile: { 26 | image: AVATAR 27 | } 28 | }); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | summary: "simple upload avatar package for meteor", 3 | version: "1.2.0", 4 | name: "particle4dev:upload-avatar", 5 | git: "https://github.com/particle4dev/upload-avatar-meteor.git" 6 | }); 7 | 8 | var both = ['client', 'server']; 9 | var client = ['client']; 10 | var server = ['server']; 11 | 12 | Package.onUse(function (api) { 13 | api.versionsFrom("METEOR@1.2.0.2"); 14 | 15 | api.use(['underscore', 'accounts-base', 'accounts-password'], both); 16 | api.use(['jquery', 'templating', 'twbs:bootstrap'], client); 17 | 18 | //add file 19 | api.addFiles([ 20 | 'src/helpers.js' 21 | ], both); 22 | 23 | api.addAssets([ 24 | 'src/vendor/imgareaselect/border-anim-h.gif', 25 | 'src/vendor/imgareaselect/border-anim-v.gif', 26 | ], client); 27 | 28 | api.addFiles([ 29 | 'src/vendor/imgareaselect/imgareaselect-animated.css', 30 | 'src/vendor/imgareaselect/jquery.imgareaselect.pack.js', 31 | 'src/client.js', 32 | 'src/template/editYourAvatarModal.html', 33 | 'src/template/editYourAvatarModal.js', 34 | 'src/template/editYourAvatarModal.css' 35 | ], client); 36 | 37 | api.add_files([ 38 | 'src/server.js', 39 | ], server); 40 | }); 41 | 42 | Package.onTest(function (api) { 43 | api.use(['particle4dev:upload-avatar','accounts-password','tinytest'], both); 44 | api.addFiles('tests/client.js', 'client'); 45 | api.addFiles('tests/server.js', 'server'); 46 | }); -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | FileReaderObject = { 2 | previewImage: function(file, callback){ 3 | var reader = new FileReader(); 4 | reader.onload = function (e) { 5 | // check file 6 | if(!_.contains(FILEUPLOAD.IMG.TYPE, file.type)){ 7 | callback(new Meteor.Error(412, "File format not supported. Please upload .jpg or .png")); 8 | return; 9 | } 10 | // check size 11 | if(file.size > FILEUPLOAD.IMG.MAXSIZE){ 12 | callback(new Meteor.Error(412, "File is too large. 512kb size limit")); 13 | return; 14 | } 15 | file.result = e.target.result; 16 | callback(null, file); 17 | }; 18 | reader.onerror = function () { 19 | callback(reader.error); 20 | }; 21 | reader.readAsDataURL(file); 22 | } 23 | }; -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * CONSTANTS 3 | * AVATAR DEFAULT BASE64 4 | */ 5 | AVATAR = "data:image/jpeg;base64,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"; 6 | FILEUPLOAD = { 7 | IMG : { TYPE: ["image/jpeg", "image/png"], MAXSIZE: 512000 },// 512 kb 8 | DOC : [] 9 | }; 10 | validateImgBase64 = function(src){ 11 | if(!/^data:image\/png;base64,/i.test(src)){ 12 | throw new Error("Image is not decode 1"); 13 | } 14 | /** 15 | try { 16 | // try decode base64 17 | var base64 = src.replace("data:image/png;base64,", "", "gi"); 18 | atob(base64); 19 | } 20 | catch (e) { 21 | throw new Error("Image is not decode 2"); 22 | } 23 | */ 24 | return true; 25 | }; 26 | createAlert = { 27 | 'error' : function(message) { 28 | return '
' + message + '
'; 29 | }, 30 | 'success' : function(message) { 31 | return '
' + message + '
'; 32 | }, 33 | 'alert' : function(message) { 34 | return '
' + message + '
'; 35 | }, 36 | 'info' : function(message) { 37 | return '
' + message + '
'; 38 | }, 39 | 'default' : function(message) { 40 | return '
' + message + '
'; 41 | } 42 | }; 43 | createAlertDanger = function(message) { 44 | return createAlert['error'](message); 45 | }; 46 | createAlertSuccess = function(message) { 47 | return createAlert['success'](message); 48 | }; 49 | createAlertInfo = function(message){ 50 | return createAlert['info'](message); 51 | }; -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | Meteor.methods({ 2 | updateAvatar: function(base64) { 3 | var id = this.userId; 4 | if (!id) { 5 | throw new Meteor.Error(403, "You must be logged in"); 6 | } 7 | try { 8 | validateImgBase64(base64); 9 | return Meteor.users.update({_id: id}, 10 | {$set: {'profile.image': base64, 'profile.upgraded': new Date()}} 11 | ); 12 | } 13 | catch(e){ 14 | throw new Meteor.Error(403, e.message); 15 | } 16 | return true; 17 | } 18 | }); -------------------------------------------------------------------------------- /src/template/editYourAvatarModal.css: -------------------------------------------------------------------------------- 1 | /*** 2 | editYourAvatarModal 3 | */ 4 | 5 | #realImage { 6 | display: block; 7 | margin: 0px; 8 | } 9 | 10 | #avatarUserImg { 11 | display: block; 12 | margin: 0px; 13 | max-width: 550px; 14 | margin: 0 0 10px 0; 15 | } 16 | 17 | #previewFrame { 18 | margin: 0 1em; 19 | width: 128px; 20 | height: 128px; 21 | } 22 | 23 | #previewFrame #preview { 24 | width: 128px; 25 | height: 128px; 26 | overflow: hidden; 27 | } 28 | 29 | #previewFrame #preview img { 30 | width: 100%; 31 | height: auto; 32 | } 33 | 34 | .w-modal0 { 35 | width: 70% !important; 36 | margin-left: auto !important; 37 | margin-right: auto !important; 38 | } 39 | -------------------------------------------------------------------------------- /src/template/editYourAvatarModal.html: -------------------------------------------------------------------------------- 1 | 27 | 28 | -------------------------------------------------------------------------------- /src/template/editYourAvatarModal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * HELPERS 3 | */ 4 | var x, // x position of crop image 5 | y, // y position of crop image 6 | width, // width of crop image 7 | height, // height of crop image 8 | error, // 9 | saveAvatarButton, 10 | modal, 11 | realImage, 12 | displayImage, 13 | isShowCropAndButton = false; 14 | 15 | var widthAvatar = 128, 16 | heightAvatar = 128; 17 | 18 | Template.editYourAvatarModalBody.helpers({ 19 | image: function(){ 20 | if(Meteor.user()) 21 | return Meteor.user().profile.image 22 | else 23 | return AVATAR; 24 | } 25 | }); 26 | 27 | Template.editYourAvatarModal.rendered = function(){ 28 | var tmpl = this; 29 | // cache the dom 30 | modal = $(tmpl.find('#editYourAvatarModal')); 31 | error = $(tmpl.find('.error')); 32 | saveAvatarButton = $(tmpl.find('#saveAvatarButton')); 33 | propSaveAvatarButton(false); 34 | realImage = tmpl.find('#realImage'); 35 | modal.on('hide.bs.modal', function () { 36 | clear(); 37 | }); 38 | modal.on('show.bs.modal', function () { 39 | loadImage(tmpl, Meteor.user().profile.image); 40 | $(function () { 41 | displayImage.imgAreaSelect({ aspectRatio: '1:1', handles: true, 42 | fadeSpeed: 200, onSelectChange: preview, parent: $('.modal-content') }); 43 | }); 44 | }); 45 | }; 46 | Template.editYourAvatarModalBody.rendered = function(){ 47 | displayImage = $(this.find('#avatarUserImg')); 48 | $(function () { 49 | displayImage.imgAreaSelect({ aspectRatio: '1:1', handles: true, 50 | fadeSpeed: 200, onSelectChange: preview }); 51 | }); 52 | }; 53 | /** 54 | * EVENTS 55 | */ 56 | Template.editYourAvatarModalBody.events({ 57 | "change input[name=avatarFile]": function(evt, tmpl){ 58 | evt.preventDefault(); 59 | var input = tmpl.find('input[name=avatarFile]'); 60 | if(input.files && input.files[0]){ 61 | FileReaderObject.previewImage(input.files[0], function(err, file){ 62 | if (err){ 63 | error.html(createAlertDanger(err.message)); 64 | Meteor.setTimeout(function(){ 65 | error.html(''); 66 | }, 5000); 67 | } 68 | else { 69 | loadImage(tmpl, file.result); 70 | $(function () { 71 | displayImage.imgAreaSelect({ aspectRatio: '1:1', handles: true, 72 | fadeSpeed: 200, onSelectChange: preview }); 73 | }); 74 | } 75 | }); 76 | } 77 | }, 78 | 'click #changeAvatarButton': function(evt, tmp){ 79 | evt.preventDefault(); 80 | tmp.find('input[name=avatarFile]').click(); 81 | } 82 | }); 83 | Template.editYourAvatarModal.events({ 84 | 'click #saveAvatarButton': function(evt, tmp){ 85 | evt.preventDefault(); 86 | processChangeAvatar(tmp); 87 | }, 88 | 'keypress': function(evt, tmp){ 89 | if(evt.charCode == 13){ 90 | evt.preventDefault(); 91 | modal.modal('hide'); 92 | } 93 | } 94 | }); 95 | /** 96 | * FUNCTION CLASS DEFINE 97 | */ 98 | var processChangeAvatar = function(tmp){ 99 | var canvas = document.createElement("canvas"); 100 | canvas.width = widthAvatar; 101 | canvas.height = heightAvatar; 102 | var scaleX = realImage.width / displayImage.width(); 103 | var scaleY = realImage.height / displayImage.height(); 104 | var ctx = canvas.getContext("2d"); 105 | ctx.drawImage(realImage, x*scaleX, y*scaleY, width*scaleX, height*scaleY, 0, 0, widthAvatar, heightAvatar); 106 | Meteor.call('updateAvatar', canvas.toDataURL(), function(err, res){ 107 | if (err){ 108 | error.html(createAlertDanger(err.message)); 109 | Meteor.setTimeout(function(){ 110 | error.html(''); 111 | }, 5000); 112 | } 113 | else { 114 | modal.modal('hide'); 115 | } 116 | }); 117 | }; 118 | function preview(img, selection) { 119 | if (!selection.width || !selection.height) 120 | return; 121 | var scaleX = widthAvatar / selection.width; 122 | var scaleY = heightAvatar / selection.height; 123 | $('#preview img').css({ 124 | width: Math.round(scaleX * img.width), 125 | height: Math.round(scaleY * img.height), 126 | marginLeft: -Math.round(scaleX * selection.x1), 127 | marginTop: -Math.round(scaleY * selection.y1) 128 | }); 129 | x = selection.x1; 130 | y = selection.y1; 131 | width = selection.width; 132 | height= selection.height; 133 | if(!isShowCropAndButton){ 134 | open(); 135 | } 136 | }; 137 | function propSaveAvatarButton(bool){ 138 | if(saveAvatarButton) 139 | saveAvatarButton.prop('disabled', !bool); 140 | }; 141 | function loadImage(tmp, src){ 142 | $(tmp.find('#avatarUserImg')).attr('src', src); 143 | $(tmp.find('#preview img')).attr('src', src); 144 | $(tmp.find('#realImage')).attr('src', src); 145 | }; 146 | function open(){ 147 | propSaveAvatarButton(true); 148 | $('#previewFrame').removeClass('hide'); 149 | isShowCropAndButton = true; 150 | }; 151 | function clear(){ 152 | // hide crop area 153 | $('.imgareaselect-border1').parent().hide(); 154 | $('.imgareaselect-outer').hide(); 155 | isShowCropAndButton = false; 156 | $('#previewFrame').addClass('hide'); 157 | // reset input 158 | //http://stackoverflow.com/questions/16452699/how-to-reset-a-form-using-jquery-with-reset-method 159 | var inputAvatar = $('input[name=avatarFile]'); 160 | inputAvatar.wrap('
').closest('form').get(0).reset(); 161 | inputAvatar.unwrap(); 162 | }; -------------------------------------------------------------------------------- /src/vendor/imgareaselect/border-anim-h.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/particle4dev/upload-avatar-meteor/fb232afea4b0322022ea65352008324f7738105e/src/vendor/imgareaselect/border-anim-h.gif -------------------------------------------------------------------------------- /src/vendor/imgareaselect/border-anim-v.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/particle4dev/upload-avatar-meteor/fb232afea4b0322022ea65352008324f7738105e/src/vendor/imgareaselect/border-anim-v.gif -------------------------------------------------------------------------------- /src/vendor/imgareaselect/imgareaselect-animated.css: -------------------------------------------------------------------------------- 1 | /* 2 | * imgAreaSelect animated border style 3 | */ 4 | 5 | .imgareaselect-border1 { 6 | background: url(border-anim-v.gif) repeat-y left top; 7 | } 8 | 9 | .imgareaselect-border2 { 10 | background: url(border-anim-h.gif) repeat-x left top; 11 | } 12 | 13 | .imgareaselect-border3 { 14 | background: url(border-anim-v.gif) repeat-y right top; 15 | } 16 | 17 | .imgareaselect-border4 { 18 | background: url(border-anim-h.gif) repeat-x left bottom; 19 | } 20 | 21 | .imgareaselect-border1, .imgareaselect-border2, 22 | .imgareaselect-border3, .imgareaselect-border4 { 23 | opacity: 0.5; 24 | filter: alpha(opacity=50); 25 | } 26 | 27 | .imgareaselect-handle { 28 | background-color: #fff; 29 | border: solid 1px #000; 30 | opacity: 0.5; 31 | filter: alpha(opacity=50); 32 | } 33 | 34 | .imgareaselect-outer { 35 | background-color: #000; 36 | opacity: 0.5; 37 | filter: alpha(opacity=50); 38 | } 39 | 40 | .imgareaselect-selection { 41 | } -------------------------------------------------------------------------------- /src/vendor/imgareaselect/jquery.imgareaselect.pack.js: -------------------------------------------------------------------------------- 1 | eval(function(p,a,c,k,e,d){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(m($){18 W=2v.4T,D=2v.4S,F=2v.4R,u=2v.4Q;m V(){C $("<4P/>")};$.N=m(T,c){18 O=$(T),1F,A=V(),1k=V(),I=V().r(V()).r(V()).r(V()),B=V().r(V()).r(V()).r(V()),E=$([]),1K,G,l,17={v:0,l:0},Q,M,1l,1g={v:0,l:0},12=0,1J="1H",2k,2j,1t,1s,S,1B,1A,2o,2n,14,1Q,a,b,j,g,f={a:0,b:0,j:0,g:0,H:0,L:0},2u=R.4O,1M=4N.4M,$p,d,i,o,w,h,2p;m 1n(x){C x+17.v-1g.v};m 1m(y){C y+17.l-1g.l};m 1b(x){C x-17.v+1g.v};m 1a(y){C y-17.l+1g.l};m 1z(3J){C 3J.4L-1g.v};m 1y(3I){C 3I.4K-1g.l};m 13(32){18 1i=32||1t,1h=32||1s;C{a:u(f.a*1i),b:u(f.b*1h),j:u(f.j*1i),g:u(f.g*1h),H:u(f.j*1i)-u(f.a*1i),L:u(f.g*1h)-u(f.b*1h)}};m 23(a,b,j,g,31){18 1i=31||1t,1h=31||1s;f={a:u(a/1i||0),b:u(b/1h||0),j:u(j/1i||0),g:u(g/1h||0)};f.H=f.j-f.a;f.L=f.g-f.b};m 1f(){9(!1F||!O.H()){C}17={v:u(O.2t().v),l:u(O.2t().l)};Q=O.2Y();M=O.3H();17.l+=(O.30()-M)>>1;17.v+=(O.2q()-Q)>>1;1B=u(c.4J/1t)||0;1A=u(c.4I/1s)||0;2o=u(F(c.4H/1t||1<<24,Q));2n=u(F(c.4G/1s||1<<24,M));9($().4F=="1.3.2"&&1J=="21"&&!2u["4E"]){17.l+=D(R.1q.2r,2u.2r);17.v+=D(R.1q.2s,2u.2s)}1g=/1H|4D/.1c(1l.q("1p"))?{v:u(1l.2t().v)-1l.2s(),l:u(1l.2t().l)-1l.2r()}:1J=="21"?{v:$(R).2s(),l:$(R).2r()}:{v:0,l:0};G=1n(0);l=1m(0);9(f.j>Q||f.g>M){1U()}};m 1V(3F){9(!1Q){C}A.q({v:1n(f.a),l:1m(f.b)}).r(1k).H(w=f.H).L(h=f.L);1k.r(I).r(E).q({v:0,l:0});I.H(D(w-I.2q()+I.2Y(),0)).L(D(h-I.30()+I.3H(),0));$(B[0]).q({v:G,l:l,H:f.a,L:M});$(B[1]).q({v:G+f.a,l:l,H:w,L:f.b});$(B[2]).q({v:G+f.j,l:l,H:Q-f.j,L:M});$(B[3]).q({v:G+f.a,l:l+f.g,H:w,L:M-f.g});w-=E.2q();h-=E.30();2O(E.3f){15 8:$(E[4]).q({v:w>>1});$(E[5]).q({v:w,l:h>>1});$(E[6]).q({v:w>>1,l:h});$(E[7]).q({l:h>>1});15 4:E.3G(1,3).q({v:w});E.3G(2,4).q({l:h})}9(3F!==Y){9($.N.2Z!=2R){$(R).U($.N.2z,$.N.2Z)}9(c.1T){$(R)[$.N.2z]($.N.2Z=2R)}}9(1j&&I.2q()-I.2Y()==2){I.q("3E",0);3x(m(){I.q("3E","4C")},0)}};m 22(3D){1f();1V(3D);a=1n(f.a);b=1m(f.b);j=1n(f.j);g=1m(f.g)};m 27(2X,2w){c.1P?2X.4B(c.1P,2w):2X.1r()};m 1d(2W){18 x=1b(1z(2W))-f.a,y=1a(1y(2W))-f.b;9(!2p){1f();2p=11;A.1G("4A",m(){2p=Y})}S="";9(c.2D){9(y<=c.1W){S="n"}X{9(y>=f.L-c.1W){S="s"}}9(x<=c.1W){S+="w"}X{9(x>=f.H-c.1W){S+="e"}}}A.q("2V",S?S+"-19":c.26?"4z":"");9(1K){1K.4y()}};m 2S(4x){$("1q").q("2V","");9(c.4w||f.H*f.L==0){27(A.r(B),m(){$(J).1r()})}$(R).U("P",2l);A.P(1d);c.2f(T,13())};m 2C(1X){9(1X.3z!=1){C Y}1f();9(S){$("1q").q("2V",S+"-19");a=1n(f[/w/.1c(S)?"j":"a"]);b=1m(f[/n/.1c(S)?"g":"b"]);$(R).P(2l).1G("1x",2S);A.U("P",1d)}X{9(c.26){2k=G+f.a-1z(1X);2j=l+f.b-1y(1X);A.U("P",1d);$(R).P(2T).1G("1x",m(){c.2f(T,13());$(R).U("P",2T);A.P(1d)})}X{O.1O(1X)}}C Y};m 1w(3C){9(14){9(3C){j=D(G,F(G+Q,a+W(g-b)*14*(j>a||-1)));g=u(D(l,F(l+M,b+W(j-a)/14*(g>b||-1))));j=u(j)}X{g=D(l,F(l+M,b+W(j-a)/14*(g>b||-1)));j=u(D(G,F(G+Q,a+W(g-b)*14*(j>a||-1))));g=u(g)}}};m 1U(){a=F(a,G+Q);b=F(b,l+M);9(W(j-a)<1B){j=a-1B*(jG+Q){a=G+Q-1B}}}9(W(g-b)<1A){g=b-1A*(gl+M){b=l+M-1A}}}j=D(G,F(j,G+Q));g=D(l,F(g,l+M));1w(W(j-a)2o){j=a-2o*(j2n){g=b-2n*(g=0){E.H(5).L(5)}9(o=c.2K){E.q({2K:o,2H:"3m"})}1R(E,{3n:"2J-28",3l:"2I-28",3o:"1e"})}1t=c.4l/Q||1;1s=c.4k/M||1;9(K.a!=3q){23(K.a,K.b,K.j,K.g);K.2F=!K.1r}9(K.1T){c.1T=$.2c({2b:1,2a:"19"},K.1T)}B.29(c.1S+"-4j");1k.29(c.1S+"-4i");3p(i=0;i++<4;){$(I[i-1]).29(c.1S+"-2J"+i)}1R(1k,{4h:"2I-28",4g:"1e"});1R(I,{3o:"1e",2K:"2J-H"});1R(B,{4f:"2I-28",4e:"1e"});9(o=c.3n){$(I[0]).q({2H:"3m",3k:o})}9(o=c.3l){$(I[1]).q({2H:"4d",3k:o})}A.2G(1k.r(I).r(1K)).2G(E);9(1j){9(o=(B.q("3j")||"").3i(/1e=(\\d+)/)){B.q("1e",o[1]/1Z)}9(o=(I.q("3j")||"").3i(/1e=(\\d+)/)){I.q("1e",o[1]/1Z)}}9(K.1r){27(A.r(B))}X{9(K.2F&&1F){1Q=11;A.r(B).2E(c.1P||0);22()}}14=(d=(c.4c||"").4b(/:/))[0]/d[1];O.r(B).U("1O",2A);9(c.1E||c.1D===Y){A.U("P",1d).U("1O",2C);$(3h).U("19",2B)}X{9(c.1D||c.1E===Y){9(c.2D||c.26){A.P(1d).1O(2C)}$(3h).19(2B)}9(!c.4a){O.r(B).1O(2A)}}c.1D=c.1E=1Y};J.1o=m(){25({1E:11});A.r(B).1o()};J.49=m(){C c};J.33=25;J.48=13;J.47=23;J.46=1N;J.45=22;18 1j=(/44 ([\\w.]+)/i.43(1M)||[])[1],3c=/42/i.1c(1M),3d=/41/i.1c(1M)&&!/3Z/i.1c(1M);$p=O;3g($p.3f){12=D(12,!1L($p.q("z-3e"))?$p.q("z-3e"):12);9($p.q("1p")=="21"){1J="21"}$p=$p.20(":3Y(1q)")}12=c.1I||12;9(1j){O.3X("3W","3V")}$.N.2z=1j||3d?"3U":"3T";9(3c){1K=V().q({H:"1Z%",L:"1Z%",1p:"1H",1I:12+2||2})}A.r(B).q({3b:"3a",1p:1J,3S:"3a",1I:12||"0"});A.q({1I:12+2||2});1k.r(I).q({1p:"1H",36:0});T.35||T.3R=="35"||!O.2y("3Q")?2x():O.1G("3P",2x);9(!1F&&1j&&1j>=7){T.34=T.34}};$.2w.N=m(Z){Z=Z||{};J.3O(m(){9($(J).1C("N")){9(Z.1o){$(J).1C("N").1o();$(J).3N("N")}X{$(J).1C("N").33(Z)}}X{9(!Z.1o){9(Z.1D===1Y&&Z.1E===1Y){Z.1D=11}$(J).1C("N",3M $.N(J,Z))}}});9(Z.3L){C $(J).1C("N")}C J}})(3K);',62,304,'|||||||||if|x1|y1|_7|||_23|y2|||x2||top|function||||css|add|||_4|left|||||_a|_d|return|_2|_e|_3|_10|width|_c|this|_55|height|_13|imgAreaSelect|_8|mousemove|_12|document|_1c|_6|unbind|_5|_1|else|false|_58||true|_16|_2c|_21|case|_50|_11|var|resize|_29|_28|test|_3a|opacity|_30|_15|sy|sx|_35|_b|_14|_27|_26|remove|position|body|hide|_1b|_1a|break|_45|_42|mouseup|evY|evX|_1e|_1d|data|enable|disable|_9|one|absolute|zIndex|_17|_f|isNaN|ua|_4a|mousedown|fadeSpeed|_22|_51|classPrefix|keys|_31|_32|resizeMargin|_40|undefined|100|parent|fixed|_36|_2e||_4f|movable|_38|color|addClass|ctrl|shift|extend|_54|altKey|onSelectEnd|onSelectChange|_49|_4c|_19|_18|_3e|_48|_20|_1f|_25|outerWidth|scrollTop|scrollLeft|offset|_24|Math|fn|_4e|is|keyPress|_4b|_4d|_3f|resizable|fadeIn|show|append|borderStyle|background|border|borderWidth|handles|_53|key|switch|alt|arrows|_34|_3c|_41|_44|cursor|_3b|_39|innerWidth|onKeyPress|outerHeight|_2f|_2d|setOptions|src|complete|fontSize||||hidden|visibility|_56|_57|index|length|while|window|match|filter|borderColor|borderColor2|solid|borderColor1|borderOpacity|for|null|_52|default|originalEvent|ctrlKey|shiftKey|onInit|setTimeout|onSelectStart|which|_47|_46|_43|_37|margin|_33|slice|innerHeight|_2b|_2a|jQuery|instance|new|removeData|each|load|img|readyState|overflow|keypress|keydown|on|unselectable|attr|not|chrome||webkit|opera|exec|msie|update|cancelSelection|setSelection|getSelection|getOptions|persistent|split|aspectRatio|dashed|outerOpacity|outerColor|selectionOpacity|selectionColor|selection|outer|imageHeight|imageWidth|parseInt|handle|corners|in|keyCode|imgareaselect|animated|instanceof|visible|preventDefault|autoHide|_3d|toggle|move|mouseout|fadeOut|auto|relative|getBoundingClientRect|jquery|maxHeight|maxWidth|minHeight|minWidth|pageY|pageX|userAgent|navigator|documentElement|div|round|min|max|abs'.split('|'))) 2 | -------------------------------------------------------------------------------- /tests/client.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/particle4dev/upload-avatar-meteor/fb232afea4b0322022ea65352008324f7738105e/tests/client.js -------------------------------------------------------------------------------- /tests/server.js: -------------------------------------------------------------------------------- 1 | Tinytest.add('server',function (test) { 2 | //test.equal(actual, expected) 3 | }); --------------------------------------------------------------------------------