From 4ac3be445ebb041a503bbd2999f5d08dbc9a8578 Mon Sep 17 00:00:00 2001 From: Nathan Kinkade Date: Sat, 23 Jul 2011 17:00:38 +0000 Subject: Added a new module for annotating photos. --- .../controllers/admin_photoannotation.php | 312 +++++++++++ .../controllers/photoannotation.php | 251 +++++++++ modules/photoannotation/css/colorpicker.css | 161 ++++++ modules/photoannotation/css/photoannotation.css | 267 ++++++++++ .../photoannotation/helpers/photoannotation.php | 275 ++++++++++ .../helpers/photoannotation_block.php | 40 ++ .../helpers/photoannotation_event.php | 248 +++++++++ .../helpers/photoannotation_installer.php | 119 +++++ .../helpers/photoannotation_theme.php | 81 +++ modules/photoannotation/images/blank.gif | Bin 0 -> 49 bytes .../images/colorpicker_background.png | Bin 0 -> 1897 bytes modules/photoannotation/images/colorpicker_hex.png | Bin 0 -> 532 bytes .../photoannotation/images/colorpicker_hsb_b.png | Bin 0 -> 970 bytes .../photoannotation/images/colorpicker_hsb_h.png | Bin 0 -> 1012 bytes .../photoannotation/images/colorpicker_hsb_s.png | Bin 0 -> 1171 bytes .../photoannotation/images/colorpicker_indic.gif | Bin 0 -> 86 bytes .../photoannotation/images/colorpicker_overlay.png | Bin 0 -> 10355 bytes .../photoannotation/images/colorpicker_rgb_b.png | Bin 0 -> 970 bytes .../photoannotation/images/colorpicker_rgb_g.png | Bin 0 -> 1069 bytes .../photoannotation/images/colorpicker_rgb_r.png | Bin 0 -> 1066 bytes .../photoannotation/images/colorpicker_select.gif | Bin 0 -> 78 bytes .../photoannotation/images/colorpicker_submit.png | Bin 0 -> 1016 bytes .../photoannotation/images/custom_background.png | Bin 0 -> 1916 bytes modules/photoannotation/images/custom_hex.png | Bin 0 -> 562 bytes modules/photoannotation/images/custom_hsb_b.png | Bin 0 -> 1097 bytes modules/photoannotation/images/custom_hsb_h.png | Bin 0 -> 970 bytes modules/photoannotation/images/custom_hsb_s.png | Bin 0 -> 1168 bytes modules/photoannotation/images/custom_indic.gif | Bin 0 -> 86 bytes modules/photoannotation/images/custom_rgb_b.png | Bin 0 -> 1008 bytes modules/photoannotation/images/custom_rgb_g.png | Bin 0 -> 1069 bytes modules/photoannotation/images/custom_rgb_r.png | Bin 0 -> 1018 bytes modules/photoannotation/images/custom_submit.png | Bin 0 -> 997 bytes modules/photoannotation/images/delete.png | Bin 0 -> 334 bytes modules/photoannotation/images/edit.png | Bin 0 -> 208 bytes modules/photoannotation/images/select.png | Bin 0 -> 506 bytes modules/photoannotation/images/select2.png | Bin 0 -> 518 bytes modules/photoannotation/images/slider.png | Bin 0 -> 315 bytes modules/photoannotation/js/jquery.annotate.js | 568 +++++++++++++++++++++ modules/photoannotation/js/jquery.annotate.min.js | 1 + modules/photoannotation/js/jquery.colorpicker.js | 484 ++++++++++++++++++ .../photoannotation/js/jquery.colorpicker.min.js | 1 + modules/photoannotation/models/items_face.php | 21 + modules/photoannotation/models/items_note.php | 21 + modules/photoannotation/models/items_user.php | 21 + .../models/photoannotation_notification.php | 21 + modules/photoannotation/module.info | 3 + .../views/admin_photoannotation.html.php | 30 ++ .../views/admin_photoannotation_converter.html.php | 10 + .../admin_photoannotation_tagsmaintanance.html.php | 27 + .../views/photoannotation_block.html.php | 17 + .../views/photoannotation_cloud.html.php | 9 + .../views/photoannotation_highlight_block.html.php | 135 +++++ .../views/photoannotation_user_search.html.php | 55 ++ 53 files changed, 3178 insertions(+) create mode 100644 modules/photoannotation/controllers/admin_photoannotation.php create mode 100644 modules/photoannotation/controllers/photoannotation.php create mode 100644 modules/photoannotation/css/colorpicker.css create mode 100644 modules/photoannotation/css/photoannotation.css create mode 100644 modules/photoannotation/helpers/photoannotation.php create mode 100644 modules/photoannotation/helpers/photoannotation_block.php create mode 100644 modules/photoannotation/helpers/photoannotation_event.php create mode 100644 modules/photoannotation/helpers/photoannotation_installer.php create mode 100644 modules/photoannotation/helpers/photoannotation_theme.php create mode 100644 modules/photoannotation/images/blank.gif create mode 100644 modules/photoannotation/images/colorpicker_background.png create mode 100644 modules/photoannotation/images/colorpicker_hex.png create mode 100644 modules/photoannotation/images/colorpicker_hsb_b.png create mode 100644 modules/photoannotation/images/colorpicker_hsb_h.png create mode 100644 modules/photoannotation/images/colorpicker_hsb_s.png create mode 100644 modules/photoannotation/images/colorpicker_indic.gif create mode 100644 modules/photoannotation/images/colorpicker_overlay.png create mode 100644 modules/photoannotation/images/colorpicker_rgb_b.png create mode 100644 modules/photoannotation/images/colorpicker_rgb_g.png create mode 100644 modules/photoannotation/images/colorpicker_rgb_r.png create mode 100644 modules/photoannotation/images/colorpicker_select.gif create mode 100644 modules/photoannotation/images/colorpicker_submit.png create mode 100644 modules/photoannotation/images/custom_background.png create mode 100644 modules/photoannotation/images/custom_hex.png create mode 100644 modules/photoannotation/images/custom_hsb_b.png create mode 100644 modules/photoannotation/images/custom_hsb_h.png create mode 100644 modules/photoannotation/images/custom_hsb_s.png create mode 100644 modules/photoannotation/images/custom_indic.gif create mode 100644 modules/photoannotation/images/custom_rgb_b.png create mode 100644 modules/photoannotation/images/custom_rgb_g.png create mode 100644 modules/photoannotation/images/custom_rgb_r.png create mode 100644 modules/photoannotation/images/custom_submit.png create mode 100644 modules/photoannotation/images/delete.png create mode 100644 modules/photoannotation/images/edit.png create mode 100644 modules/photoannotation/images/select.png create mode 100644 modules/photoannotation/images/select2.png create mode 100644 modules/photoannotation/images/slider.png create mode 100644 modules/photoannotation/js/jquery.annotate.js create mode 100644 modules/photoannotation/js/jquery.annotate.min.js create mode 100644 modules/photoannotation/js/jquery.colorpicker.js create mode 100644 modules/photoannotation/js/jquery.colorpicker.min.js create mode 100644 modules/photoannotation/models/items_face.php create mode 100644 modules/photoannotation/models/items_note.php create mode 100644 modules/photoannotation/models/items_user.php create mode 100644 modules/photoannotation/models/photoannotation_notification.php create mode 100644 modules/photoannotation/module.info create mode 100644 modules/photoannotation/views/admin_photoannotation.html.php create mode 100644 modules/photoannotation/views/admin_photoannotation_converter.html.php create mode 100644 modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php create mode 100644 modules/photoannotation/views/photoannotation_block.html.php create mode 100644 modules/photoannotation/views/photoannotation_cloud.html.php create mode 100644 modules/photoannotation/views/photoannotation_highlight_block.html.php create mode 100644 modules/photoannotation/views/photoannotation_user_search.html.php (limited to 'modules') diff --git a/modules/photoannotation/controllers/admin_photoannotation.php b/modules/photoannotation/controllers/admin_photoannotation.php new file mode 100644 index 00000000..dc7436f6 --- /dev/null +++ b/modules/photoannotation/controllers/admin_photoannotation.php @@ -0,0 +1,312 @@ +_get_view(); + } + + public function converter() { + print $this->_get_converter_view(); + } + + public function tagsmaintanance($delete) { + print $this->_get_tagsmaintanance_view($delete); + } + + public function converthandler() { + access::verify_csrf(); + $form = $this->_get_converter_form(); + if ($form->validate()) { + //Load the source tag + $sourcetag = ORM::factory("tag", $form->sourcetag->value); + if (!$sourcetag->loaded()) { + message::error(t("The specified tag could not be found")); + url::redirect("admin/photoannotation/converter"); + } + //Load the target user + $targetuser = ORM::factory("user", $form->targetuser->value); + if (!$targetuser->loaded()) { + message::error(t("The specified person could not be found")); + url::redirect("admin/photoannotation/converter"); + } + //Load all existing tag annotations + $tag_annotations = ORM::factory("items_face")->where("tag_id", "=", $sourcetag->id)->find_all(); + //Disable user notifications so that users don't get flooded with mails + $old_notification_setting = module::get_var("photoannotation", "nonotifications", false); + module::set_var("photoannotation", "nonotifications", true, true); + foreach ($tag_annotations as $tag_annotation) { + photoannotation::saveuser($targetuser->id, $tag_annotation->item_id, $tag_annotation->x1, $tag_annotation->y1, $tag_annotation->x2, $tag_annotation->y2, $tag_annotation->description); + //Delete the old annotation + $tag_annotation->delete(); + } + //Remove and delete old tag + if ($form->deletetag->value) { + $this->_remove_tag($sourcetag, true); + } elseif ($form->removetag->value) { + $this->_remove_tag($sourcetag, false); + } + module::set_var("photoannotation", "nonotifications", $old_notification_setting, true); + message::success(t("%count tag annotations (%tagname) have been converted to user annotations (%username)", array("count" => count($tag_annotations), "tagname" => $sourcetag->name, "username" => $targetuser->display_name()))); + url::redirect("admin/photoannotation/converter"); + } + print $this->_get_converter_view($form); + } + + private function _remove_tag($tag, $delete) { + $name = $tag->name; + db::build()->delete("items_tags")->where("tag_id", "=", $tag->id)->execute(); + if ($delete) { + $tag->delete(); + } + } + + public function handler() { + access::verify_csrf(); + + $form = $this->_get_form(); + if ($form->validate()) { + module::set_var( + "photoannotation", "noborder", $form->hoverphoto->noborder->value, true); + module::set_var( + "photoannotation", "bordercolor", $form->hoverphoto->bordercolor->value); + module::set_var( + "photoannotation", "noclickablehover", $form->hoverclickable->noclickablehover->value, true); + module::set_var( + "photoannotation", "clickablehovercolor", $form->hoverclickable->clickablehovercolor->value); + module::set_var( + "photoannotation", "nohover", $form->hovernoclickable->nohover->value, true); + module::set_var( + "photoannotation", "hovercolor", $form->hovernoclickable->hovercolor->value); + module::set_var( + "photoannotation", "showusers", $form->legendsettings->showusers->value, true); + module::set_var( + "photoannotation", "showfaces", $form->legendsettings->showfaces->value, true); + module::set_var( + "photoannotation", "shownotes", $form->legendsettings->shownotes->value, true); + module::set_var( + "photoannotation", "fullname", $form->legendsettings->fullname->value, true); + module::set_var( + "photoannotation", "nonotifications", $form->notifications->nonotifications->value, true); + module::set_var( + "photoannotation", "notificationoptout", $form->notifications->notificationoptout->value, true); + module::set_var( + "photoannotation", "allowguestsearch", $form->notifications->allowguestsearch->value, true); + module::set_var( + "photoannotation", "newtagsubject", strip_tags($form->newtagmail->newtagsubject->value)); + module::set_var( + "photoannotation", "newtagbody", strip_tags($form->newtagmail->newtagbody->value)); + module::set_var( + "photoannotation", "newcommentsubject", strip_tags($form->newcommentmail->newcommentsubject->value)); + module::set_var( + "photoannotation", "newcommentbody", strip_tags($form->newcommentmail->newcommentbody->value)); + module::set_var( + "photoannotation", "updatedcommentsubject", strip_tags($form->updatedcommentmail->updatedcommentsubject->value)); + module::set_var( + "photoannotation", "updatedcommentbody", strip_tags($form->updatedcommentmail->updatedcommentbody->value)); + module::set_var( + "photoannotation", "onuserdelete", $form->onuserdelete->onuserdelete->value); + message::success(t("Your settings have been saved.")); + url::redirect("admin/photoannotation"); + } + print $this->_get_view($form); + } + + private function _get_view($form=null) { + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation"); + $v->content = new View("admin_photoannotation.html"); + $v->content->form = empty($form) ? $this->_get_form() : $form; + return $v; + } + + private function _get_converter_view($form=null) { + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation converter"); + $v->content = new View("admin_photoannotation_converter.html"); + $v->content->form = empty($form) ? $this->_get_converter_form() : $form; + return $v; + } + + private function _get_tagsmaintanance_view($delete = false) { + $tag_orpanes_count = 0; + $user_orphanes_count = 0; + $item_orphanes_count = 0; + $tag_orpanes_deleted = 0; + $user_orphanes_deleted = 0; + $item_orphanes_deleted = 0; + //check all tag annotations + $tag_annotations = ORM::factory("items_face")->find_all(); + foreach ($tag_annotations as $tag_annotation) { + $check_tag = ORM::factory("tag")->where("id", "=", $tag_annotation->tag_id)->find(); + if (!$check_tag->loaded()) { + if ($delete) { + $tag_annotation->delete(); + $tag_orpanes_deleted++; + } else { + $tag_orpanes_count++; + } + } else { + $check_item = ORM::factory("item")->where("id", "=", $tag_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $tag_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + } + //check all user annotations + $user_annotations = ORM::factory("items_user")->find_all(); + foreach ($user_annotations as $user_annotation) { + $check_user = ORM::factory("user")->where("id", "=", $user_annotation->user_id)->find(); + if (!$check_user->loaded()) { + if ($delete) { + $user_annotation->delete(); + $user_orpanes_deleted++; + } else { + $user_orphanes_count++; + } + } else { + $check_item = ORM::factory("item")->where("id", "=", $user_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $user_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + } + + //check all user annotations + $note_annotations = ORM::factory("items_note")->find_all(); + foreach ($note_annotations as $note_annotation) { + $check_item = ORM::factory("item")->where("id", "=", $note_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $note_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation tags maintanance"); + $v->content = new View("admin_photoannotation_tagsmaintanance.html"); + $v->content->tag_orpanes_count = $tag_orpanes_count; + $v->content->user_orphanes_count = $user_orphanes_count; + $v->content->item_orphanes_count = $item_orphanes_count; + $v->content->tag_orpanes_deleted = $tag_orpanes_deleted; + $v->content->user_orphanes_deleted = $user_orphanes_deleted; + $v->content->item_orphanes_deleted = $item_orphanes_deleted; + $v->content->dodeletion = $delete; + return $v; + } + + private function _get_converter_form() { + //get all tags + $tags = ORM::factory("tag")->order_by("name", "ASC")->find_all(); + foreach ($tags as $tag) { + $tag_array[$tag->id] = $tag->name; + } + //get all users + $users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + foreach ($users as $user) { + $user_array[$user->id] = $user->display_name(); + } + $form = new Forge("admin/photoannotation/converthandler", "", "post", array("id" => "g-admin-form")); + $form->dropdown("sourcetag")->label(t("Select tag")) + ->options($tag_array); + $form->dropdown("targetuser")->label(t("Select user")) + ->options($user_array); + $form->checkbox("deletetag")->label(t("Delete the tag after conversion.")); + $form->checkbox("removetag")->label(t("Remove the tag from photos after conversion.")); + $form->submit("submit")->value(t("Convert")); + return $form; + } + + private function _get_form() { + if (module::is_active("comment")) { + $comment_required = ""; + } else { + $comment_required = " (comment module required)"; + } + $form = new Forge("admin/photoannotation/handler", "", "post", array("id" => "g-admin-form")); + $group = $form->group("hoverphoto")->label(t("Hovering over the photo")); + $group->checkbox("noborder")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "noborder", false)); + $group->input("bordercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "bordercolor", "000000")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("hoverclickable")->label(t("Hovering over a clickable annotation")); + $group->checkbox("noclickablehover")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "noclickablehover", false)); + $group->input("clickablehovercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "clickablehovercolor", "00AD00")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("hovernoclickable")->label(t("Hovering over a non-clickable annotation")); + $group->checkbox("nohover")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "nohover", false)); + $group->input("hovercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "hovercolor", "990000")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("legendsettings")->label(t("Legend settings")); + $group->checkbox("showusers")->label(t("Show user annotations below photo.")) + ->checked(module::get_var("photoannotation", "showusers", false)); + $group->checkbox("showfaces")->label(t("Show tag annotations below photo.")) + ->checked(module::get_var("photoannotation", "showfaces", false)); + $group->checkbox("shownotes")->label(t("Show note annotations below photo.")) + ->checked(module::get_var("photoannotation", "shownotes", false)); + $group->checkbox("fullname")->label(t("Show full name of a user instead of the username on annotations (username will be dispayed for users without a full name).")) + ->checked(module::get_var("photoannotation", "fullname", false)); + $group = $form->group("notifications")->label(t("Notification and people cloud settings")); + $group->checkbox("nonotifications")->label(t("Disable user notifications.")) + ->checked(module::get_var("photoannotation", "nonotifications", false)); + $group->checkbox("notificationoptout")->label(t("Notify users by default (only applies to new users and user who have not saved their profile after installing this module).")) + ->checked(module::get_var("photoannotation", "notificationoptout", false)); + $group->checkbox("allowguestsearch")->label(t("Show people cloud and allow people search for guests.")) + ->checked(module::get_var("photoannotation", "allowguestsearch", false)); + $group = $form->group("newtagmail")->label(t("Customize the mail sent to users when a user annotation is created")); + $group->input("newtagsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "newtagsubject", "Someone tagged a photo of you")); + $group->textarea("newtagbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was tagged)")) + ->value(module::get_var("photoannotation", "newtagbody", "Hello %name, please visit %url to view the photo.")); + $group = $form->group("newcommentmail")->label(t("Customize the mail sent to users when a comment is added". $comment_required)); + $group->input("newcommentsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "newcommentsubject", "Someone added a comment to photo of you")); + $group->textarea("newcommentbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was commented on)")) + ->value(module::get_var("photoannotation", "newcommentbody", "Hello %name, please visit %url to read the comment.")); + $group = $form->group("updatedcommentmail")->label(t("Customize the mail sent to users when a comment is updated". $comment_required)); + $group->input("updatedcommentsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "updatedcommentsubject", "Someone updated a comment to photo of you")); + $group->textarea("updatedcommentbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was commented on)")) + ->value(module::get_var("photoannotation", "updatedcommentbody", "Hello %name, please visit %url to read the comment.")); + $group = $form->group("onuserdelete")->label(t("Auto conversion settings")); + $group->dropdown("onuserdelete")->label(t("When deleting a user do the following with all annotations associated with this user")) + ->options(array("0" => t("Delete annotation"), "1" => t("Convert to tag annotation"), "2" => t("Convert to note annotation"))) + ->selected(module::get_var("photoannotation", "onuserdelete", "0")); + $form->submit("submit")->value(t("Save")); + return $form; + } +} diff --git a/modules/photoannotation/controllers/photoannotation.php b/modules/photoannotation/controllers/photoannotation.php new file mode 100644 index 00000000..ed6083e5 --- /dev/null +++ b/modules/photoannotation/controllers/photoannotation.php @@ -0,0 +1,251 @@ +guest && !module::get_var("photoannotation", "allowguestsearch", false)) { + message::error(t("You have to log in to perform a people search.")); + url::redirect(url::site()); + return; + } + $form = photoannotation::get_user_search_form("g-user-cloud-form"); + $user_id = Input::instance()->get("name", ""); + if ($user_id == "") { + $user_id = Input::instance()->post("name", ""); + } + $getuser = photoannotation::getuser($user_id); + if ($getuser->found) { + url::redirect(user_profile::url($getuser->user->id)); + return; + } + $page_size = module::get_var("gallery", "page_size", 9); + $page = Input::instance()->get("page", 1); + $offset = ($page - 1) * $page_size; + + // Make sure that the page references a valid offset + if ($page < 1) { + $page = 1; + } + list ($count, $result) = photoannotation::search_user($user_id, $page_size, $offset); + $max_pages = max(ceil($count / $page_size), 1); + if ($page > 1) { + $previous_page_url = url::site("photoannotation/showuser?name=". $user_id ."&page=". ($page - 1)); + } + if ($page < $max_pages) { + $next_page_url = url::site("photoannotation/showuser?name=". $user_id ."&page=". ($page + 1)); + } + if ($user_id == "") { + $user_id = "*"; + } + $template = new Theme_View("page.html", "other", "usersearch"); + $template->set_global("position", $page); + $template->set_global("total", $max_pages); + $template->content = new View("photoannotation_user_search.html"); + $template->content->search_form = photoannotation::get_user_search_form(g-user-search-form); + $template->content->users = $result; + $template->content->q = $user_id; + $template->content->count = $count; + $template->content->paginator = new View("paginator.html"); + $template->content->paginator->previous_page_url = $previous_page_url; + $template->content->paginator->next_page_url = $next_page_url; + print $template; + } + + public function save($item_id) { + // Prevent Cross Site Request Forgery + access::verify_csrf(); + //Get form data + $item = ORM::factory("item", $item_id); + $annotate_id = $_POST["noteid"]; + $notetype = $_POST["notetype"]; + $str_y1 = $_POST["top"]; + $str_x1 = $_POST["left"]; + $str_y2 = $_POST["height"] + $str_y1; //Annotation uses area size, tagfaces uses positions + $str_x2 = $_POST["width"] + $str_x1; //Annotation uses area size, tagfaces uses positions + $item_title = $_POST["text"]; + $tag_data = $_POST["tagsList"]; + $user_id = ""; + $user_id = $_POST["userlist"]; + $description = $_POST["desc"]; + $redir_uri = url::abs_site("{$item->type}s/{$item->id}"); + //If this is a user then get the id + if ($user_id != "") { + $getuser = photoannotation::getuser($user_id); + if (!$getuser->found) { + message::error(t("Could not find anyone with the name %user.", array("user" => $user_id))); + url::redirect($redir_uri); + return; + } + if ($getuser->isguest) { + message::error(t("You cannot create an annotation for the guest user.")); + url::redirect($redir_uri); + return; + } + $user_id = $getuser->user->id; + } + + //Add tag to item, create tag if not exists + if ($tag_data != "") { + $tag = ORM::factory("tag")->where("name", "=", $tag_data)->find(); + if (!$tag->loaded()) { + $tag->name = $tag_data; + $tag->count = 0; + } + $tag->add($item); + $tag->count++; + $tag->save(); + $tag_data = $tag->id; + } else { + $tag_data = ""; + } + //Save annotation + if ($annotate_id == "new") { //This is a new annotation + if ($user_id != "") { //Save user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($tag_data != "") { //Save face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($item_title != "") { //Save note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } else { //Something's wrong + message::error(t("Please select a person or tag or specify a title.")); + url::redirect($redir_uri); + return; + } + } else { //This is an update to an existing annotation + switch ($notetype) { + case "user": //the original annotation is a user + $updateduser = ORM::factory("items_user") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id != "") { //Conversion user -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($tag_data != "") { //Conversion user -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updateduser->delete(); //delete old user + } elseif ($item_title != "") { //Conversion user -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updateduser->delete(); //delete old user + } else { //Somethings wrong + message::error(t("Please select a person or tag or specify a title.")); + url::redirect($redir_uri); + return; + } + break; + case "face": //the original annotation is a face + $updatedface = ORM::factory("items_face") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id != "") { //Conversion face -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatedface->delete(); //delete old face + } elseif ($tag_data != "") { //Conversion face -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id); + } elseif ($item_title != "") { //Conversion face -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatedface->delete(); //delete old face + } else { //Somethings wrong + message::error(t("Please select a person or tag or specify a title.")); + url::redirect($redir_uri); + return; + } + break; + case "note": //the original annotation is a note + $updatednote = ORM::factory("items_note") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id != "") { //Conversion note -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatednote->delete(); //delete old note + } elseif ($tag_data != "") { //Conversion note -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatednote->delete(); //delete old note + } elseif ($item_title != "") { //Conversion note -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id); + } else { //Somethings wrong + message::error(t("Please select a person or tag or specify a title.")); + url::redirect($redir_uri); + return; + } + break; + default: + message::error(t("Please select a person or tag or specify a title.")); + url::redirect($redir_uri); + return; + } + } + message::success(t("Annotation saved.")); + url::redirect($redir_uri); + return; + } + + public function delete($item_data) { + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + //Get form data + $item = ORM::factory("item", $item_data); + $noteid = $_POST["noteid"]; + $notetype = $_POST["notetype"]; + $redir_uri = url::abs_site("{$item->type}s/{$item->id}"); + + if ($noteid == "") { + message::error(t("Please select a tag or note to delete.")); + url::redirect($redir_uri); + return; + } + switch ($notetype) { + case "user": + db::build()->delete("items_users")->where("id", "=", $noteid)->execute(); + break; + case "face": + db::build()->delete("items_faces")->where("id", "=", $noteid)->execute(); + break; + case "note": + db::build()->delete("items_notes")->where("id", "=", $noteid)->execute(); + break; + default: + message::error(t("Please select a tag or note to delete.")); + url::redirect($redir_uri); + return; + } + message::success(t("Annotation deleted.")); + url::redirect($redir_uri); + } + + public function autocomplete() { + if (!identity::active_user()->guest || module::get_var("photoannotation", "allowguestsearch", false)) { + $users = array(); + $user_parts = explode(",", Input::instance()->get("q")); + $limit = Input::instance()->get("limit"); + $user_part = ltrim(end($user_parts)); + $user_list = ORM::factory("user") + ->where("name", "LIKE", "{$user_part}%") + ->or_where("full_name", "LIKE", "{$user_part}%") + ->order_by("full_name", "ASC") + ->limit($limit) + ->find_all(); + foreach ($user_list as $user) { + if ($user->name != "guest") { + $users[] = $user->display_name() ." (". $user->name .")"; + } + } + print implode("\n", $users); + } + } +} diff --git a/modules/photoannotation/css/colorpicker.css b/modules/photoannotation/css/colorpicker.css new file mode 100644 index 00000000..05b02b48 --- /dev/null +++ b/modules/photoannotation/css/colorpicker.css @@ -0,0 +1,161 @@ +.colorpicker { + width: 356px; + height: 176px; + overflow: hidden; + position: absolute; + background: url(../images/colorpicker_background.png); + font-family: Arial, Helvetica, sans-serif; + display: none; +} +.colorpicker_color { + width: 150px; + height: 150px; + left: 14px; + top: 13px; + position: absolute; + background: #f00; + overflow: hidden; + cursor: crosshair; +} +.colorpicker_color div { + position: absolute; + top: 0; + left: 0; + width: 150px; + height: 150px; + background: url(../images/colorpicker_overlay.png); +} +.colorpicker_color div div { + position: absolute; + top: 0; + left: 0; + width: 11px; + height: 11px; + overflow: hidden; + background: url(../images/colorpicker_select.gif); + margin: -5px 0 0 -5px; +} +.colorpicker_hue { + position: absolute; + top: 13px; + left: 171px; + width: 35px; + height: 150px; + cursor: n-resize; +} +.colorpicker_hue div { + position: absolute; + width: 35px; + height: 9px; + overflow: hidden; + background: url(../images/colorpicker_indic.gif) left top; + margin: -4px 0 0 0; + left: 0px; +} +.colorpicker_new_color { + position: absolute; + width: 60px; + height: 30px; + left: 213px; + top: 13px; + background: #f00; +} +.colorpicker_current_color { + position: absolute; + width: 60px; + height: 30px; + left: 283px; + top: 13px; + background: #f00; +} +.colorpicker input { + background-color: transparent; + border: 1px solid transparent; + position: absolute; + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + color: #898989; + top: 4px; + right: 11px; + text-align: right; + margin: 0; + padding: 0; + height: 11px; +} +.colorpicker_hex { + position: absolute; + width: 72px; + height: 22px; + background: url(../images/colorpicker_hex.png) top; + left: 212px; + top: 142px; +} +.colorpicker_hex input { + right: 6px; +} +.colorpicker_field { + height: 22px; + width: 62px; + background-position: top; + position: absolute; +} +.colorpicker_field span { + position: absolute; + width: 12px; + height: 22px; + overflow: hidden; + top: 0; + right: 0; + cursor: n-resize; +} +.colorpicker_rgb_r { + background-image: url(../images/colorpicker_rgb_r.png); + top: 52px; + left: 212px; +} +.colorpicker_rgb_g { + background-image: url(../images/colorpicker_rgb_g.png); + top: 82px; + left: 212px; +} +.colorpicker_rgb_b { + background-image: url(../images/colorpicker_rgb_b.png); + top: 112px; + left: 212px; +} +.colorpicker_hsb_h { + background-image: url(../images/colorpicker_hsb_h.png); + top: 52px; + left: 282px; +} +.colorpicker_hsb_s { + background-image: url(../images/colorpicker_hsb_s.png); + top: 82px; + left: 282px; +} +.colorpicker_hsb_b { + background-image: url(../images/colorpicker_hsb_b.png); + top: 112px; + left: 282px; +} +.colorpicker_submit { + position: absolute; + width: 22px; + height: 22px; + background: url(../images/colorpicker_submit.png) top; + left: 322px; + top: 142px; + overflow: hidden; +} +.colorpicker_focus { + background-position: center; +} +.colorpicker_hex.colorpicker_focus { + background-position: bottom; +} +.colorpicker_submit.colorpicker_focus { + background-position: bottom; +} +.colorpicker_slider { + background-position: bottom; +} diff --git a/modules/photoannotation/css/photoannotation.css b/modules/photoannotation/css/photoannotation.css new file mode 100644 index 00000000..90d7026b --- /dev/null +++ b/modules/photoannotation/css/photoannotation.css @@ -0,0 +1,267 @@ +.photoannotation-user-search { + border-bottom-style:solid; + border-bottom-width:1px; + padding: 0.8em 0.8em !important; +} + +#g-user-cloud ul { + font-size: 1.2em; + text-align: justify; +} + +#g-user-cloud ul li { + display: inline; + line-height: 1.5em; + text-align: justify; +} + +#g-user-cloud ul li a { + text-decoration: none; +} + +#g-user-cloud ul li span { + display: none; +} + +#g-user-cloud ul li.size1 a { + color: #9cf; + font-size: 80%; + font-weight: 100; +} + +#g-user-cloud ul li.size2 a { + color: #69f; + font-size: 90%; + font-weight: 300; +} + +#g-user-cloud ul li.size3 a { + color: #69c; + font-size: 100%; + font-weight: 500; +} + +#g-user-cloud ul li.size4 a { + color: #369; + font-size: 110%; + font-weight: 700; +} + +#g-user-cloud ul li.size5 a { + color: #0e2b52; + font-size: 120%; + font-weight: 900; +} + +#g-user-cloud ul li.size6 a { + color: #0e2b52; + font-size: 130%; + font-weight: 900; +} + +#g-user-cloud ul li.size7 a { + color: #0e2b52; + font-size: 140%; + font-weight: 900; +} + +#g-user-cloud ul li a:hover { + color: #f30; + text-decoration: underline; +} + +.image-annotate-canvas { + background-position: left top; + background-repeat: no-repeat; + display: block; + margin: 0 auto; + position: relative; +} +.image-annotate-view { + display: none; + position: relative; +} +.image-annotate-area { + position: absolute; + cursor: default; +} +.image-annotate-area div { + display: block; +} +.image-annotate-area-editable { + cursor: pointer; +} +.image-annotate-note { + background: #000000 none repeat scroll 0 0; + color: #FFFFFF; + display: none; + font-family: Verdana, Sans-Serif; + font-size: 1.4em; + max-width: 200px; + padding: 3px 7px; + position: absolute; +} +.image-annotate-note .actions { + display: block; + font-size: 80%; +} +.image-annotate-edit { + display: none; + direction: ltr !important; +} +#image-annotate-edit-form { + background: #FFFFFF none repeat scroll 0 0; + border: 1px solid #000000; + padding: 7px; + position: absolute; + width: 250px; +} +.image-annotate-rtl form { + text-align: right !important; +} +#image-annotate-edit-form form { + clear: right; + margin: 0 !important; + padding: 0; + z-index: 999; + text-align: left; + color: #000000; +} +#image-annotate-edit-form .box { + margin: 0; +} +#image-annotate-edit-form input.form-text, #image-annotate-edit-form #edit-comment-wrapper textarea { + width: 90%; +} +#image-annotate-edit-form textarea { + height: 50px; + font-family: Verdana, Sans-Serif; + font-size: 12px; + width: 248px; + resize: none; +} +#image-annotate-edit-form fieldset { + background: transparent none repeat scroll 0 0; +} +#image-annotate-edit-form .form-item { + margin: 0 0 5px; +} +#image-annotate-edit-form .form-button, #image-annotate-edit-form .form-submit { + margin: 0; +} +#image-annotate-edit-form a { + cursor: pointer; + display: block; + float: left; + margin: 3px 6px 3px 0; +} +.image-annotate-rtl a { + float: right !important; + margin: 3px 0 3px 6px !important; +} +.inmage-annotate-dialog button{ + float: left !important; +} +.inmage-annotate-dialog-rtl button{ + float: left !important; +} +.image-annotate-edit-area { + border: 1px solid black; + cursor: move; + display: block; + height: 60px; + left: 10px; + margin: 0; + padding: 0; + position: absolute; + top: 10px; + width: 60px; +} +.image-annotate-edit-area .ui-resizable-handle { + opacity: 0.8; +} +.image-annotate-edit-ok { + /*background-image: url(../images/accept.png);*/ +} +.image-annotate-edit-delete { + background-image: url(../images/delete.png); +} +.image-annotate-edit-close { + /*background-image: url(../images/cross.png);*/ +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + z-index: 99999; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable- autohide .ui-resizable-handle { + display: block; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0px; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0px; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0px; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0px; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.photoannotation-del-button { + background-image: url('../images/delete.png'); + cursor: pointer; +} +.photoannotation-edit-button { + background-image: url('../images/edit.png'); + cursor: pointer; +} diff --git a/modules/photoannotation/helpers/photoannotation.php b/modules/photoannotation/helpers/photoannotation.php new file mode 100644 index 00000000..749f7778 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation.php @@ -0,0 +1,275 @@ +escape($q) ."*"; + if ($q == "*") { + $users = ORM::factory("user"); + $count = $users->count_all(); + $data = $users->order_by("name", "ASC")->find_all($page_size, $offset); + return array($count, $data); + } else { + $query = + "SELECT SQL_CALC_FOUND_ROWS {users}.*, " . + " MATCH({users}.`name`) AGAINST ('$q' IN BOOLEAN MODE) AS `score` " . + "FROM {users} " . + "WHERE MATCH({users}.`name`, {users}.`full_name`) AGAINST ('$q' IN BOOLEAN MODE) " . + "ORDER BY `score` DESC " . + "LIMIT $page_size OFFSET $offset"; + $data = $db->query($query); + $count = $db->query("SELECT FOUND_ROWS() as c")->current()->c; + return array($count, new ORM_Iterator(ORM::factory("user"), $data)); + } + } + + static function get_user_search_form($form_id) { + $form = new Forge("photoannotation/showuser", "", "post", array("id" => $form_id, "class" => "g-short-form")); + $label = t("Search for a person"); + + $group = $form->group("showuser")->label("Search for a person"); + $group->input("name")->label($label)->id("name"); + $group->submit("")->value(t("Search")); + return $form; + } + + public static function getuser($user_string) { + $user_parts = explode("(", $user_string); + $user_part = rtrim(ltrim(end($user_parts)), ")"); + $user = ORM::factory("user")->where("name", "=", $user_part)->find(); + $user_firstpart = trim(implode(array_slice($user_parts, 0, count($user_parts)-1))); + if (!$user->loaded() || strcasecmp($user_firstpart, $user->display_name()) <> 0) { + $result->found = false; + $result->isguest = false; + $result->user = ""; + return $result; + } + if (identity::guest()->id == $user->id) { + $result->found = true; + $result->isguest = true; + $result->user = ""; + return $result; + } + $result->found = true; + $result->isguest = false; + $result->user = $user; + return $result; + } + + public static function saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description) { + //Since we are associating a user we will remove any old annotation of this user on this photo + $item_old_users = ORM::factory("items_user") + ->where("user_id", "=", $user_id) + ->where("item_id", "=", $item_id) + ->find_all(); + if (count($item_old_users) > 1) { + foreach ($item_old_users as $item_old_user) { + $item_old_user->delete(); + } + $item_user = ORM::factory("items_user"); + } elseif (count($item_old_users) == 1) { + $item_user = ORM::factory("items_user", $item_old_users[0]->id); + } else { + $item_user = ORM::factory("items_user"); + photoannotation::send_notifications($user_id, $item_id, "newtag"); + } + $item_user->user_id = $user_id; + $item_user->item_id = $item_id; + $item_user->x1 = $str_x1; + $item_user->y1 = $str_y1; + $item_user->x2 = $str_x2; + $item_user->y2 = $str_y2; + $item_user->description = $description; + $item_user->save(); + } + + public static function saveface($tag_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id = "") { + if ($annotate_id == "") { + $item_face = ORM::factory("items_face"); + } else { + $item_face = ORM::factory("items_face") + ->where("id", "=", $annotate_id) + ->find(); + } + $item_face->tag_id = $tag_id; + $item_face->item_id = $item_id; + $item_face->x1 = $str_x1; + $item_face->y1 = $str_y1; + $item_face->x2 = $str_x2; + $item_face->y2 = $str_y2; + $item_face->description = $description; + $item_face->save(); + } + + public static function savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id = "") { + if ($annotate_id == "") { + $item_note = ORM::factory("items_note"); + } else { + $item_note = ORM::factory("items_note") + ->where("id", "=", $annotate_id) + ->find(); + } + $item_note->item_id = $item_id; + $item_note->x1 = $str_x1; + $item_note->y1 = $str_y1; + $item_note->x2 = $str_x2; + $item_note->y2 = $str_y2; + $item_note->title = $item_title; + $item_note->description = $description; + $item_note->save(); + } + + public static function send_notifications($recipient_id, $item_id, $mailtype) { + //Load the item + $item = ORM::factory("item")->where("id", "=", $item_id)->find(); + if (!$item->loaded()) { + return false; + } + //Load the user + $recipient = ORM::factory("user")->where("id", "=", $recipient_id)->find(); + if (!$recipient->loaded()) { + return false; + } + //Only send mail if the notifications are switched on globally + if (!module::get_var("photoannotation", "nonotifications", false)) { + //Check if the use has a valid e-mail + if (!valid::email($recipient->email)) { + return false; + } + //Get the users settings + $notification_settings = self::get_user_notification_settings($recipient); + //Check which type of mail to send + switch ($mailtype) { + case "newtag": + //Only send if user has this option enabled + if ($notification_settings->newtag) { + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "newtagsubject", "Someone tagged a photo of you"); + $body = module::get_var("photoannotation", "newtagbody", "Hello %name, please visit %url to view the photo."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + break; + case "newcomment": + //Only send if user has this option enabled + if ($notification_settings->comment) { + //Don't send if the notification module is active and the user is watching this item + if (module::is_active("notification")) { + if (notification::is_watching($item, $recipient)) { + return false; + } + } + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "newcommentsubject", "Someone added a comment to photo of you"); + $body = module::get_var("photoannotation", "newcommentbody", "Hello %name, please visit %url to read the comment."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + break; + case "updatecomment": + //Only send if user has this option enabled + if ($notification_settings->comment) { + //Don't send if the notification module is active and the user is watching this item + if (module::is_active("notification")) { + if (notification::is_watching($item, $recipient)) { + return false; + } + } + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "updatedcommentsubject", "Someone updated a comment to photo of you"); + $body = module::get_var("photoannotation", "updatedcommentbody", "Hello %name, please visit %url to read the comment."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + } + } + return false; + } + + private static function _send_mail($mailto, $subject, $message) { + //Send the notification mail + $message = nl2br($message); + return Sendmail::factory() + ->to($mailto) + ->subject($subject) + ->header("Mime-Version", "1.0") + ->header("Content-type", "text/html; charset=utf-8") + ->message($message) + ->send(); + } + + public static function get_user_notification_settings($user) { + //Try loading the notification settings of user + $notification_settings = ORM::factory("photoannotation_notification")->where("user_id", "=", $user->id)->find(); + if (!$notification_settings->loaded()) { + //If the user did not save his settings use the website default + $notify = module::get_var("photoannotation", "notificationoptout", false); + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + $notification_settings->newtag = $notify; + $notification_settings->comment = $notify; + $notification_settings->save(); + } + return $notification_settings; + } + + static function cloud($count) { + $users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + if ($users) { + $cloud = new View("photoannotation_cloud.html"); + $fullname = module::get_var("photoannotation", "fullname", false); + foreach ($users as $user) { + $annotations = ORM::factory("items_user")->where("user_id", "=", $user->id)->count_all(); + if ($annotations > 0) { + if ($annotations > $maxcount) { + $maxcount = $annotations; + } + if ($fullname) { + $user_array[$user->name]->name = $user->display_name(); + } else { + $user_array[$user->name]->name = $user->name; + } + $user_array[$user->name]->size = $annotations; + $user_array[$user->name]->url = user_profile::url($user->id); + } + } + if (isset($user_array)) { + $cloud->users = array_slice($user_array, 0, $count); + $cloud->max_count = $maxcount; + return $cloud; + } else { + return ""; + } + } + } + + static function comment_count($user_id) { + if (module::is_active("comment")) { + return ORM::factory("comment")->where("author_id", "=", $user_id)->count_all(); + } else { + return false; + } + } + + static function annotation_count($user_id) { + return ORM::factory("items_user")->where("user_id", "=", $user_id)->count_all(); + } +} diff --git a/modules/photoannotation/helpers/photoannotation_block.php b/modules/photoannotation/helpers/photoannotation_block.php new file mode 100644 index 00000000..d0e89e0a --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_block.php @@ -0,0 +1,40 @@ + t("Users")); + } + + static function get($block_id, $theme) { + $block = ""; + if (!identity::active_user()->guest || module::get_var("photoannotation", "allowguestsearch", false)) { + switch ($block_id) { + case "photoannotation": + $block = new Block(); + $block->css_id = "g-photoannotation"; + $block->title = t("People"); + $block->content = new View("photoannotation_block.html"); + $block->content->cloud = photoannotation::cloud(30); + $block->content->form = photoannotation::get_user_search_form("g-user-cloud-form"); + } + } + return $block; + } +} diff --git a/modules/photoannotation/helpers/photoannotation_event.php b/modules/photoannotation/helpers/photoannotation_event.php new file mode 100644 index 00000000..a7836052 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_event.php @@ -0,0 +1,248 @@ +deactivate)) { + site_status::warning( + t("The Photo Annotation module requires the Tags module. " . + "Activate the Tags module now", + array("url" => url::site("admin/modules"))), + "photoannotation_needs_tag"); + } else { + site_status::clear("photoannotation_needs_tag"); + } + if (module::is_active("tagfaces") || in_array("tagfaces", $changes->activate)) { + site_status::warning( + t("The Photo Annotation module cannot be used together with the TagFaces module. " . + "Dectivate the TagFaces module now", + array("url" => url::site("admin/modules"))), + "photoannotation_incompatibility_tagfaces"); + } else { + site_status::clear("photoannotation_incompatibility_tagfaces"); + } + } + + static function site_menu($menu, $theme) { + // Create a menu option for adding face data. + if (!$theme->item()) { + return; + } + $item = $theme->item(); + if ($item->is_photo()) { + if ((access::can("view", $item)) && (access::can("edit", $item))) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("photoannotation") + ->label(t("Add annotation")) + ->css_id("g-photoannotation-link") + ->url("#")); + } + } + } + + static function item_deleted($item) { + // Check for and delete existing Faces and Notes. + $existingFaces = ORM::factory("items_face") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingFaces) > 0) { + db::build()->delete("items_faces")->where("item_id", "=", $item->id)->execute(); + } + + $existingNotes = ORM::factory("items_note") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingNotes) > 0) { + db::build()->delete("items_notes")->where("item_id", "=", $item->id)->execute(); + } + + $existingUsers = ORM::factory("items_user") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingUsers) > 0) { + db::build()->delete("items_users")->where("item_id", "=", $item->id)->execute(); + } + } + + static function user_deleted($old) { + // Check for and delete existing Annotations linked to that user. + $existingFaces = ORM::factory("items_user") + ->where("user_id", "=", $old->id) + ->find_all(); + if (count($existingFaces) > 0) { + $onuserdelete = module::get_var("photoannotation", "onuserdelete", "0"); + if (module::get_var("photoannotation", "fullname", false)) { + $new_tag_name = $old->display_name(); + } else { + $new_tag_name = $old->name; + } + switch ($onuserdelete) { + case "1": + //convert to tag + $tag = ORM::factory("tag")->where("name", "=", $new_tag_name)->find(); + if (!$tag->loaded()) { + $tag->name = $new_tag_name; + $tag->count = 0; + } + foreach ($existingFaces as $existingFace) { + $item = ORM::factory("item")->where("id", "=", $existingFace->item_id)->find(); + $tag->add($item); + $tag->count++; + $tag->save(); + photoannotation::saveface($tag->id, $existingFace->item_id, $existingFace->x1, $existingFace->y1, $existingFace->x2, $existingFace->y2, $existingFace->description); + } + break; + case "2": + //convert to note + foreach ($existingFaces as $existingFace) { + photoannotation::savenote($new_tag_name, $existingFace->item_id, $existingFace->x1, $existingFace->y1, $existingFace->x2, $existingFace->y2, $existingFace->description); + } + } + db::build()->delete("items_users")->where("user_id", "=", $old->id)->execute(); + } + // Delete notification settings + $notification_settings = ORM::factory("photoannotation_notification") + ->where("user_id", "=", $old->id) + ->find(); + if ($notification_settings->loaded()) { + $notification_settings->delete(); + } + } + + static function user_created($user) { + // Write notification settings + $notify = module::get_var("photoannotation", "notificationoptout", false); + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + $notification_settings->newtag = $notify; + $notification_settings->comment = $notify; + $notification_settings->save(); + } + + static function admin_menu($menu, $theme) { + $menu->get("settings_menu") + ->append(Menu::factory("link") + ->id("photoannotation_menu") + ->label(t("Photo Annotation")) + ->url(url::site("admin/photoannotation"))); + } + + static function show_user_profile($data) { + $view = new Theme_View("dynamic.html", "collection", "userprofiles"); + //load thumbs + $item_users = ORM::factory("items_user")->where("user_id", "=", $data->user->id)->find_all(); + foreach ($item_users as $item_user) { + $item_thumb = ORM::factory("item") + ->viewable() + ->where("type", "!=", "album") + ->where("id", "=", $item_user->item_id) + ->find(); + if ($item_thumb->loaded()) { + $item_thumbs[] = $item_thumb; + } + } + $children_count = count($item_thumbs); + $page_size = module::get_var("gallery", "page_size", 9); + $page = (int) Input::instance()->get("page", "1"); + $offset = ($page-1) * $page_size; + $max_pages = max(ceil($children_count / $page_size), 1); + + // Make sure that the page references a valid offset + if ($page < 1) { + url::redirect($album->abs_url()); + } else if ($page > $max_pages) { + url::redirect($album->abs_url("page=$max_pages")); + } + if ($page < $max_pages) { + $next_page_url = url::site("user_profile/show/". $data->user->id ."?page=". ($page + 1)); + $view->set_global("next_page_url", $next_page_url); + $view->set_global("first_page_url", url::site("user_profile/show/". $data->user->id ."?page=". $max_pages)); + } + + if ($page > 1) { + $view->set_global("previous_page_url", url::site("user_profile/show/". $data->user->id ."?page=". ($page - 1))); + $view->set_global("first_page_url", url::site("user_profile/show/". $data->user->id ."?page=1")); + } + $view->set_global("page", $page); + $view->set_global("max_pages", $max_pages); + $view->set_global("page_size", $page_size); + $view->set_global("children", array_slice($item_thumbs, $offset, $page_size));; + $view->set_global("children_count", $children_count); + $view->set_global("total", $max_pages); + $view->set_global("position", t("Page") ." ". $page); + if ($children_count > 0) { + $data->content[] = (object)array("title" => t("Photos"), "view" => $view); + } + } + + static function user_edit_form($user, $form) { + // Allow users to modify notification settings. + if (!module::get_var("photoannotation", "nonotifications", false)) { + $notification_settings = photoannotation::get_user_notification_settings($user); + $user_notification = $form->edit_user->group("edit_notification")->label("Tag notifications"); + $user_notification->checkbox("photoannotation_newtag")->label(t("Notify me when a tag is added to a photo with me")) + ->checked($notification_settings->newtag); + $user_notification->checkbox("photoannotation_comment")->label(t("Notify me if someone comments on a photo with me on it")) + ->checked($notification_settings->comment); + } + } + + static function user_edit_form_completed($user, $form) { + // Save notification settings. + if (!module::get_var("photoannotation", "nonotifications", false)) { + $notification_settings = ORM::factory("photoannotation_notification")->where("user_id", "=", $user->id)->find(); + if (!$notification_settings->loaded()) { + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + } + $notification_settings->newtag = $form->edit_user->edit_notification->photoannotation_newtag->value; + $notification_settings->comment = $form->edit_user->edit_notification->photoannotation_comment->value; + $notification_settings->save(); + } + } + + static function comment_created($comment) { + //Check if there are any user annotations on the photo and send notification if applicable + $item_users = ORM::factory("items_user")->where("item_id", "=", $comment->item_id)->find_all(); + if (count($item_users) > 0) { + foreach ($item_users as $item_user) { + //Don't send if the commenter is the user to be notified + if ($comment->author_id != $item_user->user_id && module::is_active("notification")) { + photoannotation::send_notifications($item_user->user_id, $comment->item_id, "newcomment"); + } + } + } + } + + static function comment_updated($comment) { + //Check if there are any user annotations on the photo and send notification if applicable + $item_users = ORM::factory("items_user")->where("item_id", "=", $comment->item_id)->find_all(); + if (count($item_users) > 0) { + foreach ($item_users as $item_user) { + //Don't send if the commenter is the user to be notified + if ($comment->author_id != $item_user->user_id && module::is_active("notification")) { + photoannotation::send_notifications($item_user->user_id, $comment->item_id, "updatedcomment"); + } + } + } + } +} diff --git a/modules/photoannotation/helpers/photoannotation_installer.php b/modules/photoannotation/helpers/photoannotation_installer.php new file mode 100644 index 00000000..a0a881a9 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_installer.php @@ -0,0 +1,119 @@ +query("CREATE TABLE IF NOT EXISTS {items_faces} ( + `id` int(9) NOT NULL auto_increment, + `tag_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {items_notes} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `title` varchar(64) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {items_users} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {photoannotation_notifications} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL unique, + `newtag` int(2) default NULL, + `comment` int(2) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + // Set the module's version number. + module::set_version("photoannotation", 4); + } + + static function upgrade($version) { + if ($version == 1) { + module::set_version("photoannotation", $version = 2); + } + if ($version == 2) { + $db = Database::instance(); + $db->query("CREATE TABLE IF NOT EXISTS {items_users} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("photoannotation", $version = 3); + } + if ($version == 3) { + $db = Database::instance(); + $db->query("CREATE TABLE IF NOT EXISTS {photoannotation_notifications} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL unique, + `newtag` int(2) default NULL, + `comment` int(2) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("photoannotation", $version = 4); + } + } + + static function deactivate() { + // Clear the require tags message when photoannotation is deactivated. + site_status::clear("photoannotation_needs_tag"); + site_status::clear("photoannotation_incompatibility_tagfaces"); + } + + static function uninstall() { + // Delete the face table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {items_faces};"); + $db->query("DROP TABLE IF EXISTS {items_notes};"); + $db->query("DROP TABLE IF EXISTS {items_users};"); + $db->query("DROP TABLE IF EXISTS {photoannotation_notifications};"); + module::delete("photoannotation"); + } +} diff --git a/modules/photoannotation/helpers/photoannotation_theme.php b/modules/photoannotation/helpers/photoannotation_theme.php new file mode 100644 index 00000000..15f584f8 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_theme.php @@ -0,0 +1,81 @@ +css("photoannotation.css"); + if ($theme->page_subtype == "photo") { + $theme->script("jquery.annotate.min.js"); + $noborder = module::get_var("photoannotation", "noborder", false); + $noclickablehover = module::get_var("photoannotation", "noclickablehover", false); + $nohover = module::get_var("photoannotation", "nohover", false); + $bordercolor = "#". module::get_var("photoannotation", "bordercolor", "000000"); + $v = "\n"; + return $v; + } + } + + static function resize_bottom($theme) { + if ($theme->page_subtype == "photo") { + return new View("photoannotation_highlight_block.html"); + } + } + + static function admin_head($theme) { + if (strpos($theme->content->kohana_filename, "admin_photoannotation.html.php")) { + $theme->css("colorpicker.css"); + $theme->script("jquery.colorpicker.min.js"); + } + } + +} diff --git a/modules/photoannotation/images/blank.gif b/modules/photoannotation/images/blank.gif new file mode 100644 index 00000000..75b945d2 Binary files /dev/null and b/modules/photoannotation/images/blank.gif differ diff --git a/modules/photoannotation/images/colorpicker_background.png b/modules/photoannotation/images/colorpicker_background.png new file mode 100644 index 00000000..8401572f Binary files /dev/null and b/modules/photoannotation/images/colorpicker_background.png differ diff --git a/modules/photoannotation/images/colorpicker_hex.png b/modules/photoannotation/images/colorpicker_hex.png new file mode 100644 index 00000000..4e532d7c Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hex.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_b.png b/modules/photoannotation/images/colorpicker_hsb_b.png new file mode 100644 index 00000000..dfac595d Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_b.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_h.png b/modules/photoannotation/images/colorpicker_hsb_h.png new file mode 100644 index 00000000..3977ed9f Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_h.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_s.png b/modules/photoannotation/images/colorpicker_hsb_s.png new file mode 100644 index 00000000..a2a69973 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_s.png differ diff --git a/modules/photoannotation/images/colorpicker_indic.gif b/modules/photoannotation/images/colorpicker_indic.gif new file mode 100644 index 00000000..f9fa95e2 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_indic.gif differ diff --git a/modules/photoannotation/images/colorpicker_overlay.png b/modules/photoannotation/images/colorpicker_overlay.png new file mode 100644 index 00000000..561cdd9c Binary files /dev/null and b/modules/photoannotation/images/colorpicker_overlay.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_b.png b/modules/photoannotation/images/colorpicker_rgb_b.png new file mode 100644 index 00000000..dfac595d Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_b.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_g.png b/modules/photoannotation/images/colorpicker_rgb_g.png new file mode 100644 index 00000000..72b32760 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_g.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_r.png b/modules/photoannotation/images/colorpicker_rgb_r.png new file mode 100644 index 00000000..4855fe03 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_r.png differ diff --git a/modules/photoannotation/images/colorpicker_select.gif b/modules/photoannotation/images/colorpicker_select.gif new file mode 100644 index 00000000..599f7f13 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_select.gif differ diff --git a/modules/photoannotation/images/colorpicker_submit.png b/modules/photoannotation/images/colorpicker_submit.png new file mode 100644 index 00000000..566fe2dd Binary files /dev/null and b/modules/photoannotation/images/colorpicker_submit.png differ diff --git a/modules/photoannotation/images/custom_background.png b/modules/photoannotation/images/custom_background.png new file mode 100644 index 00000000..cf55ffdd Binary files /dev/null and b/modules/photoannotation/images/custom_background.png differ diff --git a/modules/photoannotation/images/custom_hex.png b/modules/photoannotation/images/custom_hex.png new file mode 100644 index 00000000..888f4444 Binary files /dev/null and b/modules/photoannotation/images/custom_hex.png differ diff --git a/modules/photoannotation/images/custom_hsb_b.png b/modules/photoannotation/images/custom_hsb_b.png new file mode 100644 index 00000000..2f99dae8 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_b.png differ diff --git a/modules/photoannotation/images/custom_hsb_h.png b/modules/photoannotation/images/custom_hsb_h.png new file mode 100644 index 00000000..a217e921 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_h.png differ diff --git a/modules/photoannotation/images/custom_hsb_s.png b/modules/photoannotation/images/custom_hsb_s.png new file mode 100644 index 00000000..7826b415 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_s.png differ diff --git a/modules/photoannotation/images/custom_indic.gif b/modules/photoannotation/images/custom_indic.gif new file mode 100644 index 00000000..222fb94c Binary files /dev/null and b/modules/photoannotation/images/custom_indic.gif differ diff --git a/modules/photoannotation/images/custom_rgb_b.png b/modules/photoannotation/images/custom_rgb_b.png new file mode 100644 index 00000000..80764e5d Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_b.png differ diff --git a/modules/photoannotation/images/custom_rgb_g.png b/modules/photoannotation/images/custom_rgb_g.png new file mode 100644 index 00000000..fc9778be Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_g.png differ diff --git a/modules/photoannotation/images/custom_rgb_r.png b/modules/photoannotation/images/custom_rgb_r.png new file mode 100644 index 00000000..91b0cd4c Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_r.png differ diff --git a/modules/photoannotation/images/custom_submit.png b/modules/photoannotation/images/custom_submit.png new file mode 100644 index 00000000..cd202cd9 Binary files /dev/null and b/modules/photoannotation/images/custom_submit.png differ diff --git a/modules/photoannotation/images/delete.png b/modules/photoannotation/images/delete.png new file mode 100644 index 00000000..13eccb1c Binary files /dev/null and b/modules/photoannotation/images/delete.png differ diff --git a/modules/photoannotation/images/edit.png b/modules/photoannotation/images/edit.png new file mode 100644 index 00000000..6dbcb679 Binary files /dev/null and b/modules/photoannotation/images/edit.png differ diff --git a/modules/photoannotation/images/select.png b/modules/photoannotation/images/select.png new file mode 100644 index 00000000..21213bfd Binary files /dev/null and b/modules/photoannotation/images/select.png differ diff --git a/modules/photoannotation/images/select2.png b/modules/photoannotation/images/select2.png new file mode 100644 index 00000000..2cd2cabe Binary files /dev/null and b/modules/photoannotation/images/select2.png differ diff --git a/modules/photoannotation/images/slider.png b/modules/photoannotation/images/slider.png new file mode 100644 index 00000000..8b03da96 Binary files /dev/null and b/modules/photoannotation/images/slider.png differ diff --git a/modules/photoannotation/js/jquery.annotate.js b/modules/photoannotation/js/jquery.annotate.js new file mode 100644 index 00000000..8310aad8 --- /dev/null +++ b/modules/photoannotation/js/jquery.annotate.js @@ -0,0 +1,568 @@ +/// +(function($) { + + $.fn.annotateImage = function(options) { + /// + /// Creates annotations on the given image. + /// Images are loaded from the "getUrl" propety passed into the options. + /// + var opts = $.extend({}, $.fn.annotateImage.defaults, options); + var image = this; + + this.image = this; + this.mode = 'view'; + + // Assign defaults + this.getUrl = opts.getUrl; + this.saveUrl = opts.saveUrl; + this.deleteUrl = opts.deleteUrl; + this.deleteUrl = opts.deleteUrl; + this.editable = opts.editable; + this.useAjax = opts.useAjax; + this.tags = opts.tags; + this.notes = opts.notes; + this.labels = opts.labels; + this.csrf = opts.csrf; + this.cssaclass = opts.cssaclass; + this.rtlsupport = opts.rtlsupport; + this.users = opts.users; + + // Add the canvas + this.canvas = $('
'); + this.canvas.children('.image-annotate-edit').hide(); + this.canvas.children('.image-annotate-view').hide(); + this.image.after(this.canvas); + + // Give the canvas and the container their size and background + this.canvas.height(this.height()); + this.canvas.width(this.width()); + this.canvas.css('background-image', 'url("' + this.attr('src') + '")'); + this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height()); + this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width()); + + // Add the behavior: hide/show the notes when hovering the picture + this.canvas.hover(function() { + if ($(this).children('.image-annotate-edit').css('display') == 'none') { + $(this).children('.image-annotate-view').show(); + } + }, function() { + $(this).children('.image-annotate-view').hide(); + $(this).children('.image-annotate-note').hide(); + }); + + this.canvas.children('.image-annotate-view').hover(function() { + $(this).show(); + }, function() { + $(this).hide(); + $(this).children('.image-annotate-note').hide(); + }); + + // load the notes + if (this.useAjax) { + $.fn.annotateImage.ajaxLoad(this); + } else { + $.fn.annotateImage.load(this, this.labels, this.editable, this.csrf, this.deleteUrl, this.tags, this.saveUrl, this.cssaclass, this.rtlsupport, this.users); + } + + // Add the "Add a note" button + if ($('#g-photoannotation-link').length !== 0) { + this.button = $('#g-photoannotation-link'); + this.button.click(function() { + $.fn.annotateImage.add(image, opts.tags, opts.labels, opts.saveUrl, opts.csrf, opts.rtlsupport, opts.users); + }); + //this.canvas.after(this.button); + } + + // Hide the original + this.hide(); + + return this; + }; + + /** + * Plugin Defaults + **/ + $.fn.annotateImage.defaults = { + getUrl: 'your-get.rails', + saveUrl: 'your-save.rails', + deleteUrl: 'your-delete.rails', + editable: true, + useAjax: true, + tags: new Array(), + notes: new Array() + }; + + $.fn.annotateImage.clear = function(image) { + /// + /// Clears all existing annotations from the image. + /// + for (var i = 0; i < image.notes.length; i++) { + image.notes[image.notes[i]].destroy(); + } + image.notes = new Array(); + }; + + $.fn.annotateImage.ajaxLoad = function(image) { + /// + /// Loads the annotations from the "getUrl" property passed in on the + /// options object. + /// + $.getJSON(image.getUrl + '?ticks=' + $.fn.annotateImage.getTicks(), function(data) { + image.notes = data; + $.fn.annotateImage.load(image); + }); + }; + + $.fn.annotateImage.load = function(image, labels, editable, csrf, deleteUrl, tags, saveUrl, cssaclass, rtlsupport, users) { + /// + /// Loads the annotations from the notes property passed in on the + /// options object. + /// + for (var i = 0; i < image.notes.length; i++) { + image.notes[image.notes[i]] = new $.fn.annotateView(image, image.notes[i], tags, labels, editable, csrf, deleteUrl, saveUrl, cssaclass, rtlsupport, users); + } + }; + + $.fn.annotateImage.getTicks = function() { + /// + /// Gets a count og the ticks for the current date. + /// This is used to ensure that URLs are always unique and not cached by the browser. + /// + var now = new Date(); + return now.getTime(); + }; + + $.fn.annotateImage.add = function(image, tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Adds a note to the image. + /// + if (image.mode == 'view') { + image.mode = 'edit'; + + // Create/prepare the editable note elements + var editable = new $.fn.annotateEdit(image, null, tags, labels, saveUrl, csrf, rtlsupport, users); + + $.fn.annotateImage.createSaveButton(editable, image, null, rtlsupport, labels); + $.fn.annotateImage.createCancelButton(editable, image, rtlsupport, labels); + } + }; + + $.fn.annotateImage.createSaveButton = function(editable, image, note, rtlsupport, labels) { + /// + /// Creates a Save button on the editable note. + /// + var ok = $('' + labels[8] + ''); + + ok.click(function() { + var form = $('#image-annotate-edit-form form'); + var text = $('#image-annotate-text').val(); + $.fn.annotateImage.appendPosition(form, editable); + image.mode = 'view'; + + form.submit(); + + editable.destroy(); + }); + editable.form.append(ok); + }; + + $.fn.annotateImage.createCancelButton = function(editable, image, rtlsupport, labels) { + /// + /// Creates a Cancel button on the editable note. + /// + var cancel = $('' + labels[9] + ''); + cancel.click(function() { + editable.destroy(); + image.mode = 'view'; + location.reload(); + }); + editable.form.append(cancel); + }; + + $.fn.annotateImage.saveAsHtml = function(image, target) { + var element = $(target); + var html = ""; + for (var i = 0; i < image.notes.length; i++) { + html += $.fn.annotateImage.createHiddenField("text_" + i, image.notes[i].text); + html += $.fn.annotateImage.createHiddenField("top_" + i, image.notes[i].top); + html += $.fn.annotateImage.createHiddenField("left_" + i, image.notes[i].left); + html += $.fn.annotateImage.createHiddenField("height_" + i, image.notes[i].height); + html += $.fn.annotateImage.createHiddenField("width_" + i, image.notes[i].width); + } + element.html(html); + }; + + $.fn.annotateImage.createHiddenField = function(name, value) { + return '<input type="hidden" name="' + name + '" value="' + value + '" />
'; + }; + + $.fn.annotateEdit = function(image, note, tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Defines an editable annotation area. + /// + this.image = image; + + if (note) { + this.note = note; + } else { + var newNote = new Object(); + newNote.noteid = "new"; + newNote.top = 30; + newNote.left = 30; + newNote.width = 60; + newNote.height = 60; + newNote.text = ""; + newNote.description = ""; + newNote.notetype = ""; + newNote.internaltext = ""; + this.note = newNote; + } + + // Set area + var area = image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area'); + this.area = area; + this.area.css('height', this.note.height + 'px'); + this.area.css('width', this.note.width + 'px'); + this.area.css('left', this.note.left + 'px'); + this.area.css('top', this.note.top + 'px'); + + // Show the edition canvas and hide the view canvas + image.canvas.children('.image-annotate-view').hide(); + image.canvas.children('.image-annotate-edit').show(); + + // Add the note (which we'll load with the form afterwards) + var selectedtag = ""; + var notetitle = ""; + var username = ""; + var selecteduser = ""; + if (this.note.notetype == "face") { + selectedtag = this.note.text; + } else if (this.note.notetype == "user") { + username = this.note.internaltext; + } else { + notetitle = this.note.text; + } + var form = $('
' + labels[12] + '
' + labels[4] + '
' + labels[4] + '
' + labels[2] + '
'); + this.form = form; + $('body').append(this.form); + $("#photoannotation-form").ready(function() { + var url = tags; + $("input#image-annotate-tag-text").autocomplete( + url, { + max: 30, + multiple: false, + cacheLength: 1 + } + ); + var urlusers = users; + $("input#photoannotation-user-list").autocomplete( + urlusers, { + max: 30, + multiple: false, + cacheLength: 1 + } + ); + }); + $("input#image-annotate-tag-text").keyup(function() { + if ($("input#image-annotate-tag-text").val() != "") { + $("input#image-annotate-text").html(""); + $("input#image-annotate-text").val(""); + $("input#photoannotation-user-list").html(""); + $("input#photoannotation-user-list").val(""); + } + }); + $("input#image-annotate-text").keyup(function() { + if ($("input#image-annotate-text").val() != "") { + $("input#image-annotate-tag-text").html(""); + $("input#image-annotate-tag-text").val(""); + $("input#photoannotation-user-list").html(""); + $("input#photoannotation-user-list").val(""); + } + }); + $("input#photoannotation-user-list").keyup(function() { + if ($("select#photoannotation-user-list").val() != "-1") { + $("input#image-annotate-tag-text").html(""); + $("input#image-annotate-tag-text").val(""); + $("input#image-annotate-text").html(""); + $("input#image-annotate-text").val(""); + } + }); + this.form.css('left', this.area.offset().left + 'px'); + this.form.css('top', (parseInt(this.area.offset().top) + parseInt(this.area.height()) + 7) + 'px'); + + // Set the area as a draggable/resizable element contained in the image canvas. + // Would be better to use the containment option for resizable but buggy + area.resizable({ + handles: 'all', + + start: function(e, ui) { + form.hide(); + }, + stop: function(e, ui) { + form.css('left', area.offset().left + 'px'); + form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 7) + 'px'); + form.show(); + } + }) + .draggable({ + containment: image.canvas, + drag: function(e, ui) { + form.hide(); + }, + stop: function(e, ui) { + form.css('left', area.offset().left + 'px'); + form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 7) + 'px'); + form.show(); + } + }); + return this; + }; + + $.fn.annotateEdit.prototype.destroy = function() { + /// + /// Destroys an editable annotation area. + /// + this.image.canvas.children('.image-annotate-edit').hide(); + this.area.resizable('destroy'); + this.area.draggable('destroy'); + this.area.css('height', ''); + this.area.css('width', ''); + this.area.css('left', ''); + this.area.css('top', ''); + this.form.remove(); + }; + + $.fn.annotateView = function(image, note, tags, labels, editable, csrf, deleteUrl, saveUrl, cssaclass, rtlsupport, users) { + /// + /// Defines a annotation area. + /// + this.image = image; + + this.note = note; + + // Add the area + this.area = $('
'); + image.canvas.children('.image-annotate-view').prepend(this.area); + + if (editable) { + this.delarea = $('
'); + this.editarea = $('
'); + image.canvas.children('.image-annotate-view').prepend(this.delarea); + image.canvas.children('.image-annotate-view').prepend(this.editarea); + this.delarea.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + var confdialog = '
' + labels[3] + '
'; + $('body').append(confdialog); + var btns = {}; + if (rtlsupport == "") { + diagclass = "inmage-annotate-dialog"; + } else { + diagclass = "inmage-annotate-dialog-rtl"; + } + btns[labels[5]] = function(){ var delform = $(this).attr("rel"); $("form#" + delform).submit(); }; + btns[labels[6]] = function(){ location.reload(); }; + $('#image-annotate-conf-dialog').dialog({ + modal: true, + resizable: false, + dialogClass: diagclass, + title: labels[7], + close: function(event, ui) { location.reload(); }, + buttons: btns + }); + }); + var form = this; + this.editarea.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + form.edit(tags, labels, saveUrl, csrf, rtlsupport, users); + }); + this.delarea.hide(); + this.editarea.hide(); + } + + // Add the note + var notedescription = ""; + if (note.description != "") { + notedescription = "
" + note.description; + } + this.form = $('
' + note.text + notedescription + '
'); + this.form.hide(); + image.canvas.children('.image-annotate-view').append(this.form); + this.form.children('span.actions').hide(); + + // Set the position and size of the note + this.setPosition(rtlsupport); + + // Add the behavior: hide/display the note when hovering the area + var annotation = this; + this.area.hover(function() { + annotation.show(); + if (annotation.delarea != undefined) { + annotation.delarea.show(); + annotation.editarea.show(); + } + }, function() { + annotation.hide(); + if (annotation.delarea != undefined) { + annotation.delarea.hide(); + annotation.editarea.hide(); + } + }); + var legendspan = "#photoannotation-legend-" + this.note.notetype + "-" + this.note.noteid; + if ($(legendspan).length > 0) { + $(legendspan).hover(function() { + var legendsarea = "#photoannotation-area-" + note.notetype + "-" + note.noteid; + $(legendsarea).children('.image-annotate-view').show(); + $(".image-annotate-view").show(); + $(legendsarea).show(); + annotation.show(); + }, function() { + var legendsarea = "#photoannotation-area-" + note.notetype + "-" + note.noteid; + annotation.hide(); + $(legendsarea).children('.image-annotate-view').hide(); + $(".image-annotate-view").hide(); + }); + } + if (editable) { + this.delarea.hover(function() { + annotation.delarea.show(); + annotation.editarea.show(); + }, function() { + annotation.delarea.hide(); + annotation.editarea.hide(); + }); + this.editarea.hover(function() { + annotation.delarea.show(); + annotation.editarea.show(); + }, function() { + annotation.delarea.hide(); + annotation.editarea.hide(); + }); + } + // Edit a note feature + if (note.url != "" && note.url != null) { + this.area.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + window.location = note.url; + }); + } + }; + + $.fn.annotateView.prototype.setPosition = function(rtlsupport) { + /// + /// Sets the position of an annotation. + /// + this.area.children('div').height((parseInt(this.note.height) - 2) + 'px'); + this.area.children('div').width((parseInt(this.note.width) - 2) + 'px'); + this.area.css('left', (this.note.left) + 'px'); + this.area.css('top', (this.note.top) + 'px'); + this.form.css('left', (this.note.left) + 'px'); + this.form.css('top', (parseInt(this.note.top) + parseInt(this.note.height) + 7) + 'px'); + + if (this.delarea != undefined) { + this.delarea.children('div').height('14px'); + this.delarea.children('div').width('14px'); + this.delarea.css('top', (this.note.top) + 'px'); + this.editarea.children('div').height('14px'); + this.editarea.children('div').width('14px'); + if (rtlsupport == '') { + this.delarea.css('left', (this.note.left + parseInt(this.note.width)) + 'px'); + this.editarea.css('left', (this.note.left + parseInt(this.note.width)) + 'px'); + } else { + this.delarea.css('left', (this.note.left - 16) + 'px'); + this.editarea.css('left', (this.note.left - 16) + 'px'); + } + this.editarea.css('top', (this.note.top + 16) + 'px'); + } + }; + + $.fn.annotateView.prototype.show = function() { + /// + /// Highlights the annotation + /// + this.form.fadeIn(250); + if (!this.note.editable) { + this.area.addClass('image-annotate-area-hover'); + } else { + this.area.addClass('image-annotate-area-editable-hover'); + } + }; + + $.fn.annotateView.prototype.hide = function() { + /// + /// Removes the highlight from the annotation. + /// + this.form.fadeOut(250); + this.area.removeClass('image-annotate-area-hover'); + this.area.removeClass('image-annotate-area-editable-hover'); + }; + + $.fn.annotateView.prototype.destroy = function() { + /// + /// Destroys the annotation. + /// + this.area.remove(); + this.form.remove(); + }; + + $.fn.annotateView.prototype.edit = function(tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Edits the annotation. + /// + if (this.image.mode == 'view') { + this.image.mode = 'edit'; + var annotation = this; + + // Create/prepare the editable note elements + var editable = new $.fn.annotateEdit(this.image, this.note, tags, labels, saveUrl, csrf, rtlsupport, users); + $.fn.annotateImage.createSaveButton(editable, this.image, annotation, rtlsupport, labels); + $.fn.annotateImage.createCancelButton(editable, this.image, rtlsupport, labels); + } + }; + + $.fn.annotateImage.appendPosition = function(form, editable) { + /// + /// Appends the annotations coordinates to the given form that is posted to the server. + /// + var areaFields = $('' + + '' + + '' + + '' + + ''); + form.append(areaFields); + }; + + $.fn.annotateView.prototype.resetPosition = function(editable, text) { + /// + /// Sets the position of an annotation. + /// + this.form.html(text); + this.form.hide(); + + // Resize + this.area.children('div').height(editable.area.height() + 'px'); + this.area.children('div').width((editable.area.width() - 2) + 'px'); + this.area.css('left', (editable.area.position().left) + 'px'); + this.area.css('top', (editable.area.position().top) + 'px'); + this.form.css('left', (editable.area.position().left) + 'px'); + this.form.css('top', (parseInt(editable.area.position().top) + parseInt(editable.area.height()) + 7) + 'px'); + + // Save new position to note + this.note.top = editable.area.position().top; + this.note.left = editable.area.position().left; + this.note.height = editable.area.height(); + this.note.width = editable.area.width(); + this.note.text = text; + this.note.id = editable.note.id; + this.editable = true; + }; + +})(jQuery); diff --git a/modules/photoannotation/js/jquery.annotate.min.js b/modules/photoannotation/js/jquery.annotate.min.js new file mode 100644 index 00000000..200962f4 --- /dev/null +++ b/modules/photoannotation/js/jquery.annotate.min.js @@ -0,0 +1 @@ +(function($){$.fn.annotateImage=function(options){var opts=$.extend({},$.fn.annotateImage.defaults,options);var image=this;this.image=this;this.mode='view';this.getUrl=opts.getUrl;this.saveUrl=opts.saveUrl;this.deleteUrl=opts.deleteUrl;this.deleteUrl=opts.deleteUrl;this.editable=opts.editable;this.useAjax=opts.useAjax;this.tags=opts.tags;this.notes=opts.notes;this.labels=opts.labels;this.csrf=opts.csrf;this.cssaclass=opts.cssaclass;this.rtlsupport=opts.rtlsupport;this.users=opts.users;this.canvas=$('
');this.canvas.children('.image-annotate-edit').hide();this.canvas.children('.image-annotate-view').hide();this.image.after(this.canvas);this.canvas.height(this.height());this.canvas.width(this.width());this.canvas.css('background-image','url("'+this.attr('src')+'")');this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height());this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width());this.canvas.hover(function(){if($(this).children('.image-annotate-edit').css('display')=='none'){$(this).children('.image-annotate-view').show()}},function(){$(this).children('.image-annotate-view').hide();$(this).children('.image-annotate-note').hide()});this.canvas.children('.image-annotate-view').hover(function(){$(this).show()},function(){$(this).hide();$(this).children('.image-annotate-note').hide()});if(this.useAjax){$.fn.annotateImage.ajaxLoad(this)}else{$.fn.annotateImage.load(this,this.labels,this.editable,this.csrf,this.deleteUrl,this.tags,this.saveUrl,this.cssaclass,this.rtlsupport,this.users)}if($('#g-photoannotation-link').length!==0){this.button=$('#g-photoannotation-link');this.button.click(function(){$.fn.annotateImage.add(image,opts.tags,opts.labels,opts.saveUrl,opts.csrf,opts.rtlsupport,opts.users)})}this.hide();return this};$.fn.annotateImage.defaults={getUrl:'your-get.rails',saveUrl:'your-save.rails',deleteUrl:'your-delete.rails',editable:true,useAjax:true,tags:new Array(),notes:new Array()};$.fn.annotateImage.clear=function(image){for(var i=0;i'+labels[8]+'');ok.click(function(){var form=$('#image-annotate-edit-form form');var text=$('#image-annotate-text').val();$.fn.annotateImage.appendPosition(form,editable);image.mode='view';form.submit();editable.destroy()});editable.form.append(ok)};$.fn.annotateImage.createCancelButton=function(editable,image,rtlsupport,labels){var cancel=$(''+labels[9]+'');cancel.click(function(){editable.destroy();image.mode='view';location.reload()});editable.form.append(cancel)};$.fn.annotateImage.saveAsHtml=function(image,target){var element=$(target);var html="";for(var i=0;i'};$.fn.annotateEdit=function(image,note,tags,labels,saveUrl,csrf,rtlsupport,users){this.image=image;if(note){this.note=note}else{var newNote=new Object();newNote.noteid="new";newNote.top=30;newNote.left=30;newNote.width=60;newNote.height=60;newNote.text="";newNote.description="";newNote.notetype="";newNote.internaltext="";this.note=newNote}var area=image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area');this.area=area;this.area.css('height',this.note.height+'px');this.area.css('width',this.note.width+'px');this.area.css('left',this.note.left+'px');this.area.css('top',this.note.top+'px');image.canvas.children('.image-annotate-view').hide();image.canvas.children('.image-annotate-edit').show();var selectedtag="";var notetitle="";var username="";var selecteduser="";if(this.note.notetype=="face"){selectedtag=this.note.text}else if(this.note.notetype=="user"){username=this.note.internaltext}else{notetitle=this.note.text}var form=$('
'+labels[12]+'
'+labels[4]+'
'+labels[4]+'
'+labels[2]+'
');this.form=form;$('body').append(this.form);$("#photoannotation-form").ready(function(){var url=tags;$("input#image-annotate-tag-text").autocomplete(url,{max:30,multiple:false,cacheLength:1});var urlusers=users;$("input#photoannotation-user-list").autocomplete(urlusers,{max:30,multiple:false,cacheLength:1})});$("input#image-annotate-tag-text").keyup(function(){if($("input#image-annotate-tag-text").val()!=""){$("input#image-annotate-text").html("");$("input#image-annotate-text").val("");$("input#photoannotation-user-list").html("");$("input#photoannotation-user-list").val("")}});$("input#image-annotate-text").keyup(function(){if($("input#image-annotate-text").val()!=""){$("input#image-annotate-tag-text").html("");$("input#image-annotate-tag-text").val("");$("input#photoannotation-user-list").html("");$("input#photoannotation-user-list").val("")}});$("input#photoannotation-user-list").keyup(function(){if($("select#photoannotation-user-list").val()!="-1"){$("input#image-annotate-tag-text").html("");$("input#image-annotate-tag-text").val("");$("input#image-annotate-text").html("");$("input#image-annotate-text").val("")}});this.form.css('left',this.area.offset().left+'px');this.form.css('top',(parseInt(this.area.offset().top)+parseInt(this.area.height())+7)+'px');area.resizable({handles:'all',start:function(e,ui){form.hide()},stop:function(e,ui){form.css('left',area.offset().left+'px');form.css('top',(parseInt(area.offset().top)+parseInt(area.height())+7)+'px');form.show()}}).draggable({containment:image.canvas,drag:function(e,ui){form.hide()},stop:function(e,ui){form.css('left',area.offset().left+'px');form.css('top',(parseInt(area.offset().top)+parseInt(area.height())+7)+'px');form.show()}});return this};$.fn.annotateEdit.prototype.destroy=function(){this.image.canvas.children('.image-annotate-edit').hide();this.area.resizable('destroy');this.area.draggable('destroy');this.area.css('height','');this.area.css('width','');this.area.css('left','');this.area.css('top','');this.form.remove()};$.fn.annotateView=function(image,note,tags,labels,editable,csrf,deleteUrl,saveUrl,cssaclass,rtlsupport,users){this.image=image;this.note=note;this.area=$('
');image.canvas.children('.image-annotate-view').prepend(this.area);if(editable){this.delarea=$('
');this.editarea=$('
');image.canvas.children('.image-annotate-view').prepend(this.delarea);image.canvas.children('.image-annotate-view').prepend(this.editarea);this.delarea.bind('click',function(){var alink=$(cssaclass);alink.unbind();alink.attr('href','#');alink.removeAttr('rel');var confdialog='
'+labels[3]+'
';$('body').append(confdialog);var btns={};if(rtlsupport==""){diagclass="inmage-annotate-dialog"}else{diagclass="inmage-annotate-dialog-rtl"}btns[labels[5]]=function(){var delform=$(this).attr("rel");$("form#"+delform).submit()};btns[labels[6]]=function(){location.reload()};$('#image-annotate-conf-dialog').dialog({modal:true,resizable:false,dialogClass:diagclass,title:labels[7],close:function(event,ui){location.reload()},buttons:btns})});var form=this;this.editarea.bind('click',function(){var alink=$(cssaclass);alink.unbind();alink.attr('href','#');alink.removeAttr('rel');form.edit(tags,labels,saveUrl,csrf,rtlsupport,users)});this.delarea.hide();this.editarea.hide()}var notedescription="";if(note.description!=""){notedescription="
"+note.description}this.form=$('
'+note.text+notedescription+'
');this.form.hide();image.canvas.children('.image-annotate-view').append(this.form);this.form.children('span.actions').hide();this.setPosition(rtlsupport);var annotation=this;this.area.hover(function(){annotation.show();if(annotation.delarea!=undefined){annotation.delarea.show();annotation.editarea.show()}},function(){annotation.hide();if(annotation.delarea!=undefined){annotation.delarea.hide();annotation.editarea.hide()}});var legendspan="#photoannotation-legend-"+this.note.notetype+"-"+this.note.noteid;if($(legendspan).length>0){$(legendspan).hover(function(){var legendsarea="#photoannotation-area-"+note.notetype+"-"+note.noteid;$(legendsarea).children('.image-annotate-view').show();$(".image-annotate-view").show();$(legendsarea).show();annotation.show()},function(){var legendsarea="#photoannotation-area-"+note.notetype+"-"+note.noteid;annotation.hide();$(legendsarea).children('.image-annotate-view').hide();$(".image-annotate-view").hide()})}if(editable){this.delarea.hover(function(){annotation.delarea.show();annotation.editarea.show()},function(){annotation.delarea.hide();annotation.editarea.hide()});this.editarea.hover(function(){annotation.delarea.show();annotation.editarea.show()},function(){annotation.delarea.hide();annotation.editarea.hide()})}if(note.url!=""&¬e.url!=null){this.area.bind('click',function(){var alink=$(cssaclass);alink.unbind();alink.attr('href','#');alink.removeAttr('rel');window.location=note.url})}};$.fn.annotateView.prototype.setPosition=function(rtlsupport){this.area.children('div').height((parseInt(this.note.height)-2)+'px');this.area.children('div').width((parseInt(this.note.width)-2)+'px');this.area.css('left',(this.note.left)+'px');this.area.css('top',(this.note.top)+'px');this.form.css('left',(this.note.left)+'px');this.form.css('top',(parseInt(this.note.top)+parseInt(this.note.height)+7)+'px');if(this.delarea!=undefined){this.delarea.children('div').height('14px');this.delarea.children('div').width('14px');this.delarea.css('top',(this.note.top)+'px');this.editarea.children('div').height('14px');this.editarea.children('div').width('14px');if(rtlsupport==''){this.delarea.css('left',(this.note.left+parseInt(this.note.width))+'px');this.editarea.css('left',(this.note.left+parseInt(this.note.width))+'px')}else{this.delarea.css('left',(this.note.left-16)+'px');this.editarea.css('left',(this.note.left-16)+'px')}this.editarea.css('top',(this.note.top+16)+'px')}};$.fn.annotateView.prototype.show=function(){this.form.fadeIn(250);if(!this.note.editable){this.area.addClass('image-annotate-area-hover')}else{this.area.addClass('image-annotate-area-editable-hover')}};$.fn.annotateView.prototype.hide=function(){this.form.fadeOut(250);this.area.removeClass('image-annotate-area-hover');this.area.removeClass('image-annotate-area-editable-hover')};$.fn.annotateView.prototype.destroy=function(){this.area.remove();this.form.remove()};$.fn.annotateView.prototype.edit=function(tags,labels,saveUrl,csrf,rtlsupport,users){if(this.image.mode=='view'){this.image.mode='edit';var annotation=this;var editable=new $.fn.annotateEdit(this.image,this.note,tags,labels,saveUrl,csrf,rtlsupport,users);$.fn.annotateImage.createSaveButton(editable,this.image,annotation,rtlsupport,labels);$.fn.annotateImage.createCancelButton(editable,this.image,rtlsupport,labels)}};$.fn.annotateImage.appendPosition=function(form,editable){var areaFields=$(''+''+''+''+'');form.append(areaFields)};$.fn.annotateView.prototype.resetPosition=function(editable,text){this.form.html(text);this.form.hide();this.area.children('div').height(editable.area.height()+'px');this.area.children('div').width((editable.area.width()-2)+'px');this.area.css('left',(editable.area.position().left)+'px');this.area.css('top',(editable.area.position().top)+'px');this.form.css('left',(editable.area.position().left)+'px');this.form.css('top',(parseInt(editable.area.position().top)+parseInt(editable.area.height())+7)+'px');this.note.top=editable.area.position().top;this.note.left=editable.area.position().left;this.note.height=editable.area.height();this.note.width=editable.area.width();this.note.text=text;this.note.id=editable.note.id;this.editable=true}})(jQuery); diff --git a/modules/photoannotation/js/jquery.colorpicker.js b/modules/photoannotation/js/jquery.colorpicker.js new file mode 100644 index 00000000..802e7c18 --- /dev/null +++ b/modules/photoannotation/js/jquery.colorpicker.js @@ -0,0 +1,484 @@ +/** + * + * Color picker + * Author: Stefan Petre www.eyecon.ro + * + * Dual licensed under the MIT and GPL licenses + * + */ +(function ($) { + var ColorPicker = function () { + var + ids = {}, + inAction, + charMin = 65, + visible, + tpl = '
', + defaults = { + eventName: 'click', + onShow: function () {}, + onBeforeShow: function(){}, + onHide: function () {}, + onChange: function () {}, + onSubmit: function () {}, + color: 'ff0000', + livePreview: true, + flat: false + }, + fillRGBFields = function (hsb, cal) { + var rgb = HSBToRGB(hsb); + $(cal).data('colorpicker').fields + .eq(1).val(rgb.r).end() + .eq(2).val(rgb.g).end() + .eq(3).val(rgb.b).end(); + }, + fillHSBFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(4).val(hsb.h).end() + .eq(5).val(hsb.s).end() + .eq(6).val(hsb.b).end(); + }, + fillHexFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(0).val(HSBToHex(hsb)).end(); + }, + setSelector = function (hsb, cal) { + $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); + $(cal).data('colorpicker').selectorIndic.css({ + left: parseInt(150 * hsb.s/100, 10), + top: parseInt(150 * (100-hsb.b)/100, 10) + }); + }, + setHue = function (hsb, cal) { + $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); + }, + setCurrentColor = function (hsb, cal) { + $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + setNewColor = function (hsb, cal) { + $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + keyDown = function (ev) { + var pressedKey = ev.charCode || ev.keyCode || -1; + if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { + return false; + } + var cal = $(this).parent().parent(); + if (cal.data('colorpicker').livePreview === true) { + change.apply(this); + } + }, + change = function (ev) { + var cal = $(this).parent().parent(), col; + if (this.parentNode.className.indexOf('_hex') > 0) { + cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); + } else if (this.parentNode.className.indexOf('_hsb') > 0) { + cal.data('colorpicker').color = col = fixHSB({ + h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), + s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) + }); + } else { + cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ + r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), + g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) + })); + } + if (ev) { + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); + }, + blur = function (ev) { + var cal = $(this).parent().parent(); + cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + }, + focus = function () { + charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; + $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + $(this).parent().addClass('colorpicker_focus'); + }, + downIncrement = function (ev) { + var field = $(this).parent().find('input').focus(); + var current = { + el: $(this).parent().addClass('colorpicker_slider'), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + y: ev.pageY, + field: field, + val: parseInt(field.val(), 10), + preview: $(this).parent().parent().data('colorpicker').livePreview + }; + $(document).bind('mouseup', current, upIncrement); + $(document).bind('mousemove', current, moveIncrement); + }, + moveIncrement = function (ev) { + ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); + if (ev.data.preview) { + change.apply(ev.data.field.get(0), [true]); + } + return false; + }, + upIncrement = function (ev) { + change.apply(ev.data.field.get(0), [true]); + ev.data.el.removeClass('colorpicker_slider').find('input').focus(); + $(document).unbind('mouseup', upIncrement); + $(document).unbind('mousemove', moveIncrement); + return false; + }, + downHue = function (ev) { + var current = { + cal: $(this).parent(), + y: $(this).offset().top + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upHue); + $(document).bind('mousemove', current, moveHue); + }, + moveHue = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(4) + .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upHue = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upHue); + $(document).unbind('mousemove', moveHue); + return false; + }, + downSelector = function (ev) { + var current = { + cal: $(this).parent(), + pos: $(this).offset() + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upSelector); + $(document).bind('mousemove', current, moveSelector); + }, + moveSelector = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(6) + .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) + .end() + .eq(5) + .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upSelector = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upSelector); + $(document).unbind('mousemove', moveSelector); + return false; + }, + enterSubmit = function (ev) { + $(this).addClass('colorpicker_focus'); + }, + leaveSubmit = function (ev) { + $(this).removeClass('colorpicker_focus'); + }, + clickSubmit = function (ev) { + var cal = $(this).parent(); + var col = cal.data('colorpicker').color; + cal.data('colorpicker').origColor = col; + setCurrentColor(col, cal.get(0)); + cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); + }, + show = function (ev) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); + var pos = $(this).offset(); + var viewPort = getViewport(); + var top = pos.top + this.offsetHeight; + var left = pos.left; + if (top + 176 > viewPort.t + viewPort.h) { + top -= this.offsetHeight + 176; + } + if (left + 356 > viewPort.l + viewPort.w) { + left -= 356; + } + cal.css({left: left + 'px', top: top + 'px'}); + if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { + cal.show(); + } + $(document).bind('mousedown', {cal: cal}, hide); + return false; + }, + hide = function (ev) { + if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { + if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + ev.data.cal.hide(); + } + $(document).unbind('mousedown', hide); + } + }, + isChildOf = function(parentEl, el, container) { + if (parentEl == el) { + return true; + } + if (parentEl.contains) { + return parentEl.contains(el); + } + if ( parentEl.compareDocumentPosition ) { + return !!(parentEl.compareDocumentPosition(el) & 16); + } + var prEl = el.parentNode; + while(prEl && prEl != container) { + if (prEl == parentEl) + return true; + prEl = prEl.parentNode; + } + return false; + }, + getViewport = function () { + var m = document.compatMode == 'CSS1Compat'; + return { + l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), + t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), + w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), + h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) + }; + }, + fixHSB = function (hsb) { + return { + h: Math.min(360, Math.max(0, hsb.h)), + s: Math.min(100, Math.max(0, hsb.s)), + b: Math.min(100, Math.max(0, hsb.b)) + }; + }, + fixRGB = function (rgb) { + return { + r: Math.min(255, Math.max(0, rgb.r)), + g: Math.min(255, Math.max(0, rgb.g)), + b: Math.min(255, Math.max(0, rgb.b)) + }; + }, + fixHex = function (hex) { + var len = 6 - hex.length; + if (len > 0) { + var o = []; + for (var i=0; i -1) ? hex.substring(1) : hex), 16); + return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + }, + HexToHSB = function (hex) { + return RGBToHSB(HexToRGB(hex)); + }, + RGBToHSB = function (rgb) { + var hsb = { + h: 0, + s: 0, + b: 0 + }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + if (max != 0) { + + } + hsb.s = max != 0 ? 255 * delta / max : 0; + if (hsb.s != 0) { + if (rgb.r == max) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if (rgb.g == max) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if (hsb.h < 0) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }, + HSBToRGB = function (hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s == 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255-s)*v/255; + var t3 = (t1-t2)*(h%60)/60; + if(h==360) h = 0; + if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} + else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} + else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} + else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} + else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} + else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} + else {rgb.r=0; rgb.g=0; rgb.b=0} + } + return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; + }, + RGBToHex = function (rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function (nr, val) { + if (val.length == 1) { + hex[nr] = '0' + val; + } + }); + return hex.join(''); + }, + HSBToHex = function (hsb) { + return RGBToHex(HSBToRGB(hsb)); + }, + restoreOriginal = function () { + var cal = $(this).parent(); + var col = cal.data('colorpicker').origColor; + cal.data('colorpicker').color = col; + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + }; + return { + init: function (opt) { + opt = $.extend({}, defaults, opt||{}); + if (typeof opt.color == 'string') { + opt.color = HexToHSB(opt.color); + } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { + opt.color = RGBToHSB(opt.color); + } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { + opt.color = fixHSB(opt.color); + } else { + return this; + } + return this.each(function () { + if (!$(this).data('colorpickerId')) { + var options = $.extend({}, opt); + options.origColor = opt.color; + var id = 'collorpicker_' + parseInt(Math.random() * 1000); + $(this).data('colorpickerId', id); + var cal = $(tpl).attr('id', id); + if (options.flat) { + cal.appendTo(this).show(); + } else { + cal.appendTo(document.body); + } + options.fields = cal + .find('input') + .bind('keyup', keyDown) + .bind('change', change) + .bind('blur', blur) + .bind('focus', focus); + cal + .find('span').bind('mousedown', downIncrement).end() + .find('>div.colorpicker_current_color').bind('click', restoreOriginal); + options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); + options.selectorIndic = options.selector.find('div div'); + options.el = this; + options.hue = cal.find('div.colorpicker_hue div'); + cal.find('div.colorpicker_hue').bind('mousedown', downHue); + options.newColor = cal.find('div.colorpicker_new_color'); + options.currentColor = cal.find('div.colorpicker_current_color'); + cal.data('colorpicker', options); + cal.find('div.colorpicker_submit') + .bind('mouseenter', enterSubmit) + .bind('mouseleave', leaveSubmit) + .bind('click', clickSubmit); + fillRGBFields(options.color, cal.get(0)); + fillHSBFields(options.color, cal.get(0)); + fillHexFields(options.color, cal.get(0)); + setHue(options.color, cal.get(0)); + setSelector(options.color, cal.get(0)); + setCurrentColor(options.color, cal.get(0)); + setNewColor(options.color, cal.get(0)); + if (options.flat) { + cal.css({ + position: 'relative', + display: 'block' + }); + } else { + $(this).bind(options.eventName, show); + } + } + }); + }, + showPicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + show.apply(this); + } + }); + }, + hidePicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + $('#' + $(this).data('colorpickerId')).hide(); + } + }); + }, + setColor: function(col) { + if (typeof col == 'string') { + col = HexToHSB(col); + } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + col = RGBToHSB(col); + } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + col = fixHSB(col); + } else { + return this; + } + return this.each(function(){ + if ($(this).data('colorpickerId')) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').color = col; + cal.data('colorpicker').origColor = col; + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + setHue(col, cal.get(0)); + setSelector(col, cal.get(0)); + setCurrentColor(col, cal.get(0)); + setNewColor(col, cal.get(0)); + } + }); + } + }; + }(); + $.fn.extend({ + ColorPicker: ColorPicker.init, + ColorPickerHide: ColorPicker.hidePicker, + ColorPickerShow: ColorPicker.showPicker, + ColorPickerSetColor: ColorPicker.setColor + }); +})(jQuery); diff --git a/modules/photoannotation/js/jquery.colorpicker.min.js b/modules/photoannotation/js/jquery.colorpicker.min.js new file mode 100644 index 00000000..36da9f98 --- /dev/null +++ b/modules/photoannotation/js/jquery.colorpicker.min.js @@ -0,0 +1 @@ +(function($){var ColorPicker=function(){var ids={},inAction,charMin=65,visible,tpl='
',defaults={eventName:'click',onShow:function(){},onBeforeShow:function(){},onHide:function(){},onChange:function(){},onSubmit:function(){},color:'ff0000',livePreview:true,flat:false},fillRGBFields=function(hsb,cal){var rgb=HSBToRGB(hsb);$(cal).data('colorpicker').fields.eq(1).val(rgb.r).end().eq(2).val(rgb.g).end().eq(3).val(rgb.b).end()},fillHSBFields=function(hsb,cal){$(cal).data('colorpicker').fields.eq(4).val(hsb.h).end().eq(5).val(hsb.s).end().eq(6).val(hsb.b).end()},fillHexFields=function(hsb,cal){$(cal).data('colorpicker').fields.eq(0).val(HSBToHex(hsb)).end()},setSelector=function(hsb,cal){$(cal).data('colorpicker').selector.css('backgroundColor','#'+HSBToHex({h:hsb.h,s:100,b:100}));$(cal).data('colorpicker').selectorIndic.css({left:parseInt(150*hsb.s/100,10),top:parseInt(150*(100-hsb.b)/100,10)})},setHue=function(hsb,cal){$(cal).data('colorpicker').hue.css('top',parseInt(150-150*hsb.h/360,10))},setCurrentColor=function(hsb,cal){$(cal).data('colorpicker').currentColor.css('backgroundColor','#'+HSBToHex(hsb))},setNewColor=function(hsb,cal){$(cal).data('colorpicker').newColor.css('backgroundColor','#'+HSBToHex(hsb))},keyDown=function(ev){var pressedKey=ev.charCode||ev.keyCode||-1;if((pressedKey>charMin&&pressedKey<=90)||pressedKey==32){return false}var cal=$(this).parent().parent();if(cal.data('colorpicker').livePreview===true){change.apply(this)}},change=function(ev){var cal=$(this).parent().parent(),col;if(this.parentNode.className.indexOf('_hex')>0){cal.data('colorpicker').color=col=HexToHSB(fixHex(this.value))}else if(this.parentNode.className.indexOf('_hsb')>0){cal.data('colorpicker').color=col=fixHSB({h:parseInt(cal.data('colorpicker').fields.eq(4).val(),10),s:parseInt(cal.data('colorpicker').fields.eq(5).val(),10),b:parseInt(cal.data('colorpicker').fields.eq(6).val(),10)})}else{cal.data('colorpicker').color=col=RGBToHSB(fixRGB({r:parseInt(cal.data('colorpicker').fields.eq(1).val(),10),g:parseInt(cal.data('colorpicker').fields.eq(2).val(),10),b:parseInt(cal.data('colorpicker').fields.eq(3).val(),10)}))}if(ev){fillRGBFields(col,cal.get(0));fillHexFields(col,cal.get(0));fillHSBFields(col,cal.get(0))}setSelector(col,cal.get(0));setHue(col,cal.get(0));setNewColor(col,cal.get(0));cal.data('colorpicker').onChange.apply(cal,[col,HSBToHex(col),HSBToRGB(col)])},blur=function(ev){var cal=$(this).parent().parent();cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus')},focus=function(){charMin=this.parentNode.className.indexOf('_hex')>0?70:65;$(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus');$(this).parent().addClass('colorpicker_focus')},downIncrement=function(ev){var field=$(this).parent().find('input').focus();var current={el:$(this).parent().addClass('colorpicker_slider'),max:this.parentNode.className.indexOf('_hsb_h')>0?360:(this.parentNode.className.indexOf('_hsb')>0?100:255),y:ev.pageY,field:field,val:parseInt(field.val(),10),preview:$(this).parent().parent().data('colorpicker').livePreview};$(document).bind('mouseup',current,upIncrement);$(document).bind('mousemove',current,moveIncrement)},moveIncrement=function(ev){ev.data.field.val(Math.max(0,Math.min(ev.data.max,parseInt(ev.data.val+ev.pageY-ev.data.y,10))));if(ev.data.preview){change.apply(ev.data.field.get(0),[true])}return false},upIncrement=function(ev){change.apply(ev.data.field.get(0),[true]);ev.data.el.removeClass('colorpicker_slider').find('input').focus();$(document).unbind('mouseup',upIncrement);$(document).unbind('mousemove',moveIncrement);return false},downHue=function(ev){var current={cal:$(this).parent(),y:$(this).offset().top};current.preview=current.cal.data('colorpicker').livePreview;$(document).bind('mouseup',current,upHue);$(document).bind('mousemove',current,moveHue)},moveHue=function(ev){change.apply(ev.data.cal.data('colorpicker').fields.eq(4).val(parseInt(360*(150-Math.max(0,Math.min(150,(ev.pageY-ev.data.y))))/150,10)).get(0),[ev.data.preview]);return false},upHue=function(ev){fillRGBFields(ev.data.cal.data('colorpicker').color,ev.data.cal.get(0));fillHexFields(ev.data.cal.data('colorpicker').color,ev.data.cal.get(0));$(document).unbind('mouseup',upHue);$(document).unbind('mousemove',moveHue);return false},downSelector=function(ev){var current={cal:$(this).parent(),pos:$(this).offset()};current.preview=current.cal.data('colorpicker').livePreview;$(document).bind('mouseup',current,upSelector);$(document).bind('mousemove',current,moveSelector)},moveSelector=function(ev){change.apply(ev.data.cal.data('colorpicker').fields.eq(6).val(parseInt(100*(150-Math.max(0,Math.min(150,(ev.pageY-ev.data.pos.top))))/150,10)).end().eq(5).val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX-ev.data.pos.left))))/150,10)).get(0),[ev.data.preview]);return false},upSelector=function(ev){fillRGBFields(ev.data.cal.data('colorpicker').color,ev.data.cal.get(0));fillHexFields(ev.data.cal.data('colorpicker').color,ev.data.cal.get(0));$(document).unbind('mouseup',upSelector);$(document).unbind('mousemove',moveSelector);return false},enterSubmit=function(ev){$(this).addClass('colorpicker_focus')},leaveSubmit=function(ev){$(this).removeClass('colorpicker_focus')},clickSubmit=function(ev){var cal=$(this).parent();var col=cal.data('colorpicker').color;cal.data('colorpicker').origColor=col;setCurrentColor(col,cal.get(0));cal.data('colorpicker').onSubmit(col,HSBToHex(col),HSBToRGB(col),cal.data('colorpicker').el)},show=function(ev){var cal=$('#'+$(this).data('colorpickerId'));cal.data('colorpicker').onBeforeShow.apply(this,[cal.get(0)]);var pos=$(this).offset();var viewPort=getViewport();var top=pos.top+this.offsetHeight;var left=pos.left;if(top+176>viewPort.t+viewPort.h){top-=this.offsetHeight+176}if(left+356>viewPort.l+viewPort.w){left-=356}cal.css({left:left+'px',top:top+'px'});if(cal.data('colorpicker').onShow.apply(this,[cal.get(0)])!=false){cal.show()}$(document).bind('mousedown',{cal:cal},hide);return false},hide=function(ev){if(!isChildOf(ev.data.cal.get(0),ev.target,ev.data.cal.get(0))){if(ev.data.cal.data('colorpicker').onHide.apply(this,[ev.data.cal.get(0)])!=false){ev.data.cal.hide()}$(document).unbind('mousedown',hide)}},isChildOf=function(parentEl,el,container){if(parentEl==el){return true}if(parentEl.contains){return parentEl.contains(el)}if(parentEl.compareDocumentPosition){return!!(parentEl.compareDocumentPosition(el)&16)}var prEl=el.parentNode;while(prEl&&prEl!=container){if(prEl==parentEl)return true;prEl=prEl.parentNode}return false},getViewport=function(){var m=document.compatMode=='CSS1Compat';return{l:window.pageXOffset||(m?document.documentElement.scrollLeft:document.body.scrollLeft),t:window.pageYOffset||(m?document.documentElement.scrollTop:document.body.scrollTop),w:window.innerWidth||(m?document.documentElement.clientWidth:document.body.clientWidth),h:window.innerHeight||(m?document.documentElement.clientHeight:document.body.clientHeight)}},fixHSB=function(hsb){return{h:Math.min(360,Math.max(0,hsb.h)),s:Math.min(100,Math.max(0,hsb.s)),b:Math.min(100,Math.max(0,hsb.b))}},fixRGB=function(rgb){return{r:Math.min(255,Math.max(0,rgb.r)),g:Math.min(255,Math.max(0,rgb.g)),b:Math.min(255,Math.max(0,rgb.b))}},fixHex=function(hex){var len=6-hex.length;if(len>0){var o=[];for(var i=0;i-1)?hex.substring(1):hex),16);return{r:hex>>16,g:(hex&0x00FF00)>>8,b:(hex&0x0000FF)}},HexToHSB=function(hex){return RGBToHSB(HexToRGB(hex))},RGBToHSB=function(rgb){var hsb={h:0,s:0,b:0};var min=Math.min(rgb.r,rgb.g,rgb.b);var max=Math.max(rgb.r,rgb.g,rgb.b);var delta=max-min;hsb.b=max;if(max!=0){}hsb.s=max!=0?255*delta/max:0;if(hsb.s!=0){if(rgb.r==max){hsb.h=(rgb.g-rgb.b)/delta}else if(rgb.g==max){hsb.h=2+(rgb.b-rgb.r)/delta}else{hsb.h=4+(rgb.r-rgb.g)/delta}}else{hsb.h=-1}hsb.h*=60;if(hsb.h<0){hsb.h+=360}hsb.s*=100/255;hsb.b*=100/255;return hsb},HSBToRGB=function(hsb){var rgb={};var h=Math.round(hsb.h);var s=Math.round(hsb.s*255/100);var v=Math.round(hsb.b*255/100);if(s==0){rgb.r=rgb.g=rgb.b=v}else{var t1=v;var t2=(255-s)*v/255;var t3=(t1-t2)*(h%60)/60;if(h==360)h=0;if(h<60){rgb.r=t1;rgb.b=t2;rgb.g=t2+t3}else if(h<120){rgb.g=t1;rgb.b=t2;rgb.r=t1-t3}else if(h<180){rgb.g=t1;rgb.r=t2;rgb.b=t2+t3}else if(h<240){rgb.b=t1;rgb.r=t2;rgb.g=t1-t3}else if(h<300){rgb.b=t1;rgb.g=t2;rgb.r=t2+t3}else if(h<360){rgb.r=t1;rgb.g=t2;rgb.b=t1-t3}else{rgb.r=0;rgb.g=0;rgb.b=0}}return{r:Math.round(rgb.r),g:Math.round(rgb.g),b:Math.round(rgb.b)}},RGBToHex=function(rgb){var hex=[rgb.r.toString(16),rgb.g.toString(16),rgb.b.toString(16)];$.each(hex,function(nr,val){if(val.length==1){hex[nr]='0'+val}});return hex.join('')},HSBToHex=function(hsb){return RGBToHex(HSBToRGB(hsb))},restoreOriginal=function(){var cal=$(this).parent();var col=cal.data('colorpicker').origColor;cal.data('colorpicker').color=col;fillRGBFields(col,cal.get(0));fillHexFields(col,cal.get(0));fillHSBFields(col,cal.get(0));setSelector(col,cal.get(0));setHue(col,cal.get(0));setNewColor(col,cal.get(0))};return{init:function(opt){opt=$.extend({},defaults,opt||{});if(typeof opt.color=='string'){opt.color=HexToHSB(opt.color)}else if(opt.color.r!=undefined&&opt.color.g!=undefined&&opt.color.b!=undefined){opt.color=RGBToHSB(opt.color)}else if(opt.color.h!=undefined&&opt.color.s!=undefined&&opt.color.b!=undefined){opt.color=fixHSB(opt.color)}else{return this}return this.each(function(){if(!$(this).data('colorpickerId')){var options=$.extend({},opt);options.origColor=opt.color;var id='collorpicker_'+parseInt(Math.random()*1000);$(this).data('colorpickerId',id);var cal=$(tpl).attr('id',id);if(options.flat){cal.appendTo(this).show()}else{cal.appendTo(document.body)}options.fields=cal.find('input').bind('keyup',keyDown).bind('change',change).bind('blur',blur).bind('focus',focus);cal.find('span').bind('mousedown',downIncrement).end().find('>div.colorpicker_current_color').bind('click',restoreOriginal);options.selector=cal.find('div.colorpicker_color').bind('mousedown',downSelector);options.selectorIndic=options.selector.find('div div');options.el=this;options.hue=cal.find('div.colorpicker_hue div');cal.find('div.colorpicker_hue').bind('mousedown',downHue);options.newColor=cal.find('div.colorpicker_new_color');options.currentColor=cal.find('div.colorpicker_current_color');cal.data('colorpicker',options);cal.find('div.colorpicker_submit').bind('mouseenter',enterSubmit).bind('mouseleave',leaveSubmit).bind('click',clickSubmit);fillRGBFields(options.color,cal.get(0));fillHSBFields(options.color,cal.get(0));fillHexFields(options.color,cal.get(0));setHue(options.color,cal.get(0));setSelector(options.color,cal.get(0));setCurrentColor(options.color,cal.get(0));setNewColor(options.color,cal.get(0));if(options.flat){cal.css({position:'relative',display:'block'})}else{$(this).bind(options.eventName,show)}}})},showPicker:function(){return this.each(function(){if($(this).data('colorpickerId')){show.apply(this)}})},hidePicker:function(){return this.each(function(){if($(this).data('colorpickerId')){$('#'+$(this).data('colorpickerId')).hide()}})},setColor:function(col){if(typeof col=='string'){col=HexToHSB(col)}else if(col.r!=undefined&&col.g!=undefined&&col.b!=undefined){col=RGBToHSB(col)}else if(col.h!=undefined&&col.s!=undefined&&col.b!=undefined){col=fixHSB(col)}else{return this}return this.each(function(){if($(this).data('colorpickerId')){var cal=$('#'+$(this).data('colorpickerId'));cal.data('colorpicker').color=col;cal.data('colorpicker').origColor=col;fillRGBFields(col,cal.get(0));fillHSBFields(col,cal.get(0));fillHexFields(col,cal.get(0));setHue(col,cal.get(0));setSelector(col,cal.get(0));setCurrentColor(col,cal.get(0));setNewColor(col,cal.get(0))}})}}}();$.fn.extend({ColorPicker:ColorPicker.init,ColorPickerHide:ColorPicker.hidePicker,ColorPickerShow:ColorPicker.showPicker,ColorPickerSetColor:ColorPicker.setColor})})(jQuery); diff --git a/modules/photoannotation/models/items_face.php b/modules/photoannotation/models/items_face.php new file mode 100644 index 00000000..8f3a4072 --- /dev/null +++ b/modules/photoannotation/models/items_face.php @@ -0,0 +1,21 @@ + +
+

+

+

TagFaces module by rWatcher.
+ This means that notes and faces that you create in either one will be shown and are editable by the other module as well. If you added users to an annotation area though they will only be displayed with the Photo Annotation module.
+ You cannot have both active at the same time.", array("tagfaces" => "http://codex.gallery2.org/Gallery3:Modules:tagfaces")) ?> +

Convert existing tag annotations to user annotations", array("url" => url::site("admin/photoannotation/converter/"))) ?> +
Check for orphaned annotations", array("url" => url::site("admin/photoannotation/tagsmaintanance/"))) ?>

+ +
+ diff --git a/modules/photoannotation/views/admin_photoannotation_converter.html.php b/modules/photoannotation/views/admin_photoannotation_converter.html.php new file mode 100644 index 00000000..4b8dfa1d --- /dev/null +++ b/modules/photoannotation/views/admin_photoannotation_converter.html.php @@ -0,0 +1,10 @@ + +
+

+

+



+ +
Back to photo annotation settings", array("url" => url::site("admin/photoannotation/"))) ?> +
Check for orphaned annotations", array("url" => url::site("admin/photoannotation/tagsmaintanance/"))) ?>

+ +
diff --git a/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php b/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php new file mode 100644 index 00000000..9e373780 --- /dev/null +++ b/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php @@ -0,0 +1,27 @@ + +
+

+

+

+

Back to photo annotation settings", array("url" => url::site("admin/photoannotation/"))) ?> +
Convert existing tag annotations to user annotations", array("url" => url::site("admin/photoannotation/converter/"))) ?>

+ +

+

$user_orphanes_deleted)) ?> +
$tag_orpanes_deleted)) ?> +
$item_orphanes_deleted)) ?> +

