<commit_before>(function(define, describe) {
    "use strict";

    var dependencies = [
        'bootstrap'
    ];

    define(dependencies, function(AppBootstrap) {

        var appName = 'app';

        describe('Network service', function() {

            beforeEach(module(appName));

            var networkService, $httpBackend, appMessagesService, $window;

            beforeEach(function() {
                $window = {
                    location: {
                        reload: jasmine.createSpy()
                    }
                };

                module(function($provide) {
                    $provide.value('$window', $window);
                });

                inject(function(_network_, _$httpBackend_, _appMessagesService_) {
                    networkService = _network_;
                    $httpBackend = _$httpBackend_;
                    appMessagesService = _appMessagesService_;
                });
            });

            afterEach(function() {
                var messages = appMessagesService.getMessages();
                for (var i = 0; i < messages.length; i++) {
                    appMessagesService.removeMessage(0);
                }
            });

            it('works with GET', function() {
                var success, api = '/api/get';

                $httpBackend.whenGET(api).respond(200);

                networkService.get(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with POST', function() {
                var success, api = '/api/post';

                $httpBackend.whenPOST(api).respond(200);

                networkService.post(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with PUT', function() {
                var success, api = '/api/put';

                $httpBackend.whenPUT(api).respond(200);

                networkService.put(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with PATCH', function() {
                var success = false, api = '/api/patch';

                $httpBackend.whenPATCH(api).respond(200);

                networkService.patch(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with DELETE', function() {
                var success, api = '/api/delete';

                $httpBackend.whenDELETE(api).respond(200);

                networkService.delete(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('remote connection fails', function() {
                var success = false, api = '/api/post';

                $httpBackend.whenPOST(api).respond(400, {'errors': ['Error']});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();

                $httpBackend.whenPUT(api).respond(400, {'errors': ['Error2']});

                var failure = false;
                networkService.put(api).then(function() {
                }, function() {
                    failure = true;
                });

                $httpBackend.flush();

                expect(failure).toBeTruthy();

                $httpBackend.whenGET(api).respond(400, 'Error');

                failure = false;
                networkService.get(api).then(function() {
                }, function() {
                    failure = true;
                });

                $httpBackend.flush();

                expect(failure).toBeTruthy();
                expect(appMessagesService.getMessages().length).toEqual(3);
            });

            it('should handle detail error message', function() {
                var success = false, api = '/api/post/test';

                $httpBackend.whenPOST(api).respond(400, {'detail': 'Detail error message'});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();
                var messages = appMessagesService.getMessages();
                expect(messages.length).toEqual(1);
                expect(messages[0].msgBody).toContain('
Detail error message
');
            });

            it('should handle multiple error messages', function() {
                var success = false, api = '/api/post/test';

                $httpBackend.whenPOST(api).respond(400, {'errors': ['Error1', 'Error2']});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();
                var messages = appMessagesService.getMessages();
                expect(messages.length).toEqual(1);
                expect(messages[0].msgBody).toContain('
');
            });

            it('makes full reload if application code at server has changed', function() {

                // GIVEN application code has changed at server side
                var api = '/api/get';
                $httpBackend.whenGET(api).respond(418, 'Application code has changed. Please reload.');

                // WHEN fetching some data from server
                networkService.get(api).catch(function() {});
                $httpBackend.flush();

                // THEN full reload is performed at client side
                expect($window.location.reload).toHaveBeenCalled();
            });
        });
    });

})(define, describe);
<commit_msg>Fix network service tests (#7)<commit_after>(function(define, describe) {
    "use strict";

    var dependencies = [
        'bootstrap',
        'angular'
    ];

    define(dependencies, function(AppBootstrap, angular) {

        var appName = 'app';

        describe('Network service', function() {

            beforeEach(module(appName));

            var networkService, $httpBackend, appMessagesService, $window;

            beforeEach(function() {
                $window = {
                    location: {
                        reload: jasmine.createSpy()
                    },
                    angular: angular
                };

                module(function($provide) {
                    $provide.value('$window', $window);
                });

                inject(function(_network_, _$httpBackend_, _appMessagesService_) {
                    networkService = _network_;
                    $httpBackend = _$httpBackend_;
                    appMessagesService = _appMessagesService_;
                });
            });

            afterEach(function() {
                var messages = appMessagesService.getMessages();
                for (var i = 0; i < messages.length; i++) {
                    appMessagesService.removeMessage(0);
                }
            });

            it('works with GET', function() {
                var success, api = '/api/get';

                $httpBackend.whenGET(api).respond(200);

                networkService.get(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with POST', function() {
                var success, api = '/api/post';

                $httpBackend.whenPOST(api).respond(200);

                networkService.post(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with PUT', function() {
                var success, api = '/api/put';

                $httpBackend.whenPUT(api).respond(200);

                networkService.put(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with PATCH', function() {
                var success = false, api = '/api/patch';

                $httpBackend.whenPATCH(api).respond(200);

                networkService.patch(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('works with DELETE', function() {
                var success, api = '/api/delete';

                $httpBackend.whenDELETE(api).respond(200);

                networkService.delete(api).then(function() {
                    success = true;
                });

                $httpBackend.flush();

                expect(success).toBeTruthy();
            });

            it('remote connection fails', function() {
                var success = false, api = '/api/post';

                $httpBackend.whenPOST(api).respond(400, {'errors': ['Error']});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();

                $httpBackend.whenPUT(api).respond(400, {'errors': ['Error2']});

                var failure = false;
                networkService.put(api).then(function() {
                }, function() {
                    failure = true;
                });

                $httpBackend.flush();

                expect(failure).toBeTruthy();

                $httpBackend.whenGET(api).respond(400, 'Error');

                failure = false;
                networkService.get(api).then(function() {
                }, function() {
                    failure = true;
                });

                $httpBackend.flush();

                expect(failure).toBeTruthy();
                expect(appMessagesService.getMessages().length).toEqual(3);
            });

            it('should handle detail error message', function() {
                var success = false, api = '/api/post/test';

                $httpBackend.whenPOST(api).respond(400, {'detail': 'Detail error message'});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();
                var messages = appMessagesService.getMessages();
                expect(messages.length).toEqual(1);
                expect(messages[0].msgBody).toContain('
Detail error message
');
            });

            it('should handle multiple error messages', function() {
                var success = false, api = '/api/post/test';

                $httpBackend.whenPOST(api).respond(400, {'errors': ['Error1', 'Error2']});

                networkService.post(api).then(function() {
                    success = true;
                }).catch(function() {});

                $httpBackend.flush();

                expect(success).toBeFalsy();
                var messages = appMessagesService.getMessages();
                expect(messages.length).toEqual(1);
                expect(messages[0].msgBody).toContain('
');
            });

            it('makes full reload if application code at server has changed', function() {

                // GIVEN application code has changed at server side
                var api = '/api/get';
                $httpBackend.whenGET(api).respond(418, 'Application code has changed. Please reload.');

                // WHEN fetching some data from server
                networkService.get(api).catch(function() {});
                $httpBackend.flush();

                // THEN full reload is performed at client side
                expect($window.location.reload).toHaveBeenCalled();
            });
        });
    });

})(define, describe);
<|endoftext|>


<commit_before>import React from "react";
import { View } from "react-native-web";
import { storiesOf } from "@storybook/react";

import { Header } from "./Header";

storiesOf("Header", module).add("default", () => (
  <View style={{ width: 680 }}>
    <Header />
  </View>
));
<commit_msg>Remove view from header story<commit_after>import React from "react";
import { storiesOf } from "@storybook/react";

import { Header } from "./Header";

storiesOf("Header", module).add("default", () => (
  <div style={{ width: 680 }}>
    <Header />
  </div>
));
<|endoftext|>


<commit_before>import React from "react";
import axios from "axios";
import Dropzone from "react-dropzone";
// import { first } from "lodash";
import uniqid from "uniqid";
import classnames from "classnames";

function noop() {}
function emptyPromise() {
  //wink wink
  return Promise.resolve;
}
export default props => {
  const {
    accept,
    contentOverride,
    innerIcon,
    innerText,
    action,
    className = "",
    fileLimit,
    readBeforeUpload,
    uploadInBulk, //tnr: not yet implemented
    showUploadList = true,
    beforeUpload,
    fileList, //list of files with options: {name, loading, error, url, originalName, downloadName}
    fileListItemRenderer, // handle rendering the file list items yourself :)
    onFileSuccess = emptyPromise, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
    onFieldSubmit = noop, //called when all files have successfully uploaded
    onRemove = noop, //called when a file has been selected to be removed
    onChange = noop, //this is almost always getting passed by redux-form, no need to pass this handler manually
    ...rest //everything else gets spread on the <Dropzone/> https://react-dropzone.js.org/
  } = props;

  let acceptToUse = Array.isArray(accept) ? accept.join(", ") : accept;
  let fileListToUse = fileList ? fileList : [];
  return (

      <Dropzone
        className={"tg-dropzone " + className}
        multiple={fileLimit !== 1}
        activeClassName={"tg-dropzone-active"}
        rejectClassName={"tg-dropzone-reject"}
        //acceptClassName={"tg-dropzone-accept"} //tnr: commenting these out temporarily until https://github.com/react-dropzone/react-dropzone/pull/504 gets merged
        //disabledClassName={"tg-dropzone-disabled"}
        accept={acceptToUse}
        {...{
          onDrop: acceptedFiles => {
            if (fileLimit) {
              acceptedFiles = acceptedFiles.slice(0, fileLimit);
            }
            acceptedFiles.forEach(file => {
              file.loading = true;
              if (!file.id) {
                file.id = uniqid();
              }
            });
            if (beforeUpload) {
              if (readBeforeUpload) {
                /* const file = files[0];
                let reader = new FileReader();
                reader.readAsText(file, "UTF-8");
                reader.onload = function(evt: Object) {
                  const content: string = evt.target.result;
                  anyToJson(content, result => {
                    const parsedSequence: SequenceJson = processSequenceJson(
                      result[0].parsedSequence
                    );
                    onCreateSubmit({ parsedSequence });
                  });
                };
                reader.onerror = function() {
                  window.toastr.error("Failure reading file.");
                };
                */
              }
              const keepGoing = beforeUpload(acceptedFiles, onChange);
              if (keepGoing === false) {
                return;
              }
            }
            fileListToUse = [
              ...acceptedFiles.map(file => {
                return {
                  id: file.id,
                  lastModified: file.lastModified,
                  lastModifiedDate: file.lastModifiedDate,
                  loading: file.loading,
                  name: file.name,
                  preview: file.preview,
                  size: file.size,
                  type: file.type
                };
              }),
              ...fileListToUse
            ].slice(0, fileLimit ? fileLimit : undefined);
            onChange(fileListToUse);
            if (action) {
              const data = new FormData();
              acceptedFiles.forEach(file => {
                data.append("file", file);
              });

              if (uploadInBulk) {
                //tnr: not yet implemented
                /* const config = {
                  onUploadProgress: function(progressEvent) {
                    let percentCompleted = Math.round(
                      progressEvent.loaded * 100 / progressEvent.total
                    );
                  }
                };

                axios
                  .post(action, data, config)
                  .then(function(res) {
                    onChange(res.data);
                  })
                  .catch(function(err) {
                  }); */
              } else {
                const responses = [];
                Promise.all(
                  acceptedFiles.map(fileToUpload => {
                    return axios
                      .post(action, data)
                      .then(function(res) {
                        responses.push(res.data && res.data[0]);
                        onFileSuccess(res.data[0]).then(() => {
                          onChange(
                            (fileListToUse = fileListToUse.map(file => {
                              const fileToReturn = { ...file };
                              if (fileToReturn.id === fileToUpload.id) {
                                fileToReturn.loading = false;
                              }
                              return fileToReturn;
                            }))
                          );
                        });
                      })
                      .catch(function(err) {
                        console.error("Error uploading file:", err);
                        responses.push({
                          ...fileToUpload,
                          error: err && err.msg ? err.msg : err
                        });
                        onChange(
                          (fileListToUse = fileListToUse.map(file => {
                            const fileToReturn = { ...file };
                            if (fileToReturn.id === fileToUpload.id) {
                              fileToReturn.loading = false;
                              fileToReturn.error = true;
                            }
                            return fileToReturn;
                          }))
                        );
                      });
                  })
                ).then(() => {
                  onFieldSubmit(responses);
                });
              }
            }

            //if (beforeAction) {
            //}
          }
        }}
        {...rest}
      >
        {contentOverride || (

            {innerIcon || }
            {innerText || "Click or drag to upload"}

        )}
      </Dropzone>
      {fileList &&
      showUploadList &&
      !!fileList.length && (

          {fileList.map((file, index) => {
            const {
              loading,
              error,
              name,
              originalName,
              url,
              downloadName
            } = file;
            return fileListItemRenderer ? (
              fileListItemRenderer(file)
            ) : (

                  {" "}
                  {name || originalName}{" "}

                {!loading && (
                   {
                      onRemove(file, index, fileList);
                      onChange(
                        fileList.filter((file, index2) => {
                          return index2 !== index;
                        })
                      );
                    }}
                  />
                )}

            );
          })}

      )}

  );
};
<commit_msg>update uploader to read files before upload<commit_after>import React from "react";
import axios from "axios";
import Dropzone from "react-dropzone";
// import { first } from "lodash";
import uniqid from "uniqid";
import classnames from "classnames";

function noop() {}
function emptyPromise() {
  //wink wink
  return Promise.resolve;
}
export default props => {
  const {
    accept,
    contentOverride,
    innerIcon,
    innerText,
    action,
    className = "",
    fileLimit,
    readBeforeUpload,
    uploadInBulk, //tnr: not yet implemented
    showUploadList = true,
    beforeUpload,
    fileList, //list of files with options: {name, loading, error, url, originalName, downloadName}
    fileListItemRenderer, // handle rendering the file list items yourself :)
    onFileSuccess = emptyPromise, //called each time a file is finished and before the file.loading gets set to false, needs to return a promise!
    onFieldSubmit = noop, //called when all files have successfully uploaded
    onRemove = noop, //called when a file has been selected to be removed
    onChange = noop, //this is almost always getting passed by redux-form, no need to pass this handler manually
    ...rest //everything else gets spread on the <Dropzone/> https://react-dropzone.js.org/
  } = props;

  let acceptToUse = Array.isArray(accept) ? accept.join(", ") : accept;
  let fileListToUse = fileList ? fileList : [];
  return (

      <Dropzone
        className={"tg-dropzone " + className}
        multiple={fileLimit !== 1}
        activeClassName={"tg-dropzone-active"}
        rejectClassName={"tg-dropzone-reject"}
        //acceptClassName={"tg-dropzone-accept"} //tnr: commenting these out temporarily until https://github.com/react-dropzone/react-dropzone/pull/504 gets merged
        //disabledClassName={"tg-dropzone-disabled"}
        accept={acceptToUse}
        {...{
          onDrop: acceptedFiles => {
            if (fileLimit) {
              acceptedFiles = acceptedFiles.slice(0, fileLimit);
            }
            acceptedFiles.forEach(file => {
              file.loading = true;
              if (!file.id) {
                file.id = uniqid();
              }
            });

            Promise.resolve()
              .then(() => {
                if (readBeforeUpload) {
                  return Promise.all(
                    acceptedFiles.map(file => {
                      return new Promise((resolve, reject) => {
                        let reader = new FileReader();
                        reader.readAsText(file, "UTF-8");
                        reader.onload = evt => {
                          resolve(evt.target.result);
                        };
                        reader.onerror = err => {
                          console.error("err:", err);
                          reject(err);
                        };
                      });
                    })
                  );
                } else {
                  return acceptedFiles;
                }
              })
              .then(acceptedFiles => {
                return beforeUpload
                  ? beforeUpload(acceptedFiles, onChange)
                  : true;
              })
              .then(keepGoing => {
                if (!keepGoing) return;
                fileListToUse = [
                  ...acceptedFiles.map(file => {
                    return {
                      id: file.id,
                      lastModified: file.lastModified,
                      lastModifiedDate: file.lastModifiedDate,
                      loading: file.loading,
                      name: file.name,
                      preview: file.preview,
                      size: file.size,
                      type: file.type
                    };
                  }),
                  ...fileListToUse
                ].slice(0, fileLimit ? fileLimit : undefined);
                onChange(fileListToUse);
                if (action) {
                  const data = new FormData();
                  acceptedFiles.forEach(file => {
                    data.append("file", file);
                  });

                  if (uploadInBulk) {
                    //tnr: not yet implemented
                    /* const config = {
                      onUploadProgress: function(progressEvent) {
                        let percentCompleted = Math.round(
                          progressEvent.loaded * 100 / progressEvent.total
                        );
                      }
                    };

                    axios
                      .post(action, data, config)
                      .then(function(res) {
                        onChange(res.data);
                      })
                      .catch(function(err) {
                      }); */
                  } else {
                    const responses = [];
                    Promise.all(
                      acceptedFiles.map(fileToUpload => {
                        return axios
                          .post(action, data)
                          .then(function(res) {
                            responses.push(res.data && res.data[0]);
                            onFileSuccess(res.data[0]).then(() => {
                              onChange(
                                (fileListToUse = fileListToUse.map(file => {
                                  const fileToReturn = { ...file };
                                  if (fileToReturn.id === fileToUpload.id) {
                                    fileToReturn.loading = false;
                                  }
                                  return fileToReturn;
                                }))
                              );
                            });
                          })
                          .catch(function(err) {
                            console.error("Error uploading file:", err);
                            responses.push({
                              ...fileToUpload,
                              error: err && err.msg ? err.msg : err
                            });
                            onChange(
                              (fileListToUse = fileListToUse.map(file => {
                                const fileToReturn = { ...file };
                                if (fileToReturn.id === fileToUpload.id) {
                                  fileToReturn.loading = false;
                                  fileToReturn.error = true;
                                }
                                return fileToReturn;
                              }))
                            );
                          });
                      })
                    ).then(() => {
                      onFieldSubmit(responses);
                    });
                  }
                }
              });
          }
        }}
        {...rest}
      >
        {contentOverride || (

            {innerIcon || }
            {innerText || "Click or drag to upload"}

        )}
      </Dropzone>
      {fileList &&
      showUploadList &&
      !!fileList.length && (

          {fileList.map((file, index) => {
            const {
              loading,
              error,
              name,
              originalName,
              url,
              downloadName
            } = file;
            return fileListItemRenderer ? (
              fileListItemRenderer(file)
            ) : (

                  {" "}
                  {name || originalName}{" "}

                {!loading && (
                   {
                      onRemove(file, index, fileList);
                      onChange(
                        fileList.filter((file, index2) => {
                          return index2 !== index;
                        })
                      );
                    }}
                  />
                )}

            );
          })}

      )}

  );
};
<|endoftext|>


<commit_before>'use strict';

var Utils = require('./Utils'),
    ObjectID = require('mongodb').ObjectID,
    async = require('async'),
    assert = require('assert');

module.exports = function(User, Pet, Location) {
    return {
        index: function(req, res) {
            // Get all pets for the given user
            console.log('Pets requested for ', req.user);
            Pet.find({owner: req.user._id}).toArray(function(err, pets) {
                // For each missing dog,
                //     if the dog is missing: get the locations
                //     else:                  remove the locations
                async.map(pets, function addLocations(pet) {
                    if (pet.isMeasuring) {  // Add the locations to the pet
                        Location.find({uuid: pet.uuid}).toArray(function(err, locations) {
                            if (err) {
                                console.error('Could not find locations for '+pet.uuid, err);
                                locations = [];
                            }

                            pet.locations = locations;
                            return pet;
                        });
                    } else {  // Clear the locations for the pet
                        Location.remove({uuid: pet.uuid}, function(err, result) {
                            if (err) {
                                console.error('Could not remove the locations for '+pet.uuid, err);
                            }
                        });
                        pet.locations = null;
                        return pet;
                    }
                }, function callback(err, results) {
                    if (err) {
                        res.status(500).json(err);
                    } else {
                        res.status(200).json(results);
                    }
                });
            });
        },

        create: function(req, res) {
            // Add pet for the given user
            console.log('Received create');
            console.log('uuid:', req.uuid);
            console.log('body:', req.body);
            var newPet = Utils.createPet(req.body, req.user._id);
            console.log('newPet', newPet);
            if (newPet) {
                Pet.insert(newPet, function(err, pet) {
                    if (err) {
                        return res.serverError(err);
                    }

                    // Remove the owner id? FIXME
                    return res.status(200).send(JSON.stringify(pet));
                });
            } else {
                return res.status(400).send('Invalid pet');  // Malformed pet
            }
        },

        update: function(req, res) {
            // Update pet for the given user
            var petId = req.body.petId,
                pet = Utils.getPetAttributes(req.body);

            console.log('Received update:', req.body, 'for', petId);
            assert(!!petId, 'Did not receive pet id');

            if (pet) {
                Pet.update({_id: ObjectID(petId)}, {$set: pet}, function(err, result) {
                    if (err) {
                        return res.status(500).send(err);
                    }
                    return res.status(200).send('Pet has been updated!');
                });
            } else {
                return res.status(400).send('Invalid Request');
            }
        },

        destroy: function(req, res) {
            // Destroy pet for the given user
            var petId = req.body.petId;
            console.log('Received destroy for', petId);

            assert(!!petId, 'Did not receive pet id');

            Pet.remove({_id: ObjectID(petId)}, function(err, result) {
                if (err) {
                    return res.status(500).send(err);
                }
                return res.status(200).send('Removed pet');
            });
        }
    };
};
<commit_msg>Added more logs to GET /pets<commit_after>'use strict';

var Utils = require('./Utils'),
    ObjectID = require('mongodb').ObjectID,
    async = require('async'),
    assert = require('assert');

module.exports = function(User, Pet, Location) {
    return {
        index: function(req, res) {
            // Get all pets for the given user
            console.log('Pets requested for ', req.user);
            Pet.find({owner: req.user._id}).toArray(function(err, pets) {
                // For each missing dog,
                //     if the dog is missing: get the locations
                //     else:                  remove the locations
                console.log('Found pets:', pets);
                async.map(pets, function addLocations(pet) {
                    if (pet.isMeasuring) {  // Add the locations to the pet
                        Location.find({uuid: pet.uuid}).toArray(function(err, locations) {
                            if (err) {
                                console.error('Could not find locations for '+pet.uuid, err);
                                locations = [];
                            }

                            pet.locations = locations;
                            return pet;
                        });
                    } else {  // Clear the locations for the pet
                        Location.remove({uuid: pet.uuid}, function(err, result) {
                            if (err) {
                                console.error('Could not remove the locations for '+pet.uuid, err);
                            }
                        });
                        pet.locations = null;
                        return pet;
                    }
                }, function callback(err, results) {
                    if (err) {
                        res.status(500).json(err);
                    } else {
                        console.log('Responding with', results);
                        res.status(200).json(results);
                    }
                });
            });
        },

        create: function(req, res) {
            // Add pet for the given user
            console.log('Received create');
            console.log('uuid:', req.uuid);
            console.log('body:', req.body);
            var newPet = Utils.createPet(req.body, req.user._id);
            console.log('newPet', newPet);
            if (newPet) {
                Pet.insert(newPet, function(err, pet) {
                    if (err) {
                        return res.serverError(err);
                    }

                    // Remove the owner id? FIXME
                    return res.status(200).send(JSON.stringify(pet));
                });
            } else {
                return res.status(400).send('Invalid pet');  // Malformed pet
            }
        },

        update: function(req, res) {
            // Update pet for the given user
            var petId = req.body.petId,
                pet = Utils.getPetAttributes(req.body);

            console.log('Received update:', req.body, 'for', petId);
            assert(!!petId, 'Did not receive pet id');

            if (pet) {
                Pet.update({_id: ObjectID(petId)}, {$set: pet}, function(err, result) {
                    if (err) {
                        return res.status(500).send(err);
                    }
                    return res.status(200).send('Pet has been updated!');
                });
            } else {
                return res.status(400).send('Invalid Request');
            }
        },

        destroy: function(req, res) {
            // Destroy pet for the given user
            var petId = req.body.petId;
            console.log('Received destroy for', petId);

            assert(!!petId, 'Did not receive pet id');

            Pet.remove({_id: ObjectID(petId)}, function(err, result) {
                if (err) {
                    return res.status(500).send(err);
                }
                return res.status(200).send('Removed pet');
            });
        }
    };
};
<|endoftext|>


<commit_before>'use strict';

// eslint-disable-next-line no-process-env
const processEnv = process.env;

// Common prefix to all environment variables
const ENV_VARS_PREFIX = /^API_ENGINE__/;

// Shortcuts, e.g. environment variable PORT will becomes API_ENGINE__HTTP__PORT
const basicNamesMap = {
  ENV: 'ENV',
  PORT: 'HTTP__PORT',
  HOST: 'HTTP__HOST',
};

module.exports = {
  processEnv,
  ENV_VARS_PREFIX,
  basicNamesMap,
};
<commit_msg>Fix NODE_ENV<commit_after>'use strict';

// eslint-disable-next-line no-process-env
const processEnv = process.env;

// Common prefix to all environment variables
const ENV_VARS_PREFIX = /^API_ENGINE__/;

// Shortcuts, e.g. environment variable PORT will becomes API_ENGINE__HTTP__PORT
const basicNamesMap = {
  NODE_ENV: 'ENV',
  PORT: 'HTTP__PORT',
  HOST: 'HTTP__HOST',
};

module.exports = {
  processEnv,
  ENV_VARS_PREFIX,
  basicNamesMap,
};
<|endoftext|>


<commit_before>import ExpressWS from "express-ws"

import getLogger from "./logger"
import Controller from "./controller"

const logger = getLogger()

export default function(app) {
  const wss = ExpressWS(app).getWss()

  app.ws("/ws", (ws, req) => {
    logger.info({ ws }, "Websocket connection established")

    const controller = new Controller((command) => {
      const message = JSON.stringify(command)

      logger.info({ ws, message }, `Sending message: ${message}`)

      ws.send(message)
    })

    ws.on("message", (message) => {
      logger.info({ ws, message }, `Websocket message received: ${message}`)

      try {
        (({action, ...rest}) => {
          if (!controller[action]) {
            logger.info(`Received invalid action for dispatch: ${action}`)
          } else {
            logger.info({ controller, args: rest }, `Dispatching ${action}`)

            controller[action](rest)
          }
        })(JSON.parse(message))

      } catch(err) {
        if (err instanceof SyntaxError) {
          logger.info("Received invalid JSON command")
        } else {
          throw err
        }
      }
    })
  })
}

export const __useDefault = true
<commit_msg>factor out client creation process<commit_after>import { v4 } from "uuid"
import ExpressWS from "express-ws"

import getLogger from "./logger"
import Controller from "./controller"

const logger = getLogger()

export default function(app) {
  const wss = ExpressWS(app).getWss()

  app.ws("/ws", (ws, req) => {
    logger.info({ ws }, "Websocket connection established")

    const controller = new Controller(mkClient(ws))

    ws.on("message", (message) => {
      logger.info({ ws, message }, `Websocket message received: ${message}`)

      try {
        (({action, ...rest}) => {
          if (!controller[action]) {
            logger.info(`Received invalid action for dispatch: ${action}`)
          } else {
            logger.info({ controller, args: rest }, `Dispatching ${action}`)

            controller[action](rest)
          }
        })(JSON.parse(message))

      } catch(err) {
        if (err instanceof SyntaxError) {
          logger.info("Received invalid JSON command")
        } else {
          throw err
        }
      }
    })
  })
}

function mkClient(ws) {
  return {
    id: v4(),
    send: (command) => {
      const message = JSON.stringify(command)

      logger.info({ ws, message }, `Sending message: ${message}`)

      ws.send(message)
    }
  }
}

export const __useDefault = true
<|endoftext|>


<commit_before>import React from 'react';
import utils from '../utils';

function makeHttpRequest(method, uri, body, headers, callback) {
  var request = new XMLHttpRequest();

  request.open(method.toUpperCase(), uri, true);

  if (headers) {
    for (var name in headers) {
      var value = headers[name];
      request.setRequestHeader(name, value);
    }
  }

  // If the URI is different than the URI of the domain we're on,
  // then set withCredentials to true so that we enable CORS.
  if (!utils.isRelativeUri(uri) && !utils.isSameHost(uri, window.location.href)) {
    request.withCredentials = true;
  }

  request.onreadystatechange = function () {
    // 4 = Request finished and response is ready.
    // Ignore everything else.
    if (request.readyState !== 4) {
      return;
    }

    let result = {
      status: request.status,
      responseJSON: null
    };

    try {
      if (request.responseText) {
        result.responseJSON = JSON.parse(request.responseText);
      }
    } catch(e) {
      return callback(e);
    }

    callback(null, result);
  };

  if (body && typeof body === 'object') {
    request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
    request.send(JSON.stringify(body));
  } else {
    request.send();
  }
}

export default class BaseService {
  constructor(endpoints) {
    var defaultEndpoints = {
      baseUri: null
    };

    this.endpoints = utils.mergeObjects(defaultEndpoints, endpoints);
  }

  _makeRequest(method, path, body, callback) {
    var uri = this._buildEndpoint(path);
    var headers = {
      'Accept': 'application/json'
    };

    // Only set the X-Stormpath-Agent header if we're on the same domain as the requested URI.
    // This because we want to avoid CORS requests that require you to have to whitelist the X-Stormpath-Agent header.
    if (utils.isRelativeUri(uri) || utils.isSameHost(uri, window.location.href)) {
      headers['X-Stormpath-Agent'] = `stormpath-sdk-react/${pkg.version} react/${React.version}`;
    }

    makeHttpRequest(method, uri, body, headers, function (err, result) {
      if (err) {
        return callback(err);
      }

      var data = result.responseJSON || {};

      if (result.status === 200) {
        callback(null, data);
      } else {
        var error = new Error(data.message || data.error || 'A request to the API failed.');
        error.status = result.status;
        callback(error);
      }
    });
  }

  _buildEndpoint(endpoint) {
    return (this.endpoints.baseUri || '') + endpoint;
  }
}
<commit_msg>Added Content-Type header to all http requests<commit_after>import React from 'react';
import utils from '../utils';

function makeHttpRequest(method, uri, body, headers, callback) {
  var request = new XMLHttpRequest();

  request.open(method.toUpperCase(), uri, true);

  if (headers) {
    for (var name in headers) {
      var value = headers[name];
      request.setRequestHeader(name, value);
    }
  }

  // If the URI is different than the URI of the domain we're on,
  // then set withCredentials to true so that we enable CORS.
  if (!utils.isRelativeUri(uri) && !utils.isSameHost(uri, window.location.href)) {
    request.withCredentials = true;
  }

  request.onreadystatechange = function () {
    // 4 = Request finished and response is ready.
    // Ignore everything else.
    if (request.readyState !== 4) {
      return;
    }

    let result = {
      status: request.status,
      responseJSON: null
    };

    try {
      if (request.responseText) {
        result.responseJSON = JSON.parse(request.responseText);
      }
    } catch(e) {
      return callback(e);
    }

    callback(null, result);
  };

  if (body && typeof body === 'object') {
    request.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
    request.send(JSON.stringify(body));
  } else {
    request.send();
  }
}

export default class BaseService {
  constructor(endpoints) {
    var defaultEndpoints = {
      baseUri: null
    };

    this.endpoints = utils.mergeObjects(defaultEndpoints, endpoints);
  }

  _makeRequest(method, path, body, callback) {
    var uri = this._buildEndpoint(path);
    var headers = {
      'Content-Type': 'application/json;charset=UTF-8',
      'Accept': 'application/json'
    };

    // Only set the X-Stormpath-Agent header if we're on the same domain as the requested URI.
    // This because we want to avoid CORS requests that require you to have to whitelist the X-Stormpath-Agent header.
    if (utils.isRelativeUri(uri) || utils.isSameHost(uri, window.location.href)) {
      headers['X-Stormpath-Agent'] = `stormpath-sdk-react/${pkg.version} react/${React.version}`;
    }

    makeHttpRequest(method, uri, body, headers, function (err, result) {
      if (err) {
        return callback(err);
      }

      var data = result.responseJSON || {};

      if (result.status === 200) {
        callback(null, data);
      } else {
        var error = new Error(data.message || data.error || 'A request to the API failed.');
        error.status = result.status;
        callback(error);
      }
    });
  }

  _buildEndpoint(endpoint) {
    return (this.endpoints.baseUri || '') + endpoint;
  }
}
<|endoftext|>


<commit_before>import express from 'express';
import http from 'http';
import { config } from '/services/config';
import { Session, User } from '/models/index';
import bodyParser from 'body-parser';

const expressServer = express();
const server = http.Server(expressServer);

const port = config['server-port'] || 3000;

const authenticatedMiddleware = (req, res, next) => {

  if (!req.headers.authorization) {
    return res.status(403).send(`Authorization token required to access ${req.method} ${req.baseUrl}`)
  }

  const token = req.headers.authorization;

  //TODO: #10 set expiry date

  Session.findOne({
    where: {
      token: token
    },
    include: [
      {
        model: User,
        attributes: {
          exclude: ['password']
        }
      }
    ]
  }).then((row, err) => {

    if (!row) {
      return res.status(403).send(`Bad token, access to ${req.method} ${req.baseUrl} not allowed`);
    }

    if (!row.User) {
      //TODO: #10 remove token from DB
      return res.status(403).send(`Orphan token, access to ${req.method} ${req.baseUrl} not allowed`);
    }

    req.Session = row;

    next();

  });

};

expressServer.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, Authorization, X-Requested-With, Content-Type, Accept");
  next();
});

expressServer.use('/session', (req, res, next) => {
  switch(req.method) {
    case 'GET':
    case 'DELETE':
      return authenticatedMiddleware(req, res, next);
    default:
      return next();
  }
});

expressServer.use(bodyParser.urlencoded({
  extended: true
}));

expressServer.use(bodyParser.json());

/**
 * Letsencrypt validation
 */
expressServer.get('/.well-known', (req, res) => {
  res.send('ok');
});

server.listen(port, () => {
  console.info(`Server had started on localhost:${port}`);
});

export {
  expressServer as express,
  server as http
}<commit_msg>remove letsencrypt integration from server<commit_after>import express from 'express';
import http from 'http';
import { config } from '/services/config';
import { Session, User } from '/models/index';
import bodyParser from 'body-parser';

const expressServer = express();
const server = http.Server(expressServer);

const port = config['server-port'] || 3000;

const authenticatedMiddleware = (req, res, next) => {

  if (!req.headers.authorization) {
    return res.status(403).send(`Authorization token required to access ${req.method} ${req.baseUrl}`)
  }

  const token = req.headers.authorization;

  //TODO: #10 set expiry date

  Session.findOne({
    where: {
      token: token
    },
    include: [
      {
        model: User,
        attributes: {
          exclude: ['password']
        }
      }
    ]
  }).then((row, err) => {

    if (!row) {
      return res.status(403).send(`Bad token, access to ${req.method} ${req.baseUrl} not allowed`);
    }

    if (!row.User) {
      //TODO: #10 remove token from DB
      return res.status(403).send(`Orphan token, access to ${req.method} ${req.baseUrl} not allowed`);
    }

    req.Session = row;

    next();

  });

};

expressServer.use((req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, Authorization, X-Requested-With, Content-Type, Accept");
  next();
});

expressServer.use('/session', (req, res, next) => {
  switch(req.method) {
    case 'GET':
    case 'DELETE':
      return authenticatedMiddleware(req, res, next);
    default:
      return next();
  }
});

expressServer.use(bodyParser.urlencoded({
  extended: true
}));

expressServer.use(bodyParser.json());

server.listen(port, () => {
  console.info(`Server had started on localhost:${port}`);
});

export {
  expressServer as express,
  server as http
}<|endoftext|>


<commit_before>(function(global) {

  'use strict';

  var fabric = global.fabric || (global.fabric = {}),
      clone  = fabric.util.object.clone;

  /**
   * Textbox class, based on IText, allows the user to resize the text rectangle
   * and wraps lines automatically. Textboxes have their Y scaling locked, the
   * user can only change width. Height is adjusted automatically based on the
   * wrapping of lines.
   * @class fabric.Textbox
   * @extends fabric.IText
   * @mixes fabric.Observable
   * @return {fabric.Textbox} thisArg
   * @see {@link fabric.Textbox#initialize} for constructor definition
   */
  fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
    /**
     * Type of an object
     * @type String
     * @default
     */
    type: 'textbox',
    /**
     * Minimum width of textbox, in pixels.
     * @type Number
     * @default
     */
    minWidth: 20,
    /**
     * Minimum calculated width of a textbox, in pixels.
     * @type Number
     * @default
     */
    dynamicMinWidth: 0,
    /**
     * Cached array of text wrapping.
     * @type Array
     */
    __cachedLines: null,
    /**
     * Constructor. Some scaling related property values are forced. Visibility
     * of controls is also fixed; only the rotation and width controls are
     * made available.
     * @param {String} text Text string
     * @param {Object} [options] Options object
     * @return {fabric.Textbox} thisArg
     */
    initialize: function(text, options) {
      this.ctx = fabric.util.createCanvasElement().getContext('2d');

      this.callSuper('initialize', text, options);
      this.set({
        lockUniScaling: false,
        lockScalingY: true,
        lockScalingFlip: true,
        hasBorders: true
      });
      this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility());

      // add width to this list of props that effect line wrapping.
      this._dimensionAffectingProps.width = true;
    },

    /**
     * Unlike superclass's version of this function, Textbox does not update
     * its width.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @private
     * @override
     */
    _initDimensions: function(ctx) {
      if (this.__skipDimension) {
        return;
      }

      if (!ctx) {
        ctx = fabric.util.createCanvasElement().getContext('2d');
        this._setTextStyles(ctx);
      }

      // clear dynamicMinWidth as it will be different after we re-wrap line
      this.dynamicMinWidth = 0;

      // wrap lines
      this._textLines = this._splitTextIntoLines();

      // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
      if (this.dynamicMinWidth > this.width) {
        this._set('width', this.dynamicMinWidth);
      }

      // clear cache and re-calculate height
      this._clearCache();
      this.height = this._getTextHeight(ctx);
    },

    /**
     * Generate an object that translates the style object so that it is
     * broken up by visual lines (new lines and automatic wrapping).
     * The original text styles object is broken up by actual lines (new lines only),
     * which is only sufficient for Text / IText
     * @private
     */
    _generateStyleMap: function() {
      var realLineCount     = 0,
          realLineCharCount = 0,
          charCount         = 0,
          map               = {};

      for (var i = 0; i < this._textLines.length; i++) {
        if (this.text[charCount] === '\n') {
          realLineCharCount = 0;
          charCount++;
          realLineCount++;
        }
        else if (this.text[charCount] === ' ') {
          // this case deals with space's that are removed from end of lines when wrapping
          realLineCharCount++;
          charCount++;
        }

        map[i] = { line: realLineCount, offset: realLineCharCount };

        charCount += this._textLines[i].length;
        realLineCharCount += this._textLines[i].length;
      }

      return map;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Boolean} [returnCloneOrEmpty=false]
     * @private
     */
    _getStyleDeclaration: function(lineIndex, charIndex, returnCloneOrEmpty) {
      if (this._styleMap) {
        var map = this._styleMap[lineIndex];
        lineIndex = map.line;
        charIndex = map.offset + charIndex;
      }
      return this.callSuper('_getStyleDeclaration', lineIndex, charIndex, returnCloneOrEmpty);
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Object} style
     * @private
     */
    _setStyleDeclaration: function(lineIndex, charIndex, style) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      this.styles[lineIndex][charIndex] = style;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @private
     */
    _deleteStyleDeclaration: function(lineIndex, charIndex) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      delete this.styles[lineIndex][charIndex];
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _getLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      return this.styles[map.line];
    },

    /**
     * @param {Number} lineIndex
     * @param {Object} style
     * @private
     */
    _setLineStyle: function(lineIndex, style) {
      var map = this._styleMap[lineIndex];
      this.styles[map.line] = style;
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _deleteLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      delete this.styles[map.line];
    },

    /**
     * Wraps text using the 'width' property of Textbox. First this function
     * splits text on newlines, so we preserve newlines entered by the user.
     * Then it wraps each line using the width of the Textbox by calling
     * _wrapLine().
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text that is split into lines
     * @returns {Array} Array of lines
     */
    _wrapText: function(ctx, text) {
      var lines = text.split(this._reNewline), wrapped = [], i;

      for (i = 0; i < lines.length; i++) {
        wrapped = wrapped.concat(this._wrapLine(ctx, lines[i], i));
      }

      return wrapped;
    },

    /**
     * Helper function to measure a string of text, given its lineIndex and charIndex offset
     *
     * @param {CanvasRenderingContext2D} ctx
     * @param {String} text
     * @param {number} lineIndex
     * @param {number} charOffset
     * @returns {number}
     * @private
     */
    _measureText: function(ctx, text, lineIndex, charOffset) {
      var width = 0;
      charOffset = charOffset || 0;

      for (var i = 0, len = text.length; i < len; i++) {
        width += this._getWidthOfChar(ctx, text[i], lineIndex, i + charOffset);
      }

      return width;
    },

    /**
     * Wraps a line of text using the width of the Textbox and a context.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text to split into lines
     * @param {Number} lineIndex
     * @returns {Array} Array of line(s) into which the given text is wrapped
     * to.
     */
    _wrapLine: function(ctx, text, lineIndex) {
      var lineWidth        = 0,
          lines            = [],
          line             = '',
          words            = text.split(' '),
          word             = '',
          offset           = 0,
          infix            = ' ',
          wordWidth        = 0,
          infixWidth       = 0,
          largestWordWidth = 0;

      for (var i = 0; i < words.length; i++) {
        word = words[0];
        wordWidth = this._measureText(ctx, word, lineIndex, offset);
        offset += word.length;

        lineWidth += infixWidth + wordWidth;

        if (lineWidth >= this.width && line !== '') {
          lines.push(line);
          line = '';
          lineWidth = wordWidth;
        }

        if (line !== '' || word === '') {
          line += infix;
        }
        line += word;

        infixWidth = this._measureText(ctx, infix, lineIndex, offset);
        offset++;

        // keep track of largest word
        if (wordWidth > largestWordWidth) {
          largestWordWidth = wordWidth;
        }
      }

      i && lines.push(line);

      if (largestWordWidth > this.dynamicMinWidth) {
        this.dynamicMinWidth = largestWordWidth;
      }

      return lines;
    },

    /**
     * Gets lines of text to render in the Textbox. This function calculates
     * text wrapping on the fly everytime it is called.
     * @returns {Array} Array of lines in the Textbox.
     * @override
     */
    _splitTextIntoLines: function() {
      this.ctx.save();
      this._setTextStyles(this.ctx);

      var lines = this._wrapText(this.ctx, this.text);

      this.ctx.restore();
      this._textLines = lines;
      this._styleMap = this._generateStyleMap();
      return lines;
    },

    /**
     * When part of a group, we don't want the Textbox's scale to increase if
     * the group's increases. That's why we reduce the scale of the Textbox by
     * the amount that the group's increases. This is to maintain the effective
     * scale of the Textbox at 1, so that font-size values make sense. Otherwise
     * the same font-size value would result in different actual size depending
     * on the value of the scale.
     * @param {String} key
     * @param {Any} value
     */
    setOnGroup: function(key, value) {
      if (key === 'scaleX') {
        this.set('scaleX', Math.abs(1 / value));
        this.set('width', (this.get('width') * value) /
          (typeof this.__oldScaleX === 'undefined' ? 1 : this.__oldScaleX));
        this.__oldScaleX = value;
      }
    },

    /**
     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start).
     * Overrides the superclass function to take into account text wrapping.
     *
     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
     */
    get2DCursorLocation: function(selectionStart) {
      if (typeof selectionStart === 'undefined') {
        selectionStart = this.selectionStart;
      }

      var numLines = this._textLines.length,
          removed  = 0;

      for (var i = 0; i < numLines; i++) {
        var line    = this._textLines[i],
            lineLen = line.length;

        if (selectionStart <= removed + lineLen) {
          return {
            lineIndex: i,
            charIndex: selectionStart - removed
          };
        }

        removed += lineLen;

        if (this.text[removed] === '\n' || this.text[removed] === ' ') {
          removed++;
        }
      }

      return {
        lineIndex: numLines - 1,
        charIndex: this._textLines[numLines - 1].length
      };
    },

    /**
     * Overrides superclass function and uses text wrapping data to get cursor
     * boundary offsets instead of the array of chars.
     * @param {Array} chars Unused
     * @param {String} typeOfBoundaries Can be 'cursor' or 'selection'
     * @returns {Object} Object with 'top', 'left', and 'lineLeft' properties set.
     */
    _getCursorBoundariesOffsets: function(chars, typeOfBoundaries) {
      var topOffset      = 0,
          leftOffset     = 0,
          cursorLocation = this.get2DCursorLocation(),
          lineChars      = this._textLines[cursorLocation.lineIndex].split(''),
          lineLeftOffset = this._getLineLeftOffset(this._getLineWidth(this.ctx, cursorLocation.lineIndex));

      for (var i = 0; i < cursorLocation.charIndex; i++) {
        leftOffset += this._getWidthOfChar(this.ctx, lineChars[i], cursorLocation.lineIndex, i);
      }

      for (i = 0; i < cursorLocation.lineIndex; i++) {
        topOffset += this._getHeightOfLine(this.ctx, i);
      }

      if (typeOfBoundaries === 'cursor') {
        topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, cursorLocation.lineIndex)
          / this.lineHeight - this.getCurrentCharFontSize(cursorLocation.lineIndex, cursorLocation.charIndex)
          * (1 - this._fontSizeFraction);
      }

      return {
        top: topOffset,
        left: leftOffset,
        lineLeft: lineLeftOffset
      };
    },

    getMinWidth: function() {
      return Math.max(this.minWidth, this.dynamicMinWidth);
    },

    /**
     * Returns object representation of an instance
     * @method toObject
     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
     * @return {Object} object representation of an instance
     */
    toObject: function(propertiesToInclude) {
      return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
        minWidth: this.minWidth
      });
    }
  });
  /**
   * Returns fabric.Textbox instance from an object representation
   * @static
   * @memberOf fabric.Textbox
   * @param {Object} object Object to create an instance from
   * @return {fabric.Textbox} instance of fabric.Textbox
   */
  fabric.Textbox.fromObject = function(object) {
    return new fabric.Textbox(object.text, clone(object));
  };
  /**
   * Returns the default controls visibility required for Textboxes.
   * @returns {Object}
   */
  fabric.Textbox.getTextboxControlVisibility = function() {
    return {
      tl: false,
      tr: false,
      br: false,
      bl: false,
      ml: true,
      mt: false,
      mr: true,
      mb: false,
      mtr: true
    };
  };
  /**
   * Contains all fabric.Textbox objects that have been created
   * @static
   * @memberOf fabric.Textbox
   * @type Array
   */
  fabric.Textbox.instances = [];
})(typeof exports !== 'undefined' ? exports : this);
<commit_msg>Update textbox.class.js<commit_after>(function(global) {

  'use strict';

  var fabric = global.fabric || (global.fabric = {}),
      clone  = fabric.util.object.clone;

  /**
   * Textbox class, based on IText, allows the user to resize the text rectangle
   * and wraps lines automatically. Textboxes have their Y scaling locked, the
   * user can only change width. Height is adjusted automatically based on the
   * wrapping of lines.
   * @class fabric.Textbox
   * @extends fabric.IText
   * @mixes fabric.Observable
   * @return {fabric.Textbox} thisArg
   * @see {@link fabric.Textbox#initialize} for constructor definition
   */
  fabric.Textbox = fabric.util.createClass(fabric.IText, fabric.Observable, {
    /**
     * Type of an object
     * @type String
     * @default
     */
    type: 'textbox',
    /**
     * Minimum width of textbox, in pixels.
     * @type Number
     * @default
     */
    minWidth: 20,
    /**
     * Minimum calculated width of a textbox, in pixels.
     * @type Number
     * @default
     */
    dynamicMinWidth: 0,
    /**
     * Cached array of text wrapping.
     * @type Array
     */
    __cachedLines: null,
    /**
     * Constructor. Some scaling related property values are forced. Visibility
     * of controls is also fixed; only the rotation and width controls are
     * made available.
     * @param {String} text Text string
     * @param {Object} [options] Options object
     * @return {fabric.Textbox} thisArg
     */
    initialize: function(text, options) {
      this.ctx = fabric.util.createCanvasElement().getContext('2d');

      this.callSuper('initialize', text, options);
      this.set({
        lockUniScaling: false,
        lockScalingY: true,
        lockScalingFlip: true,
        hasBorders: true
      });
      this.setControlsVisibility(fabric.Textbox.getTextboxControlVisibility());

      // add width to this list of props that effect line wrapping.
      this._dimensionAffectingProps.width = true;
    },

    /**
     * Unlike superclass's version of this function, Textbox does not update
     * its width.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @private
     * @override
     */
    _initDimensions: function(ctx) {
      if (this.__skipDimension) {
        return;
      }

      if (!ctx) {
        ctx = fabric.util.createCanvasElement().getContext('2d');
        this._setTextStyles(ctx);
      }

      // clear dynamicMinWidth as it will be different after we re-wrap line
      this.dynamicMinWidth = 0;

      // wrap lines
      this._textLines = this._splitTextIntoLines();

      // if after wrapping, the width is smaller than dynamicMinWidth, change the width and re-wrap
      if (this.dynamicMinWidth > this.width) {
        this._set('width', this.dynamicMinWidth);
      }

      // clear cache and re-calculate height
      this._clearCache();
      this.height = this._getTextHeight(ctx);
    },

    /**
     * Generate an object that translates the style object so that it is
     * broken up by visual lines (new lines and automatic wrapping).
     * The original text styles object is broken up by actual lines (new lines only),
     * which is only sufficient for Text / IText
     * @private
     */
    _generateStyleMap: function() {
      var realLineCount     = 0,
          realLineCharCount = 0,
          charCount         = 0,
          map               = {};

      for (var i = 0; i < this._textLines.length; i++) {
        if (this.text[charCount] === '\n') {
          realLineCharCount = 0;
          charCount++;
          realLineCount++;
        }
        else if (this.text[charCount] === ' ') {
          // this case deals with space's that are removed from end of lines when wrapping
          realLineCharCount++;
          charCount++;
        }

        map[i] = { line: realLineCount, offset: realLineCharCount };

        charCount += this._textLines[i].length;
        realLineCharCount += this._textLines[i].length;
      }

      return map;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Boolean} [returnCloneOrEmpty=false]
     * @private
     */
    _getStyleDeclaration: function(lineIndex, charIndex, returnCloneOrEmpty) {
      if (this._styleMap) {
        var map = this._styleMap[lineIndex];
        lineIndex = map.line;
        charIndex = map.offset + charIndex;
      }
      return this.callSuper('_getStyleDeclaration', lineIndex, charIndex, returnCloneOrEmpty);
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @param {Object} style
     * @private
     */
    _setStyleDeclaration: function(lineIndex, charIndex, style) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      this.styles[lineIndex][charIndex] = style;
    },

    /**
     * @param {Number} lineIndex
     * @param {Number} charIndex
     * @private
     */
    _deleteStyleDeclaration: function(lineIndex, charIndex) {
      var map = this._styleMap[lineIndex];
      lineIndex = map.line;
      charIndex = map.offset + charIndex;

      delete this.styles[lineIndex][charIndex];
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _getLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      return this.styles[map.line];
    },

    /**
     * @param {Number} lineIndex
     * @param {Object} style
     * @private
     */
    _setLineStyle: function(lineIndex, style) {
      var map = this._styleMap[lineIndex];
      this.styles[map.line] = style;
    },

    /**
     * @param {Number} lineIndex
     * @private
     */
    _deleteLineStyle: function(lineIndex) {
      var map = this._styleMap[lineIndex];
      delete this.styles[map.line];
    },

    /**
     * Wraps text using the 'width' property of Textbox. First this function
     * splits text on newlines, so we preserve newlines entered by the user.
     * Then it wraps each line using the width of the Textbox by calling
     * _wrapLine().
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text that is split into lines
     * @returns {Array} Array of lines
     */
    _wrapText: function(ctx, text) {
      var lines = text.split(this._reNewline), wrapped = [], i;

      for (i = 0; i < lines.length; i++) {
        wrapped = wrapped.concat(this._wrapLine(ctx, lines[i], i));
      }

      return wrapped;
    },

    /**
     * Helper function to measure a string of text, given its lineIndex and charIndex offset
     *
     * @param {CanvasRenderingContext2D} ctx
     * @param {String} text
     * @param {number} lineIndex
     * @param {number} charOffset
     * @returns {number}
     * @private
     */
    _measureText: function(ctx, text, lineIndex, charOffset) {
      var width = 0;
      charOffset = charOffset || 0;

      for (var i = 0, len = text.length; i < len; i++) {
        width += this._getWidthOfChar(ctx, text[i], lineIndex, i + charOffset);
      }

      return width;
    },

    /**
     * Wraps a line of text using the width of the Textbox and a context.
     * @param {CanvasRenderingContext2D} ctx Context to use for measurements
     * @param {String} text The string of text to split into lines
     * @param {Number} lineIndex
     * @returns {Array} Array of line(s) into which the given text is wrapped
     * to.
     */
    _wrapLine: function(ctx, text, lineIndex) {
      var lineWidth        = 0,
          lines            = [],
          line             = '',
          words            = text.split(' '),
          word             = '',
          offset           = 0,
          infix            = ' ',
          wordWidth        = 0,
          infixWidth       = 0,
          largestWordWidth = 0;

      for (var i = 0; i < words.length; i++) {
        word = words[0];
        wordWidth = this._measureText(ctx, word, lineIndex, offset);
        offset += word.length;

        lineWidth += infixWidth + wordWidth;

        if (lineWidth >= this.width && line !== '') {
          lines.push(line);
          line = '';
          lineWidth = wordWidth;
        }

        if (line !== '' || i === 1) {
          line += infix;
        }
        line += word;

        infixWidth = this._measureText(ctx, infix, lineIndex, offset);
        offset++;

        // keep track of largest word
        if (wordWidth > largestWordWidth) {
          largestWordWidth = wordWidth;
        }
      }

      i && lines.push(line);

      if (largestWordWidth > this.dynamicMinWidth) {
        this.dynamicMinWidth = largestWordWidth;
      }

      return lines;
    },

    /**
     * Gets lines of text to render in the Textbox. This function calculates
     * text wrapping on the fly everytime it is called.
     * @returns {Array} Array of lines in the Textbox.
     * @override
     */
    _splitTextIntoLines: function() {
      this.ctx.save();
      this._setTextStyles(this.ctx);

      var lines = this._wrapText(this.ctx, this.text);

      this.ctx.restore();
      this._textLines = lines;
      this._styleMap = this._generateStyleMap();
      return lines;
    },

    /**
     * When part of a group, we don't want the Textbox's scale to increase if
     * the group's increases. That's why we reduce the scale of the Textbox by
     * the amount that the group's increases. This is to maintain the effective
     * scale of the Textbox at 1, so that font-size values make sense. Otherwise
     * the same font-size value would result in different actual size depending
     * on the value of the scale.
     * @param {String} key
     * @param {Any} value
     */
    setOnGroup: function(key, value) {
      if (key === 'scaleX') {
        this.set('scaleX', Math.abs(1 / value));
        this.set('width', (this.get('width') * value) /
          (typeof this.__oldScaleX === 'undefined' ? 1 : this.__oldScaleX));
        this.__oldScaleX = value;
      }
    },

    /**
     * Returns 2d representation (lineIndex and charIndex) of cursor (or selection start).
     * Overrides the superclass function to take into account text wrapping.
     *
     * @param {Number} [selectionStart] Optional index. When not given, current selectionStart is used.
     */
    get2DCursorLocation: function(selectionStart) {
      if (typeof selectionStart === 'undefined') {
        selectionStart = this.selectionStart;
      }

      var numLines = this._textLines.length,
          removed  = 0;

      for (var i = 0; i < numLines; i++) {
        var line    = this._textLines[i],
            lineLen = line.length;

        if (selectionStart <= removed + lineLen) {
          return {
            lineIndex: i,
            charIndex: selectionStart - removed
          };
        }

        removed += lineLen;

        if (this.text[removed] === '\n' || this.text[removed] === ' ') {
          removed++;
        }
      }

      return {
        lineIndex: numLines - 1,
        charIndex: this._textLines[numLines - 1].length
      };
    },

    /**
     * Overrides superclass function and uses text wrapping data to get cursor
     * boundary offsets instead of the array of chars.
     * @param {Array} chars Unused
     * @param {String} typeOfBoundaries Can be 'cursor' or 'selection'
     * @returns {Object} Object with 'top', 'left', and 'lineLeft' properties set.
     */
    _getCursorBoundariesOffsets: function(chars, typeOfBoundaries) {
      var topOffset      = 0,
          leftOffset     = 0,
          cursorLocation = this.get2DCursorLocation(),
          lineChars      = this._textLines[cursorLocation.lineIndex].split(''),
          lineLeftOffset = this._getLineLeftOffset(this._getLineWidth(this.ctx, cursorLocation.lineIndex));

      for (var i = 0; i < cursorLocation.charIndex; i++) {
        leftOffset += this._getWidthOfChar(this.ctx, lineChars[i], cursorLocation.lineIndex, i);
      }

      for (i = 0; i < cursorLocation.lineIndex; i++) {
        topOffset += this._getHeightOfLine(this.ctx, i);
      }

      if (typeOfBoundaries === 'cursor') {
        topOffset += (1 - this._fontSizeFraction) * this._getHeightOfLine(this.ctx, cursorLocation.lineIndex)
          / this.lineHeight - this.getCurrentCharFontSize(cursorLocation.lineIndex, cursorLocation.charIndex)
          * (1 - this._fontSizeFraction);
      }

      return {
        top: topOffset,
        left: leftOffset,
        lineLeft: lineLeftOffset
      };
    },

    getMinWidth: function() {
      return Math.max(this.minWidth, this.dynamicMinWidth);
    },

    /**
     * Returns object representation of an instance
     * @method toObject
     * @param {Array} [propertiesToInclude] Any properties that you might want to additionally include in the output
     * @return {Object} object representation of an instance
     */
    toObject: function(propertiesToInclude) {
      return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
        minWidth: this.minWidth
      });
    }
  });
  /**
   * Returns fabric.Textbox instance from an object representation
   * @static
   * @memberOf fabric.Textbox
   * @param {Object} object Object to create an instance from
   * @return {fabric.Textbox} instance of fabric.Textbox
   */
  fabric.Textbox.fromObject = function(object) {
    return new fabric.Textbox(object.text, clone(object));
  };
  /**
   * Returns the default controls visibility required for Textboxes.
   * @returns {Object}
   */
  fabric.Textbox.getTextboxControlVisibility = function() {
    return {
      tl: false,
      tr: false,
      br: false,
      bl: false,
      ml: true,
      mt: false,
      mr: true,
      mb: false,
      mtr: true
    };
  };
  /**
   * Contains all fabric.Textbox objects that have been created
   * @static
   * @memberOf fabric.Textbox
   * @type Array
   */
  fabric.Textbox.instances = [];
})(typeof exports !== 'undefined' ? exports : this);
<|endoftext|>


<commit_before><commit_msg>implemented johnsons algorithm<commit_after>"use strict";

const dijkstra = require("./dijkstra");
const bellmanFord = require("./bellmanFord");

function johnson(graph) {
	const tempGraph = graph.clone(); // Operate on a clone of the graph to be side-effect-free.
	const tempStart = tempGraph.addNode();

	tempGraph.nodes.forEach(node => tempStart.connectTo(node, 0));

	const bfDistance = bellmanFord(tempGraph, tempStart);

	tempGraph.nodes.pop();

	tempGraph.nodes.forEach(from => {
		const fromDist = bfDistance.get(from);

		from.neighbors.forEach((weight, to) => {
			from.neighbors.set(to, weight + fromDist - bfDistance.get(to));
		});
	});

	const distance = new Map();

	tempGraph.nodes.forEach(from => {
		const fromDist = bfDistance.get(from);
		const tempDistance = dijkstra(tempGraph, from);

		tempDistance.forEach((distance, to) => {
			tempDistance.set(to, distance - fromDist + bfDistance.get(to));
		});

		distance.set(from, tempDistance);
	});

	return distance;
}

module.exports = johnson;
<|endoftext|>


<commit_before>const test = require('tape');
const dataLayer = require('../../lib/transforms/data-layer');

test('data-layer return right object', (t) => {
  t.plan(1);
  var data = [
    {
      key: 'part of archive',
      value: 'Babbage'
    },
    {
      key: 'maker',
      value: 'Babbage'
    },
    {
      key: 'Made',
      date: {
        value: '1820'
      }
    }
  ];

  const layer = dataLayer('documents', data);
  const expected = JSON.stringify({
    'Level1': 'documents',
    'Level2': 'Babbage',
    'Level3': 'Babbage',
    'Level4': '1820'
  });
  t.equal(layer, expected, 'The layer for documents is ok');
  t.end();
});
<commit_msg>add test for data layer<commit_after>const test = require('tape');
const dataLayer = require('../../lib/transforms/data-layer');

test('data-layer return right object for documnents', (t) => {
  t.plan(1);
  var data = [
    {
      key: 'part of archive',
      value: 'Babbage'
    },
    {
      key: 'maker',
      value: 'Babbage'
    },
    {
      key: 'Made',
      date: {
        value: '1820'
      }
    }
  ];

  const layer = dataLayer('documents', data);
  const expected = JSON.stringify({
    'Level1': 'documents',
    'Level2': 'Babbage',
    'Level3': 'Babbage',
    'Level4': '1820'
  });
  t.equal(layer, expected, 'The layer for documents is ok');
  t.end();
});

test('data-layer return right object for people', (t) => {
  t.plan(1);
  var data = [
    {
      key: 'occupation',
      value: [{value: 'mathematician'}]
    },
    {
      key: 'born in',
      value: 'Southwark'
    },
    {
      key: 'Nationality',
      value: 'British'
    }
  ];

  const layer = dataLayer('people', data);
  const expected = JSON.stringify({
    'Level1': 'people',
    'Level2': ['mathematician'],
    'Level3': 'British',
    'Level4': 'Southwark'
  });
  t.equal(layer, expected, 'The layer for people is ok');
  t.end();
});
<|endoftext|>