/* global Upload */

/**
 * smartflocollabapi
 */

/**
 * Represents remote SmartFlo™ DAM Service APIs.
 * It is expected that this class will be injected into an
 * Angular module as a service.
 * <br><br>
 * This AngularJS service is built upon the SmartFlo™ RESTful Service APIs 
 * for Digital Asset Management (DAM) in the SmartFlo™ system. It is provided 
 * to remove some of the complexities entailed by using RESTful APIs.
 * @description <strong>Warning!</strong> The SmartFlo™ APIs assume 2 global 
 * variables are set in 
 * $rootScope prior to using the APIs:
 * <ol>
 * <li><code>$rootScope.credential</code></li> set implicitely by the AuthAPI 
 * class upon success of the authenticate method
 * <li><code>$rootScope.site</code></li> set by the calling application to the  
 * domain (e.g., www.smartflo.biz) or IP address of the target SmartFlo™ system, 
 * possibly when initializing the module that declares an API service
 * </ol>
 * @copyright © Chalex Corp. 2002 - 2017. All rights reserved.
 * @example 
 * //
 * // Warning! The global '$rootScope.site' field value must be set 
 * // to the target SmartFlo™ domain prior to using theSmartFlo™ APIs. In this 
 * // example it is set by the configuration block of the 'SFdamApi' 
 * // module.
 * //
 * angular.module('sfdam', [])
 *      // inject API as a service
 *      .service('SFdamApi', ['$rootScope', '$http', DamAPI])
 *      .controller('SFdamCtrl', DamCtrl);
 *      .run(function ($rootScope, $location) { 
 *      // store the site for use by APIs
 *          $rootScope.site = $location.host();
 *          if ($location.port !== null & $location.port !== 80) {
 *          $rootScope.site += ':' + $location.port().toString();
 *     }
 * });
 * @constructor 
 * @param {object} $rootScope - The topmost parent scope in AngularJS.
 * @param {service} $http - The core AngularJS service that facilitates 
 * communication with the remote HTTP servers via the browser's XMLHttpRequest 
 * object or via JSONP.
 * @returns {class} A constructor that will be instantiated.
 */