+ Back", array("url" => url::site("admin/photoannotation/"))) ?> + +

+

$user_orphanes_count)) ?> +
$tag_orpanes_count)) ?> +
$item_orphanes_count)) ?> +

+ + Back", array("url" => url::site("admin/photoannotation/"))) ?> + + Remove all", array("url" => url::site("admin/photoannotation/tagsmaintanance/true"))) ?> + + +
diff --git a/modules/photoannotation/views/photoannotation_block.html.php b/modules/photoannotation/views/photoannotation_block.html.php new file mode 100644 index 00000000..8861699a --- /dev/null +++ b/modules/photoannotation/views/photoannotation_block.html.php @@ -0,0 +1,17 @@ + + +
"> + +
+ \ No newline at end of file diff --git a/modules/photoannotation/views/photoannotation_cloud.html.php b/modules/photoannotation/views/photoannotation_cloud.html.php new file mode 100644 index 00000000..6f47b272 --- /dev/null +++ b/modules/photoannotation/views/photoannotation_cloud.html.php @@ -0,0 +1,9 @@ + +
    + +
  • + size ?> photos are tagged with + name) ?> +
  • + +
diff --git a/modules/photoannotation/views/photoannotation_highlight_block.html.php b/modules/photoannotation/views/photoannotation_highlight_block.html.php new file mode 100644 index 00000000..814bc600 --- /dev/null +++ b/modules/photoannotation/views/photoannotation_highlight_block.html.php @@ -0,0 +1,135 @@ +where("item_id", "=", $item->id) + ->find_all(); + $existingFaces = ORM::factory("items_face") + ->where("item_id", "=", $item->id) + ->find_all(); + $existingNotes = ORM::factory("items_note") + ->where("item_id", "=", $item->id) + ->find_all(); + $fullname = module::get_var("photoannotation", "fullname", false); + $showusers = module::get_var("photoannotation", "showusers", false); + $showfaces = module::get_var("photoannotation", "showfaces", false); + $shownotes = module::get_var("photoannotation", "shownotes", false); + if (locales::is_rtl()) { + $rtl_support = "image-annotate-rtl"; + } else { + $rtl_support = ""; + } + $tags_arraystring = ""; + $jscode = ""; + $legend_faces = ""; + $legend_notes = ""; + $legend_users = ""; + if (module::get_var("gallery", "active_site_theme") == "greydragon") { + $css_item_id = "#g-photo-id-". $item->id; + $css_a_class = ".g-sb-preview"; + } else { + $css_item_id = "#g-item-id-". $item->id; + $css_a_class = ".g-fullsize-link"; + } + // If it does, then insert some javascript and display an image map + // to show where the faces are at. + if ((count($existingFaces) > 0) || (count($existingNotes) > 0) || (count($existingUsers) > 0)) { + $jscode = "notes: [ "; + foreach ($existingUsers as $oneUser) { + $oneTag = ORM::factory("user", $oneUser->user_id); + if ($oneTag->loaded()) { + if ($fullname) { + $user_text = $oneTag->display_name(); + } else { + $user_text = $oneTag->name; + } + if ($showusers) { + $legend_users .= "id . "\">user_id) ."\">". html::clean($user_text) .", "; + } + $jscode .= "{ \"top\": ". $oneUser->y1 .",\n"; + $jscode .= "\"left\": ". $oneUser->x1 .",\n"; + $jscode .= "\"width\": ". ($oneUser->x2 - $oneUser->x1) .",\n"; + $jscode .= "\"height\": ". ($oneUser->y2 - $oneUser->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($user_text) ."\",\n"; + $jscode .= "\"internaltext\": \"". $oneTag->display_name() ." (". $oneTag->name .")\",\n"; + $jscode .= "\"description\": \"". html::clean($oneUser->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneUser->id .",\n"; + $jscode .= "\"notetype\": \"user\",\n"; + $jscode .= "\"editable\": true,\n"; + $jscode .= "\"url\": \"". user_profile::url($oneUser->user_id) ."\" },\n"; + } + } + if ($legend_users != "") { + $legend_users = trim($legend_users, ", "); + $legend_users = t("People on this photo: ") . $legend_users ."
"; + } + foreach ($existingFaces as $oneFace) { + $oneTag = ORM::factory("tag", $oneFace->tag_id); + if ($oneTag->loaded()) { + if ($showfaces) { + $legend_faces .= "id . "\">url() ."\">". html::clean($oneTag->name) .", "; + } + $jscode .= "{ \"top\": ". $oneFace->y1 .",\n"; + $jscode .= "\"left\": ". $oneFace->x1 .",\n"; + $jscode .= "\"width\": ". ($oneFace->x2 - $oneFace->x1) .",\n"; + $jscode .= "\"height\": ". ($oneFace->y2 - $oneFace->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($oneTag->name) ."\",\n"; + $jscode .= "\"description\": \"". html::clean($oneFace->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneFace->id .",\n"; + $jscode .= "\"notetype\": \"face\",\n"; + $jscode .= "\"editable\": true,\n"; + $jscode .= "\"url\": \"". $oneTag->url() ."\" },\n"; + } + } + if ($legend_faces != "") { + $legend_faces = trim($legend_faces, ", "); + $legend_faces = t("Faces on this photo: ") . $legend_faces ."
"; + } + foreach ($existingNotes as $oneNote) { + if ($shownotes) { + $legend_notes .= "id . "\">". html::clean($oneNote->title) .", "; + } + $jscode .= "{ \"top\": ". $oneNote->y1 .",\n"; + $jscode .= "\"left\": ". $oneNote->x1 .",\n"; + $jscode .= "\"width\": ". ($oneNote->x2 - $oneNote->x1) .",\n"; + $jscode .= "\"height\": ". ($oneNote->y2 - $oneNote->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($oneNote->title) ."\",\n"; + $jscode .= "\"description\": \"". html::clean($oneNote->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneNote->id .",\n"; + $jscode .= "\"notetype\": \"note\",\n"; + $jscode .= "\"editable\": false,\n"; + $jscode .= "\"url\": \"\" },\n"; + } + $jscode = trim($jscode, ",\n"); + $jscode .= " ],"; + if ($legend_notes != "") { + $legend_notes = trim($legend_notes, ", "); + $legend_notes = t("Notes on this photo: ") . $legend_notes ."
"; + } + } + $legend_display = $legend_users . $legend_faces . $legend_notes; + $labels_arraystring = "labels: [ '". t("Tag:") ."','". t("Note Title:") ."','". t("Description (optional)") ."','". t("Are you sure you want to delete this annotation?") ."','". t("or") ."','". t("Yes") ."','". t("No") ."','". t("Confirm deletion") ."','". t("Save") ."','". t("Cancel") ."','". t("Person:") ."','". t("No user selected") ."','". t("Select one of the following") ."' ],"; +?> + + + ". $legend_display ."
" ?> + diff --git a/modules/photoannotation/views/photoannotation_user_search.html.php b/modules/photoannotation/views/photoannotation_user_search.html.php new file mode 100644 index 00000000..2182a324 --- /dev/null +++ b/modules/photoannotation/views/photoannotation_user_search.html.php @@ -0,0 +1,55 @@ + + +
"> +

+ + + + + id) ."\">" ?> +
+

" + alt="display_name()) ?>" + class="g-avatar" width="40" height="40" /> + name ?>

+
+ + + + + + + + + + + + + + + + +
display_name() ?>
id) ?>
id) ?>
+
+
+ + + + + +
-- cgit v1.2.3