class DamAPI {
    constructor($rootScope, $http) {
        return {
            /** 
             * Retrieves a hierarchical ordering of nested objects representing 
             * archives and suitable for display in an archive tree.
             * @function 
             * @name getUserArchives
             * @description Retrieves object containing Array of top-level 
             * hierarchically ordered 
             * archive trees representing the user's set of archives and the 
             * sets of archives to which the user has been granted shared 
             * access.
             * <pre><code>
             *                      Archive Trees
             *       _____________________|_______________________
             *      |                                             |
             *   Tree 1                                        Tree 2
             *      |                                             |
             *      -- archives Array             archives Array --
             *      -- folders Array               folders Array --
             *      -- id                                     id --
             *      -- name                                 name --  
             *      
             * At each level of a given tree is a folder object naming the node 
             * in the tree and holding archive objects and sub-folder objects. 
             *
             * archive {
             *      id: [40 character unique hash],
             *      archiveid: [full identity of archive],
             *      name: [display name of archive]
             * };
             * 
             * folder {
             *      id: [full path in heirarchy tree identifying the current folder],
             *      name: [the current folder name],
             *      archives: [Array of srchive objects],
             *      folders: [Array of folder objects]
             * };
             * <code><pre>
             * @memberof DamAPI.prototype
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      archiveTrees: [Object containing Array of user archive trees]
             * };
             * </code></pre>
             * @example 
             * function getUserArchives() {
             *      SFdamApi.getUserArchives(handleGetUserArchives);
             * }
             * 
             * function handleGetUserArchives(result) {
             *      if ('0' === status) {
             *          $scope.archiveTrees = result.archiveTrees;
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getUserArchives: function(successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = get('archives?');
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var archivedata = {};
                            archivedata.date = new Date();
                            if (jsonData.dam_service_response) {
                                archivedata.api = jsonData.dam_service_response.api;
                                archivedata.status = jsonData.dam_service_response.error_response.response_code;
                                archivedata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === archivedata.status) {
                                if (jsonData.dam_service_response.response &&
                                        jsonData.dam_service_response.response.archive_tree) {
                                    var archiveTrees = jsonData.dam_service_response.response.archive_tree
                                    fixArchiveFolders(archiveTrees.folder);
                                    archivedata.archiveTrees = archiveTrees;
                                } else {
                                    archivedata.archiveTrees =  null;
                                }
                                successHandler(archivedata);
                            } else {
                                failureHandler(archivedata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Retrieves a URL to a zip file after exporting a selected list of 
             * attachments.
             * @function 
             * @name getAttachmentsExport
             * @description Retrieves a URL to a zip file after exporting a 
             * selected list of attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the list of
             * attachment IDs and a flag indicating whther or not to include 
             * the previews in the export.
             * <pre><code>
             * var params = {
             *      attachmentids: attachmentids [comma separated list of attachment IDs],
             *      previews: false | true [flag indication whether to include previews 
             *          as well as the original content in the export]
             * };
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      exportUrl: [URL to zip file of exported attachments]
             * };
             * </code></pre>
             * @example 
             * function getAttachmentsExport() {
             *      var params = {
             *          attachmentids: attachmentids,
             *          previews: false
             *      };
             *      SFdamApi.getAttachmentsExport(params, handleGetAttachmentsExport);
             * }
             * 
             * function handleGetAttachmentsExport(result) {
             *      if ('0' === status) {
             *          $window.open(result.exportUrl);
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getAttachmentsExport: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = get('attachments/exports?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var exportdata = {};
                            exportdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                exportdata.api = jsonData.dam_service_response.api;
                                exportdata.status = jsonData.dam_service_response.error_response.response_code;
                                exportdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === exportdata.status) {
                                if (jsonData.dam_service_response.response.export_url) {
                                    var part = jsonData.dam_service_response.response.export_url;
                                    var exportUrl = 'https://' + $rootScope.site + part;
                                    exportdata.exportUrl = exportUrl;
                                }
                                successHandler(exportdata);
                            } else {
                                failureHandler(exportdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Retrieves all the attachments in a given archive.
             * @function 
             * @name getArchiveAttachments
             * @description Retrieves all the attachments in a given archive.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the list of
             * archive IDs for which attachments will be retrieved.
             * <pre><code>
             * var params = {
             *      archiveid: attachmentids [comma separated list of archive IDs]
             * };
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      currentAttachments: [Array of attachments]
             * };
             * 
             * var attachment {
             *      affiliate: [SmartFlo™ affiliate to which the attachment belongs],
             *      accessible_by: [SmartFlo™ user ID or group access control],
             *      attachment_id: [unique SmartFlo™ ID for attachment],
             *      creator:SmartFlo™ user ID of user who uploaded the attachment content],
             *      creation_date: [date and time attachment content was uploaded],
             *      attachment_approval_status: [one of: 
             *          attachment.no.status (1)
             *          attachment.rejected (2)
             *          attachment.approved (3)
             *          attachment.pending (4)
             *          attachment.canceled (5)],
             *      attachment_approval_status_number: [one of: 1, 2, 3, 4, 5],
             *      display_name: user edited name given to attachment],
             *      checked_out_by: [if checked out, the user ID of the user who has checked it out],
             *      joinId: [full name of job to which the attachment belongs],
             *      comments: [Array of attachment comments],
             *      tags: [Array of attachment tags],
             *      properties: [Array of attachment properties],
             *      content: [URL to original content],
             *      content_mime_type:[MIME type of content],   
             *      content_low_res: [URL to reduced version of content suitable for use as a preview -
             *              for video content, an flv formated low-res movie, otherwise a reduced 
             *              image of bitmapped content and pdfs],
             *      content_preview: [URL to reduced version of content, used only for video files - 
             *              a still shot image from the video, suitable for use as a static preview],
             *      content_thumbnail: [URL to a reduced bitmapped image suitable for display as a thumbnail in a content gallery],
             *      size_in_bytes: [size of original content],
             *      previewImage: [URL to recommended content to display in web preview of image types - null if video type],
             *      previewVideo: [URL to recommended content to display in web preview of image types - null if image type],
             *      unsupportedMIME: [true or false depending upon whether the MIME type is supported in SmartFlo™]
             * };
             * 
             * </code></pre>
             * @example 
             * function getArchiveAttachments() {
             *      var params = {
             *          archiveid: attachmentids [comma separated list of archive IDs]
             *      };
             *      SFdamApi.getArchiveAttachments(params, handleGetArchiveAttachments);
             * }
             * 
             * function handleGetArchiveAttachments(result) {
             *      if ('0' === status) {
             *          $scope.currentAttachments = result.currentAttachments;
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getArchiveAttachments: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = get('attachments?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentdata = {};
                            attachmentdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentdata.api = jsonData.dam_service_response.api;
                                attachmentdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentdata.status) {
                                if (jsonData.dam_service_response.response.archive_list &&
                                        jsonData.dam_service_response.response.archive_list.archive &&
                                        jsonData.dam_service_response.response.archive_list.archive.attachment_list &&
                                        jsonData.dam_service_response.response.archive_list.archive.attachment_list.attachment)
                                {
                                    var currentAttachments = jsonData.dam_service_response.response.archive_list.archive.attachment_list.attachment;
                                    if (!angular.isArray(currentAttachments)) {
                                        var arr = [];
                                        arr.push(currentAttachments);
                                        currentAttachments = arr;
                                    }
                                    attachmentdata.currentAttachments = fixAttachmentArrays(currentAttachments);
                                } else {
                                    attachmentdata.currentAttachments = [];
                                }
                                successHandler(attachmentdata);
                            } else {
                                failureHandler(attachmentdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Retrieves job attachments where search terms are 
             * used to find matching jobs.
             * @function 
             * @name getSearchAttachments
             * @description Retrieves job attachments where search terms are 
             * used to find matching jobs, then attachments from those 
             * jobs returned:
             * <ol>
             * <li>A jobs term search is performed to find matching jobs</li>
             * jobtype === running (running and suspended) | completed
             * <br><b>See:</b> JobsAPI.getMatchingJobboardTasks <br>
             * <li>The attachments belonging to these jobs are then found</li>
             * <li>The attachment set is then filtered by the approvableonly value</li>
             * approvableonly === true | false 
             * </ol>
             * The job fields searched are same as those used to find jobs:
             * <ul>
             * <li><code>job data</code></li>
             *    brand, category, client_company, distribution_channel, item_description, item_id, 
             *    product, project_id, task_id, task_name, project_manager, project_name, 
             *    workflow_id, workflow_definition, resource_group, status
             * <br><br>
             * <li><code>extended job metadata</code></li>
             *    account_name, art_due_date, creative_agency, customer_position, 
             *    file_destination, master_artwork_template, num_of_units, production_schedule, 
             *    program_owner, team, states_program, program_classification, 
             *    program_type, legal_owner, alliance_owner, prize_logic_owner, program_manager_owner
             * </ul>
             
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the list of
             * attachment IDs and a flag indicating whther or not to include 
             * the previews in the export.
             * <pre><code>
             * var params = {
             *      approvableonly: [true | false],
             *      jobtype: [running | completed],
             *      terms: terms,
             *      pageSize: $scope.gallerySettings.itemsPerPage, [number of attachments per page]
             *      page: $scope.gallerySettings.currentPage [current page of resut set]
             * };
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      searchAttachments: [the list of attachments matching the search]
             * };
             * </code></pre>
             * @example 
             * function getSearchAttachments() {
             *      var params = {
             *          approvableonly: [true | false],
             *          jobtype: [running | completed],
             *          terms: terms,
             *          pageSize: $scope.gallerySettings.itemsPerPage, [number of attachments per page]
             *          page: $scope.gallerySettings.currentPage [current page of resut set]
             *      };
             *      SFdamApi.getSearchAttachments(params, handleGetSearchAttachments);
             * }
             * 
             * function handleGetSearchAttachments(result) {
             *      if ('0' === status) {
             *          $scope.searchAttachments = result.searchAttachments;
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getSearchAttachments: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = get('attachments?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentdata = {};
                            attachmentdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentdata.api = jsonData.dam_service_response.api;
                                attachmentdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentdata.status) {
                                if (jsonData.dam_service_response.response &&
                                        jsonData.dam_service_response.response.attachment_list &&
                                        jsonData.dam_service_response.response.attachment_list.attachment)
                                {
                                    var searchAttachments = jsonData.dam_service_response.response.attachment_list.attachment;
                                    if (!angular.isArray(searchAttachments)) {
                                        var arr = [];
                                        arr.push(searchAttachments);
                                        searchAttachments = arr;
                                    }
                                    attachmentdata.searchAttachments = searchAttachments;
                                    attachmentdata.totalItems = jsonData.dam_service_response.response.total_attachments;
                                } else {
                                    attachmentdata.searchAttachments = [];
                                    attachmentdata.totalItems = 0;
                                }
                                successHandler(attachmentdata);
                            } else {
                                failureHandler(attachmentdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Checks-in a locked attachment.
             * @function 
             * @name postFileCheckIn
             * @requires ng-file-upload.min.js
             * @description Checks-in a locked attachment and replaces it with 
             * a new version.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the list of
             * attachment IDs and a flag indicating whther or not to include 
             * the previews in the export.
             * <pre><code>
             * var params = {
             *      lockstatus: checkedin,
             *      attachmentid: [comma separated list of attachmentids to check-in] 
             * };
             * </code></pre>
             * @param {object} file - A JavaScript File object.
             * @param {object} uploader - The Upload object from ng-file-upload.min.js. 
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             * };
             * </code></pre>
             * @example 
             * function postFileCheckIn() {
             *      var params = {
             *          lockstatus: "checkedin",
             *          attachmentid: [comman separated list of attachmentids to check-in]
             *      };
             *      SFdamApi.postFileCheckIn(params, handlePostFileCheckIn);
             * }
             * 
             * function handlePostFileCheckIn(result) {
             *      if ('0' === status) {
             *          $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postFileCheckIn: function (params, file, uploader, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var site = $rootScope.site;
                var token = $rootScope.credential.token;
                var reqUrl = url(site, token, 'attachments/locks?', params);
                file.upload = uploader.upload({
                    url: reqUrl,
                    resumeSizeUrl: null,
                    resumeChunkSize: null,
                    headers: null,
                    data: {attachment: file},
                    progress: function (e) {
                        var a = 0;
                    }
                });

                file.upload.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var filecheckindata = {};
                            filecheckindata.date = new Date();
                            if (jsonData.dam_service_response) {
                                filecheckindata.api = jsonData.dam_service_response.api;
                                filecheckindata.status = jsonData.dam_service_response.error_response.response_code;
                                filecheckindata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === filecheckindata.status) {
                                successHandler(filecheckindata);
                            } else {
                                failureHandler(filecheckindata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Uploads a digital file and creates an attachment (asset).
             * @function 
             * @name postFileUpload
             * @requires ng-file-upload.min.js
             * @description Uploads a digital file and creates an attachment 
             * (asset).
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the list of
             * attachment IDs and a flag indicating whther or not to include 
             * the previews in the export.
             * <pre><code>
             * var params = {
             *          archiveid: $scope.currentTask.job_id,
             *          taglist: $scope.formData.attachmentTags
             *          collaborationcycle: [cycle name - used iff task is collaboration task],
             *          isprivate: 'false' [flag indicating to keep from other reviewers but not monitor - used iff task is collaboration task]
             * };
             * </code></pre>
             * @param {object} file - A JavaScript File object.
             * @param {object} uploader - The Upload object from ng-file-upload.min.js. 
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             * };
             * </code></pre>
             * @example 
             * function postFileUpload() {
             *      var params = {
             *          archiveid: $scope.currentTask.job_id,
             *          taglist: $scope.formData.attachmentTags
             *          collaborationcycle: [cycle name - used iff task is collaboration task],
             *          isprivate: 'false' [flag indicating to keep from other reviewers but not monitor - used iff task is collaboration task]
             *      };
             *      SFdamApi.postFileUpload(params, handlePostFileUploadn);
             * }
             * 
             * function handlePostFileUpload(result) {
             *      if ('0' === status) {
             *          $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postFileUpload: function (params, file, uploader, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var site = $rootScope.site;
                var token = $rootScope.credential.token;
                var reqUrl = url(site, token, 'attachments?', params);
                file.upload = uploader.upload({
                    url: reqUrl,
                    resumeSizeUrl: null,
                    resumeChunkSize: null,
                    headers: null,
                    data: {attachment: file},
                    progress: function (e) {
                        var a = 0;
                    }
                });
                file.upload.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var uploadfiledata = {};
                            uploadfiledata.date = new Date();
                            if (jsonData.dam_service_response) {
                                uploadfiledata.api = jsonData.dam_service_response.api;
                                uploadfiledata.status = jsonData.dam_service_response.error_response.response_code;
                                uploadfiledata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === uploadfiledata.status) {
                                successHandler(uploadfiledata);
                            } else {
                                failureHandler(uploadfiledata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Retrieves the available properties.
             * @function 
             * @name getProperties
             * @description Retrieves Array holding the available properties 
             * the current user can assign to attachments.
             * @memberof DamAPI.prototype
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      currentProperties: [the Array of properties]
             * };
             * </code></pre>
             * @example 
             * function handleGetProperties() {
             *      SFdamApi.getProperties(handleGetProperties);
             * }
             * 
             * function handleGetProperties(result) {
             *      if ('0' === status) {
             *          $scope.currentProperties = result.currentProperties;
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getProperties: function (successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = get('attachments/properties');
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var propertydata = {};
                            propertydata.date = new Date();
                            if (jsonData.dam_service_response) {
                                propertydata.api = jsonData.dam_service_response.api;
                                propertydata.status = jsonData.dam_service_response.error_response.response_code;
                                propertydata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === propertydata.status) {
                                if (jsonData.dam_service_response.response &&
                                        jsonData.dam_service_response.response.property_list &&
                                        jsonData.dam_service_response.response.property_list.property)
                                {
                                    var currentProperties = jsonData.dam_service_response.response.property_list.property;
                                    if (!angular.isArray(currentProperties)) {
                                        var arr = [];
                                        arr.push(currentProperties);
                                        currentProperties = arr;
                                    }
                                    propertydata.currentProperties = currentProperties;
                                } else {
                                    propertydata.currentProperties = [];
                                }
                                successHandler(propertydata);
                            } else {
                                failureHandler(propertydata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Retrieves a URL to a vProof (Viki Proofing) collaboration session.
             * @function 
             * @name getVproofSession
             * @description Retrieves a URL to a vProof (Viki Proofing) 
             * collaboration session. Before this method can be used, the
             * SmartFlo™ attachment must be uploaded to vProof.
             * @see postVproofAttachments
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides the view mode
             * and a comma separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             * params {
             *      viewmode: $scope.vproofAnnotateMode,
             *      attachmentids: $scope.vproofAttachmentIds
             * };
             * 
             * The "viewmode" must be one of:
             * </code></pre>
             * <ul>
             * <li>Default</li>
             * <li>SinglePage</li>
             * <li>Book</li>
             * <li>Compare</li>
             * <li>Grid</li>
             * <li>List</li>
             * <li>Report</li>
             * <li>Tile</li>
             * </ul>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made],
             *      vproofUrl: [URL to vProof session]
             * };
             * </code></pre>
             * @example 
             * $scope.function getVproofSession() {
             *      params {
             *          viewmode: $scope.vproofAnnotateMode,
             *          attachmentids: $scope.vproofAttachmentIds
             *      };
             *      SFdamApi.getVproofSession(params, handleGetVproofSession);
             * }
             * 
             * function handleGetVproofSession(result) {
             *      if ('0' === status) {
             *          var vproofUrl = result.vproofUrl;
             *          if (vproofUrl && vproofUrl.length > 0) {
             *              $window.open(vproofUrl);
             *          }
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            getVproofSession: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                params.sessiontype = 'Advanced';                
                var request = get('attachments/vproofsessions?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var vproofsessiondata = {};
                            vproofsessiondata.date = new Date();
                            if (jsonData.dam_service_response) {
                                vproofsessiondata.api = jsonData.dam_service_response.api;
                                vproofsessiondata.status = jsonData.dam_service_response.error_response.response_code;
                                vproofsessiondata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === vproofsessiondata.status) {
                                if (jsonData.dam_service_response.response &&
                                        jsonData.dam_service_response.response.vproof_session_url)
                                {
                                    vproofsessiondata.vproofUrl = jsonData.dam_service_response.response.vproof_session_url;
                                }
                                successHandler(vproofsessiondata);
                            } else {
                                failureHandler(vproofsessiondata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts SmartFlo™ attachments to the vProof collaboration service.
             * @function 
             * @name postVproofAttachments
             * @description Posts SmartFlo™ attachments to the vProof 
             * collaboration service.
             * @see getVproofSession
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             * params {
             *      attachmentids: [comma separated list of SmartFlo™ attachment IDs]
             * };
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postVproofAttachments() {
             *      params {
             *          attachmentids: $scope.vproofAttachmentIds,
             *      };
             *      SFdamApi.postVproofAttachments(params, handlePostVproofAttachments);
             * }
             * 
             * function handlePostVproofAttachments(result) {
             *      if ('0' === status) {
             *          if ($scope.vproofAttachmentIds.length > 0) {
             *              var params = {
             *                  viewmode: $scope.vproofAnnotateMode,
             *                  attachmentids: $scope.vproofAttachmentIds
             *              };
             *              $scope.getVproofSession(params, handleGetVproofSession);
             *          }
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postVproofAttachments: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/vproofuploads?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var vproofsessiondata = {};
                            vproofsessiondata.date = new Date();
                            if (jsonData.dam_service_response) {
                                vproofsessiondata.api = jsonData.dam_service_response.api;
                                vproofsessiondata.status = jsonData.dam_service_response.error_response.response_code;
                                vproofsessiondata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === vproofsessiondata.status) {
                                successHandler(vproofsessiondata);
                            } else {
                                failureHandler(vproofsessiondata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts a status change to a set of SmartFo™ attachments.
             * @function 
             * @name postAttachmentStatus
             * @description Posts a status change to a set of SmartFo™ 
             * attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of SmartFlo™ attachment IDs and a new status to
             * apply to each attachment identified by those IDs. 
             * <pre><code>
             * params {
             *      attachmentids:[a comma separated list of SmartFlo™ attachment IDs],
             *      status: [the status]
             * };
             * 
             * Status must be one of:
             * </code></pre>
             * <ul>
             * <li>attachment.no.status</li>
             * <li>attachment.rejected</li>
             * <li>attachment.approved</li>
             * <li>attachment.pending</li>
             * <li>attachment.canceled</li>
             * <li>attachment.annotated</li>
             * </ul>
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postAttachmentStatus() {
             *      params {
             *          attachmentids: $scope.selectedAttachments,
             *          status: $scopeSelectedStatus
             *      };
             *      SFdamApi.postAttachmentStatus(params, handlePostAttachmentStatus);
             * }
             * 
             * function handlePostAttachmentStatus(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postAttachmentStatus: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/statuses?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentstatusdata = {};
                            attachmentstatusdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentstatusdata.api = jsonData.dam_service_response.api;
                                attachmentstatusdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentstatusdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentstatusdata.status) {
                                successHandler(attachmentstatusdata);
                            } else {
                                failureHandler(attachmentstatusdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts a new comment to a given set of attachments.
             * @function 
             * @name postAttachmentComment
             * @description Posts a new comment to a given set of attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of SmartFlo™ attachment IDs, a comment to apply and
             * a flag indicating wethere the comment should be restricted to 
             * members of the group, "Private Comments User. 
             * <pre><code>
             *params {
             *      attachmentids: $scope.selectedAttachments,
             *      comment: [comment text],
             *      isprivate: [flag indicating whether the comment is 
             *          private or not to members of the group 
             *      "Private Comments User"],
             *};
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postAttachmentComment() {
             *      params {
             *          attachmentids: $scope.selectedAttachments,
             *          comment: [comment text],
             *          isprivate: [flag indicating whether the comment is 
             *              private or not to members of the group 
             *              "Private Comments User"],
             *      };
             *      SFdamApi.postAttachmentComment(params, handlePostAttachmentComment);
             * }
             * 
             * function handlePostAttachmentComment(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postAttachmentComment: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/comments?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentcommentsdata = {};
                            attachmentcommentsdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentcommentsdata.api = jsonData.dam_service_response.api;
                                attachmentcommentsdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentcommentsdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentcommentsdata.status) {
                                successHandler(attachmentcommentsdata);
                            } else {
                                failureHandler(attachmentcommentsdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Deletes a set of attachments from the SmartFlo™ system.
             * @function 
             * @name deleteAttachments
             * @see postDeleteAttachments
             * @description Deletes a set of attachments from the SmartFlo™ system.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             *params {
             *      attachmentids: [comma separated list of attachment IDs]
             *};
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function deleteAttachments() {
             *      params {
             *          attachmentids: $scope.selectedAttachments
             *      };
             *      SFdamApi.deleteAttachments(params, handleDeleteAttachments);
             * }
             * 
             * function handleDeleteAttachments(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            deleteAttachments: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = del('attachments?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var delattachmentdata = {};
                            delattachmentdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                delattachmentdata.api = jsonData.dam_service_response.api;
                                delattachmentdata.status = jsonData.dam_service_response.error_response.response_code;
                                delattachmentdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === delattachmentdata.status) {
                                successHandler(delattachmentdata);
                            } else {
                                failureHandler(delattachmentdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Deletes a set of attachments from the SmartFlo™ system.
             * @function 
             * @name postDeleteAttachments
             * @see deleteAttachments
             * @description Deletes a set of attachments from the SmartFlo™ system.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             *params {
             *      attachmentids: [comma separated list of attachment IDs]
             *};
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postDeleteAttachments() {
             *      params {
             *          attachmentids: $scope.selectedAttachments
             *      };
             *      SFdamApi.postDeleteAttachments(params, handlePostDeleteAttachments);
             * }
             * 
             * function handlePostDeleteAttachments(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postDeleteAttachments: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var delattachmentdata = {};
                            delattachmentdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                delattachmentdata.api = jsonData.dam_service_response.api;
                                delattachmentdata.status = jsonData.dam_service_response.error_response.response_code;
                                delattachmentdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === delattachmentdata.status) {
                                successHandler(delattachmentdata);
                            } else {
                                failureHandler(delattachmentdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts a set of tags to a set of SmartFlo™ attachments.
             * @function 
             * @name postAttachmentTags
             * @description Posts a set of tags to a set of SmartFlo™ attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a comma 
             * separated list of tags to a set of attachments identified by 
             * a comma separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             *params {
             *      taglist: [comma separated list of tags],
             *      attachmentids: [comma separated list of attachment IDs]
             *};
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postAttachmentTags() {
             *      params {
             *          taglist: tag1, tag2,
             *          attachmentids: $scope.selectedAttachments
             *      };
             *      SFdamApi.postAttachmentTags(params, handlePostAttachmentTags);
             * }
             * 
             * function handlePostAttachmentTags(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postAttachmentTags: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/tags?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmenttagsdata = {};
                            attachmenttagsdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmenttagsdata.api = jsonData.dam_service_response.api;
                                attachmenttagsdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmenttagsdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmenttagsdata.status) {
                                successHandler(attachmenttagsdata);
                            } else {
                                failureHandler(attachmenttagsdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts a lock status on a set of SmartFlo™ attachments.
             * @function 
             * @name postAttachmentLocks
             * @description Posts a lock status on a set of SmartFlo™ attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a lock status 
             * to apply to a set of attachments identified by 
             * a comma separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             * params {
             *      lockstatus: [one of: unlocked, checkedout, checkedin],
             *      attachmentids: [comma separated list of attachment IDs]
             * };
             * 
             * The lock status must be one of:
             * </code></pre>
             * <ul>
             * <li>unlocked</li>
             * <li>checkedout</li>
             * <li>checkedin</li>
             * </ul>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postAttachmentLocks() {
             *      params {
             *          lockstatus: checkedout,
             *          attachmentids: $scope.selectedAttachments
             *      };
             *      SFdamApi.postAttachmentLocks(params, handlePostAttachmentLocks);
             * }
             * 
             * function handlePostAttachmentLocks(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postAttachmentLocks: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/locks?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentlocksdata = {};
                            attachmentlocksdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentlocksdata.api = jsonData.dam_service_response.api;
                                attachmentlocksdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentlocksdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentlocksdata.status) {
                                successHandler(attachmentlocksdata);
                            } else {
                                failureHandler(attachmentlocksdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            },
            /** 
             * Posts an attachment property to a set of SmartFlo™ attachments.
             * @function 
             * @name postAttachmentProperties
             * @see getProperties
             * @description Posts an attachment property to a set of SmartFlo™ 
             * attachments.
             * @memberof DamAPI.prototype
             * @param {object} params - An object that provides a property name 
             * and a property value to apply to a set of attachments identified 
             * by a comma separated list of SmartFlo™ attachment IDs. 
             * <pre><code>
             * params {
             *      propertyname: property,
             *      propertyvalue: [value for property],
             *      attachmentids: [comma separated list of attachment IDs]
             * };
             * </code></pre>
             * @param {function} successHandler - A function that is 
             * called back upon success.
             * @param {function} [failureHandler] - An optional 
             * function that is called back upon failure. If none is specified,
             * the success callback is called with failure data.
             * <code>status, statusMessage, api</code> and <code>date</code>.
             * @throws (string) "missing.callback.exception"
             * @throws {} $http errors
             * @returns {object} A result object. 
             * <pre><code>
             * var result {
             *      status: 0,
             *      statusMessage: "auth.no.errors", 
             *      api: "auth::post::users::passwords", // name of the call
             *      date: [Date object providing time call was made]
             * };
             * </code></pre>
             * @example 
             * function postAttachmentProperties() {
             *      params {
             *          propertyname: "Source",
             *          propertyvalue: "Weather Service",
             *          attachmentids: $scope.selectedAttachments
             *      };
             *      SFdamApi.postAttachmentProperties(params, handlePostAttachmentProperties);
             * }
             * 
             * function handlePostAttachmentProperties(result) {
             *      if ('0' === status) {
             *           $scope.attachmentThumbnailsSelected = [];
             *           $scope.getJobAttachments();
             *      } else {
             *          $scope.errorCode = result.status;
             *          $scope.errorMessage = result.statusMessage;
             *      }
             * }
             */
            postAttachmentProperties: function (params, successHandler, failureHandler) {
                // require a successHandler
                if (!successHandler) {
                    throw new Error("missing.callback.exception");
                }

                // if no failureHandler is defined, use successHandler as 
                // general callback
                if (!failureHandler) {
                    failureHandler = successHandler;
                }

                var request = post('attachments/properties?', params);
                request.then(
                        function (result) {
                            var x2js = new X2JS();
                            var jsonData = x2js.xml_str2json(result.data);
                            var attachmentlocksdata = {};
                            attachmentlocksdata.date = new Date();
                            if (jsonData.dam_service_response) {
                                attachmentlocksdata.api = jsonData.dam_service_response.api;
                                attachmentlocksdata.status = jsonData.dam_service_response.error_response.response_code;
                                attachmentlocksdata.statusMessage = jsonData.dam_service_response.error_response.response_message;
                            }

                            if ('0' === attachmentlocksdata.status) {
                                successHandler(attachmentlocksdata);
                            } else {
                                failureHandler(attachmentlocksdata);
                            }
                        },
                        function (errorInfo) {
                            throw new Error(errorInfo.data);
                        });
            }
        };

        function get(path, param) {
            return request("GET", path, param);
        }

        function post(path, param, data) {
            return request("POST", path, param, data);
        }

        function put(path, param, data) {
            return request("PUT", path, param, data);
        }

        function del(path, param) {
            return request("DELETE", path, param);
        }

        function request(verb, path, param, data) {
            var site = $rootScope.site;
            var token = null;
            if (validData($rootScope.credential) && validData($rootScope.credential.token)) {
                token = $rootScope.credential.token;
            }
            var req = {
                method: verb,
                url: url(site, token, path, param),
                headers: {
                    'Authorization': getAuthHeader()
                },
                data: validData(data) ? data : null
            };
            return $http(req);
        }

        function url(site, token, path, param) {
            var reqUrl = 'https://' + site + '/api/' + token + '/dam/';
            if (validData(path)) {
                reqUrl += path;
            }

            if (validData(param)) {
                reqUrl += $.param(param);
            }
            return reqUrl;
        }

        function validData(data) {
            return data !== undefined && data !== null;
        }

        function getAuthHeader() {
            return "";
        }
        
        function fixAttachmentComments(attachment) {
            if (!attachment.comment_list || !attachment.comment_list.comment) {
                // comments undefined == true
                attachment.comments = [];
            } else if (!angular.isArray(attachment.comment_list.comment)) {
                // not array = true
                var savedComment = attachment.comment_list.comment;
                attachment.comments = [];
                attachment.comments[0] = savedComment;
            } else {
                attachment.comments = attachment.comment_list.comment;
            }
        }

        function fixAttachmentProperties(attachment) {
            if (!attachment.property_list || !attachment.property_list.property) {
                // comments undefined == true
                attachment.properties = [];
            } else if (!angular.isArray(attachment.property_list.property)) {
                // not array = true
                var savedProperties = attachment.property_list.property;
                attachment.properties = [];
                attachment.properties[0] = savedProperties;
            } else {
                attachment.properties = attachment.property_list.property;
            }
        }

        function fixAttachmentTags(attachment) {
            if (!attachment.tag_list || !attachment.tag_list.tag) {
                // comments undefined == true
                attachment.tags = [];
            } else if (!angular.isArray(attachment.tag_list.tag)) {
                // not array = true
                var savedTags = attachment.tag_list.property;
                attachment.tags = [];
                attachment.tags[0] = savedTags;
            } else {
                attachment.tags = attachment.tag_list.tag;
            }
        }

        function determinePreviewImage(attachment) {
            var mimeType = attachment['content_mime_type'].toString();
            if (mimeType.indexOf('image/') === -1 &&
                    mimeType.indexOf('application/pdf') === -1 &&
                    mimeType.indexOf('video/mp4') === -1 &&
                    mimeType.indexOf('video/mpeg') === -1 &&
                    mimeType.indexOf('video/x-ms-wmv') === -1 &&
                    mimeType.indexOf('video/x-msvideo') === -1) {
                attachment.unsupportedMIME = true;
                attachment.previewImage = attachment['content_low_res'];
            } else {
                attachment.unsupportedMIME = false;
                // is this a video? We will need to inject a video tag and listen
                if (mimeType.indexOf('video/mp4') !== -1) {
                    attachment.previewVideo = attachment['content'];
                } else if (mimeType.indexOf('video/mpeg') !== -1) {
                    attachment.previewImage = attachment['content_preview'];
                } else if (mimeType.indexOf('video/x-ms-wmv') !== -1) {
                    attachment.previewImage = attachment['content_thumbnail'];
                } else if (mimeType.indexOf('video/x-msvideo') !== -1) {
                    attachment.previewImage = attachment['content_thumbnail'];
                } else if (mimeType.indexOf('application/pdf') !== -1) {
                    attachment.previewImage = attachment['content_low_res'];
                } else {
                    attachment.previewImage = attachment['content'];
                }
            }
        }

        function fixAttachmentArrays(attachments) {
            for (var i = 0; i < attachments.length; i++) {
                fixAttachmentComments(attachments[i]);
                fixAttachmentTags(attachments[i]);
                fixAttachmentProperties(attachments[i]);
                determinePreviewImage(attachments[i]);
            }
        }
        
        function fixArchives(archives) {
            if (!angular.isArray(archives)) {
                var arr = [];
                arr[0] = archives;
                archives = arr;
            }
            for (var i = 0; i < archives.length; i++) {
                var archive = archives[i];
                archive.archiveid = archive._archive_id;
                archive.id = archive._id;
                archive.name = archive._name;
                delete archive._archive_id;
                delete archive._id;
                delete archive._name;
            }
        }
        
        function fixArchiveFolders(folders) {
            if (!angular.isArray(folders)) {
                var arr = [];
                arr[0] = folders;
                folders = arr;
            }
            for (var i = 0; i < folders.length; i++) {
                var folder = folders[i];
                folder.id = folder._id;
                folder.name = folder._name;
                if (folder.archive) {
                    fixArchives(folder.archive);
                    folder.archives = folder.archive;
                }
                if (folder.folder) {
                    fixArchiveFolders(folder.folder);
                    folder.folders = folder.folder;
                }
                
                delete folder._id;
                delete folder._name;
                
                delete folder.archive;
                delete folder.folder;
            }
        }
    }
}