README.md | 8 +- css/edit.css | 143 +- edit.module | 174 +- generate-core-patch.sh | 9 +- grunt.js | 41 +- images/attention.png | Bin 1687 -> 409 bytes images/close.png | Bin 3276 -> 333 bytes images/icon-edit-active.png | Bin 0 -> 358 bytes images/icon-edit.png | Bin 0 -> 498 bytes images/throbber.gif | Bin 13395 -> 1032 bytes includes/form.inc | 68 +- includes/missing-api.inc | 43 - js/app.js | 20 +- js/backbone.drupalform.js | 9 +- js/createjs/editable.js | 4 +- js/createjs/editingWidgets/drupalalohawidget.js | 20 +- .../editingWidgets/drupalcontenteditablewidget.js | 7 +- js/createjs/editingWidgets/formwidget.js | 10 +- js/edit.js | 4 +- js/lib/create.js | 1643 ------ js/lib/vie.js | 3682 ------------- js/theme.js | 4 +- js/viejs/EditService.js | 201 + js/viejs/SparkEditService.js | 208 - js/views/fielddecorator-view.js | 324 -- js/views/overlay-view.js | 2 - js/views/propertyeditordecoration-view.js | 322 ++ js/views/toolbar-view.js | 13 +- vie-and-create.patch | 5401 ++++++++++++++++++++ 29 files changed, 6141 insertions(+), 6219 deletions(-) diff --git a/README.md b/README.md index 391048c..628e59f 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,16 @@ # Requirements +- Please apply the included core patch "vie-and-create.patch". - Please also apply the included core patch "text.patch". Without it, you wont' be able to use direct editing, with or without WYSIWYG. - Optionally, if you also want WYSIWYG editing, install the latest version of Aloha Editor for Drupal 8: http://drupal.org/project/aloha # D8 port notes -* D8 has no Entity Access API yet, so I implemented a node-only stub in includes/missing-api.inc. +* D8 has no Entity Access API yet, so I implemented a node-only stub. * Node title/author/date are not yet proper Entity Properties, and thus they are not yet editable. -# TODO - -Search the code base for "@todo" - - # Want to see it? Go to a node page (e.g. node/1), at the top of the page you should see the "editbar", with a "View/Quick edit" toggle. Click that and now you should be able to see outlines around every field, if you click any of those, you should get an in-place form for that field. diff --git a/css/edit.css b/css/edit.css index 9109d62..3480a2b 100644 --- a/css/edit.css +++ b/css/edit.css @@ -2,13 +2,13 @@ * Animations. */ .edit-animate-invisible { - opacity: 0 !important; + opacity: 0; } .edit-animate-fast { -webkit-transition: all .2s ease; -moz-transition: all .2s ease; - -ie-transition: all .2s ease; + -ms-transition: all .2s ease; -o-transition: all .2s ease; transition: all .2s ease; } @@ -16,7 +16,7 @@ .edit-animate-default { -webkit-transition: all .4s ease; -moz-transition: all .4s ease; - -ie-transition: all .4s ease; + -ms-transition: all .4s ease; -o-transition: all .4s ease; transition: all .4s ease; } @@ -24,105 +24,58 @@ .edit-animate-slow { -webkit-transition: all .6s ease; -moz-transition: all .6s ease; - -ie-transition: all .6s ease; + -ms-transition: all .6s ease; -o-transition: all .6s ease; transition: all .6s ease; } .edit-animate-delay-veryfast { -webkit-transition-delay: .05s; + -moz-transition-delay: .05s; + -ms-transition-delay: .05s; + -o-transition-delay: .05s; + transition-delay: .05s; } .edit-animate-delay-fast { -webkit-transition-delay: .2s; -} - -.edit-animate-delay-default { - -webkit-transition-delay: .4s; -} - -.edit-animate-delay-slow { - -webkit-transition-delay: .6s; + -moz-transition-delay: .2s; + -ms-transition-delay: .2s; + -o-transition-delay: .2s; + transition-delay: .2s; } .edit-animate-disable-width { -webkit-transition: width 0s; + -moz-transition: width 0s; + -ms-transition: width 0s; + -o-transition: width 0s; + transition: width 0s; } .edit-animate-only-visibility { -webkit-transition: opacity .2s ease; -moz-transition: opacity .2s ease; - -ie-transition: opacity .2s ease; + -ms-transition: opacity .2s ease; -o-transition: opacity .2s ease; transition: opacity .2s ease; - -webkit-transition-delay: 0s; } /** - * Edit's bar — inspired by core's toolbar.module & shortcut.module. + * Toolbar. */ -#editbar, -#editbar * { - border: 0; - font-size: 100%; - line-height: inherit; - list-style: none; - margin: 0; - outline: 0; - padding: 0; - text-align: left; /* LTR */ - vertical-align: baseline; +.icon-edit:before { + background-image: url("../images/icon-edit.png"); } -#editbar { - position: relative; - background: #666; - color: #ccc; - font: normal small "Lucida Grande", Verdana, sans-serif; - margin: 0 -20px; - padding: 0 20px; - -moz-box-shadow: 0 3px 20px #000; - -webkit-box-shadow: 0 3px 20px #000; - box-shadow: 0 3px 20px #000; - z-index: 500; -} -#editbar ul { - padding: 5px 0 2px 0; - height: 28px; - line-height: 24px; - margin-left:5px; /* LTR */ -} -#editbar ul li, -#editbar ul li a { - float: left; /* LTR */ - padding: 0 5px 0 5px; - margin-right: 5px; /* LTR */ - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; +.icon-edit:active:before, +.active .icon-edit:before { + background-image: url("../images/icon-edit-active.png"); } -#editbar a { - padding: 5px 10px 5px 5px; - line-height: 24px; - color: #fefefe; - font-size: .846em; - text-decoration: none; -} -#editbar a:focus, -#editbar a:hover, -#editbar a.active { - color: #fff; -} -#editbar ul li a:focus, -#editbar ul li a:hover, -#editbar ul li a.active:focus { - background: #555; -} -#editbar ul li a.active:hover, -#editbar ul li a.active { - background: #000; +.toolbar .tray.edit.active { + z-index: 340; } @@ -140,9 +93,10 @@ z-index: 250; width: 100%; height: 100%; + background-color: #fff; background-color: rgba(255,255,255,.5); - top: 0px; /* offset for navbar, modified later */ - left: 0px; + top: 0; + left: 0; } /* Editable. */ @@ -155,22 +109,22 @@ } .edit-field.edit-editable, .edit-field.edit-type-direct .edit-editable { - box-shadow: 0px 0px 1px 1px #4D9DE9; + box-shadow: 0 0 1px 1px #4D9DE9; } /* Highlighted (hovered) editable. */ .edit-editable.edit-highlighted { - min-width: 200px; /* TODO: we even need them to be at least fairly wide! */ + min-width: 200px; } .edit-field.edit-editable.edit-highlighted, .edit-form.edit-editable.edit-highlighted, .edit-field.edit-type-direct .edit-editable.edit-highlighted { - box-shadow: 0px 0px 1px 1px #0199FF, 0px 0px 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px #0199FF, 0 0 3px 3px rgba(153, 153, 153, .5); } .edit-field.edit-editable.edit-highlighted.edit-validation-error, .edit-form.edit-editable.edit-highlighted.edit-validation-error, .edit-field.edit-type-direct .edit-editable.edit-highlighted.edit-validation-error { - box-shadow: 0px 0px 1px 1px red, 0px 0px 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px red, 0 0 3px 3px rgba(153, 153, 153, .5); } .edit-form.edit-editable .form-item .error { border: 1px solid #eea0a0; @@ -181,7 +135,9 @@ .edit-form.edit-editable.edit-editing, .edit-field.edit-type-direct .edit-editable.edit-editing { /* In the latest design, there's no special styling when editing as opposed to - just hovering. */ + * just hovering. + * This will be necessary again for http://drupal.org/node/1844220. + */ } @@ -242,7 +198,7 @@ left: -5px; margin: 0; border: none; - box-shadow: 0px 0px 1px 1px red, 0px 0px 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px red, 0 0 3px 3px rgba(153, 153, 153, .5); background-color: white; } @@ -305,7 +261,7 @@ height: auto; position: absolute; bottom: 1px; - box-shadow: 0px 0px 1px 1px #0199FF, 0px 0px 3px 3px rgba(153, 153, 153, .5); + box-shadow: 0 0 1px 1px #0199FF, 0 0 3px 3px rgba(153, 153, 153, .5); background: #FFF; } @@ -355,7 +311,7 @@ .edit-toolgroup.wysiwyg { clear: left; width: 100%; - padding-left: none; + padding-left: 0; } @@ -402,14 +358,9 @@ float: left; } .edit-toolbar a span.close { - background: url('../images/close.png') no-repeat 2px 2px; + background: url('../images/close.png') no-repeat 3px 2px; } -.edit-toolbar a span.close:hover { - /* TODO: use a different "close" image */ -} - - .edit-toolbar a.blank-button { color: black; } @@ -418,6 +369,7 @@ .edit-toolbar a.blue-button { color: white; background-image: -webkit-linear-gradient(top, #6fc2f2 0%, #4e97c0 100%); + background-image: -moz-linear-gradient(top, #6fc2f2 0%, #4e97c0 100%); background-image: linear-gradient(top, #6fc2f2 0%, #4e97c0 100%); border-radius: 5px; } @@ -426,26 +378,17 @@ .edit-toolbar a.gray-button { color: #666; background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #ccc 100%); + background-image: -moz-linear-gradient(top, #f5f5f5 0%, #ccc 100%); background-image: linear-gradient(top, #f5f5f5 0%, #ccc 100%); border-radius: 5px; } -#edit_modal a img.gray-button.close img, .gray-button.save img, .blue-button.save img, -.edit-toolbar a img.gray-button.close img, .gray-button.save img, .blue-button.save img { - padding: 0; -} - -.gray-button img, .blue-button img, -.gray-button img, .blue-button img { - padding-right: 5px; -} - #edit_modal a.blue-button:hover, .edit-toolbar a.blue-button:hover, #edit_modal a.blue-button:active, .edit-toolbar a.blue-button:active { border: 1px solid #55a5d3; - box-shadow: 0px 2px 1px rgba(0,0,0,0.2); + box-shadow: 0 2px 1px rgba(0,0,0,0.2); } #edit_modal a.gray-button:hover, @@ -453,5 +396,5 @@ #edit_modal a.gray-button:active, .edit-toolbar a.gray-button:active { border: 1px solid #cdcdcd; - box-shadow: 0px 2px 1px rgba(0,0,0,0.1); + box-shadow: 0 2px 1px rgba(0,0,0,0.1); } diff --git a/edit.module b/edit.module index 0e11895..5df4a07 100644 --- a/edit.module +++ b/edit.module @@ -14,8 +14,19 @@ use Drupal\Core\Template\Attribute; use Drupal\field\FieldInstance; -// @todo Get rid of missing-api.inc by improving Entity API. -module_load_include('inc', 'edit', 'includes/missing-api'); +// @todo: POSTPONED_ON(Drupal core, http://drupal.org/node/1839516) +// Remove this when Entity Access API is ready. +// Edit needs the Entity Access API. Sadly, http://drupal.org/node/1696660 is +// just the framework, it still needs to be used; currently it will simply +// always return FALSE (i.e. "no access"). +function edit_entity_access($op, $entity_type, $entity = NULL, $account = NULL) { + if ($entity_type == 'node') { + return node_access($op, $entity, $account); + } + + // Currently, only editing of nodes is supported. See the above issue. + return FALSE; +} /** * Implements hook_menu() @@ -49,46 +60,68 @@ function edit_menu() { } /** - * Implements hook_page_alter(). + * Implements hook_toolbar(). */ -function edit_page_alter(&$page) { +function edit_toolbar() { if (path_is_admin(current_path())) { return; } - $page['page_top']['edit'] = array( - 'view_edit_toggle' => array( - '#prefix' => '

' . t('In-place edit operations') . '

', - 'content' => array( - array( - '#theme' => 'menu_local_task', - '#link' => array('title' => t('View'), 'href' => current_path(), 'localized_options' => array('fragment' => 'view', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-view')))), - '#active' => TRUE, + $tab['edit'] = array( + 'tab' => array( + 'title' => t('Edit'), + 'href' => '', + 'html' => FALSE, + 'attributes' => array( + 'class' => array('icon', 'icon-edit'), + ), + // @todo: BLOCKED_ON(Drupal core, http://drupal.org/node/1847198) + // This #post_render callback does not yet get called, which means that + // even if zero fields on the current page are editable for the current + // user, the "Edit" tab will still show up. + '#post_render' => array( + 'edit_toolbar_post_render', + ), + ), + 'tray' => array( + 'view_edit_toggle' => array( + '#prefix' => '

' . t('In-place edit operations') . '

', + 'content' => array( + array( + '#theme' => 'menu_local_task', + '#link' => array('title' => t('View'), 'href' => current_path(), 'localized_options' => array('fragment' => 'view', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-view')))), + '#active' => TRUE, + ), + array( + '#theme' => 'menu_local_task', + '#link' => array('title' => t('Quick edit'), 'href' => current_path(), 'localized_options' => array('fragment' => 'quick-edit', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-edit')))), + ), ), - array( - '#theme' => 'menu_local_task', - '#link' => array('title' => t('Quick edit'), 'href' => current_path(), 'localized_options' => array('fragment' => 'quick-edit', 'attributes' => array('class' => array('edit_view-edit-toggle', 'edit-edit')))), + '#attached' => array( + 'library' => array( + array('edit', 'edit'), + ), ), ), - '#attached' => array( - 'library' => array( - array('edit', 'edit'), - ), + '#post_render' => array( + 'edit_toolbar_post_render', ), ), - '#post_render' => array( - 'edit_editbar_post_render', - ), ); + + return $tab; } /** - * Post-render function to remove the editbar if nothing editable is present. + * Post-render function to only show the Edit tab in the toolbar if relevant. + * + * When the user cannot edit any of the fields on the page, he should not be + * given the possibility to toggle edit mode. */ -function edit_editbar_post_render($html) { - global $editbar; - return ($editbar !== TRUE) ? '' : $html; +function edit_toolbar_post_render($html) { + global $edit_toolbar; + return ($edit_toolbar !== TRUE) ? '' : $html; } /** @@ -101,15 +134,32 @@ function edit_library_info() { 'website' => 'http://drupal.org/project/edit', 'version' => VERSION, 'js' => array( - $path . '/js/edit.js' => array( - 'defer' => TRUE, - ), - $path . '/js/util.js' => array( - 'defer' => TRUE, - ), - $path . '/js/theme.js' => array( - 'defer' => TRUE, - ), + // Core. + $path . '/js/edit.js' => array('defer' => TRUE), + $path . '/js/app.js' => array('defer' => TRUE), + // Routers. + $path . '/js/routers/edit-router.js' => array('defer' => TRUE), + // Models. + $path . '/js/models/edit-app-model.js' => array('defer' => TRUE), + // Views. + $path . '/js/views/propertyeditordecoration-view.js' => array('defer' => TRUE), + $path . '/js/views/menu-view.js' => array('defer' => TRUE), + $path . '/js/views/modal-view.js' => array('defer' => TRUE), + $path . '/js/views/overlay-view.js' => array('defer' => TRUE), + $path . '/js/views/toolbar-view.js' => array('defer' => TRUE), + // Backbone.sync implementation on top of Drupal forms. + $path . '/js/backbone.drupalform.js' => array('defer' => TRUE), + // VIE service. + $path . '/js/viejs/EditService.js' => array('defer' => TRUE), + // Create.js subclasses. + $path . '/js/createjs/editable.js' => array('defer' => TRUE), + $path . '/js/createjs/storage.js' => array('defer' => TRUE), + $path . '/js/createjs/editingWidgets/formwidget.js' => array('defer' => TRUE), + $path . '/js/createjs/editingWidgets/drupalcontenteditablewidget.js' => array('defer' => TRUE), + $path . '/js/createjs/editingWidgets/drupalalohawidget.js' => array('defer' => TRUE), + // Other. + $path . '/js/util.js' => array('defer' => TRUE), + $path . '/js/theme.js' => array('defer' => TRUE), // Basic settings. array( 'data' => array('edit' => array( @@ -124,52 +174,14 @@ function edit_library_info() { $path . '/css/edit.css', ), 'dependencies' => array( + array('system', 'jquery'), + array('system', 'underscore'), + array('system', 'backbone'), + array('system', 'vie.core'), + array('system', 'create.editonly'), array('system', 'jquery.form'), array('system', 'drupal.form'), array('system', 'drupal.ajax'), - array('system', 'backbone'), - array('edit', 'edit.createjs'), - ), - ); - - $libraries['edit.createjs'] = array( - 'title' => 'CreateJS and deps', - 'website' => 'http://createjs.org', - 'version' => NULL, - 'js' => array( - $path . '/js/lib/create.js' => array('defer' => TRUE), - $path . '/js/viejs/SparkEditService.js' => array('defer' => TRUE), - $path . '/js/createjs/editable.js' => array('defer' => TRUE), - $path . '/js/createjs/storage.js' => array('defer' => TRUE), - $path . '/js/createjs/editingWidgets/formwidget.js' => array('defer' => TRUE), - $path . '/js/createjs/editingWidgets/drupalcontenteditablewidget.js' => array('defer' => TRUE), - $path . '/js/createjs/editingWidgets/drupalalohawidget.js' => array('defer' => TRUE), - $path . '/js/views/menu-view.js' => array('defer' => TRUE), - $path . '/js/views/overlay-view.js' => array('defer' => TRUE), - $path . '/js/views/toolbar-view.js' => array('defer' => TRUE), - $path . '/js/views/fielddecorator-view.js' => array('defer' => TRUE), - $path . '/js/views/modal-view.js' => array('defer' => TRUE), - $path . '/js/routers/edit-router.js' => array('defer' => TRUE), - $path . '/js/models/edit-app-model.js' => array('defer' => TRUE), - $path . '/js/backbone.drupalform.js' => array('defer' => TRUE), - $path . '/js/app.js' => array('defer' => TRUE), - ), - 'dependencies' => array( - array('system', 'jquery.ui.widget'), - array('system', 'backbone'), - array('edit', 'edit.vie'), - ), - ); - - $libraries['edit.vie'] = array( - 'title' => 'Vienna IKS Editables', - 'website' => 'http://wiki.iks-project.eu/index.php/VIE', - 'version' => '2.0', - 'js' => array( - $path . '/js/lib/vie.js' => array('defer' => TRUE), - ), - 'dependencies' => array( - array('system', 'backbone'), ), ); @@ -215,8 +227,8 @@ function edit_preprocess_field(&$variables) { $field_access = field_access('edit', $field_name, $entity->entityType(), $entity); $editability = _edit_analyze_field_editability($items, $instance, $formatter_type); if ($entity_access && $field_access && $editability != 'disabled') { - global $editbar; - $editbar = TRUE; + global $edit_toolbar; + $edit_toolbar = TRUE; // Mark this field as editable and provide metadata through data- attributes. $variables['attributes']['data-edit-field-label'] = $instance->definition['label']; diff --git a/generate-core-patch.sh b/generate-core-patch.sh index 67a64ea..8dd4d13 100644 --- a/generate-core-patch.sh +++ b/generate-core-patch.sh @@ -19,12 +19,19 @@ mkdir $DRUPAL_DIR/core/modules/edit # Generate the patch for the Edit module. cp -R edit.info edit.module css images includes js $DRUPAL_DIR/core/modules/edit/ -rm $DRUPAL_DIR/core/modules/edit/includes/text.inc +rm -rf $DRUPAL_DIR/core/modules/edit/js/build cp text.patch $DRUPAL_DIR/ +cp vie-and-create.patch $DRUPAL_DIR/ cd $DRUPAL_DIR git apply text.patch +rm text.patch +git apply vie-and-create.patch +rm vie-and-create.patch git add core/modules/edit git add core/modules/field/modules/text/lib/Drupal/text/Plugin/field/formatter +git add core/modules/system +git add core/misc/create +git add core/misc/vie git diff --staged --binary --patch-with-stat > $EDIT_DIR/$FILENAME-$COMMENTNR.patch git commit -m "edit module" cd $EDIT_DIR diff --git a/grunt.js b/grunt.js index 1c94414..f160df9 100644 --- a/grunt.js +++ b/grunt.js @@ -1,5 +1,4 @@ module.exports = function(grunt) { - // Project configuration. grunt.initConfig({ lint: { all: [ @@ -13,7 +12,6 @@ module.exports = function(grunt) { }, concat: { dist: { - // @todo: do we want to concat VIE and createjs, too? src: [ 'js/createjs/*.js', 'js/viejs/*.js', @@ -22,20 +20,7 @@ module.exports = function(grunt) { 'js/routers/*.js', 'js/*.js' ], - // @todo: shouldn't we keep source and build/ more separate? dest: 'js/build/edit.js' - }, - 'dist-all': { - src: [ - 'js/lib/*', - 'js/createjs/*.js', - 'js/viejs/*.js', - 'js/models/*.js', - 'js/views/*.js', - 'js/routers/*.js', - 'js/*.js' - ], - dest: 'js/build/edit-all.js' } }, watch: { @@ -46,11 +31,7 @@ module.exports = function(grunt) { dist: { src: ['js/build/edit.js'], dest: 'js/build/edit.min.js' - }, - 'dist-all':{ - src: ['js/build/edit-all.js'], - dest: 'js/build/edit-all.min.js' - }, + } }, jshint: { options: { @@ -67,13 +48,21 @@ module.exports = function(grunt) { VIE: true, _: true } + }, + csslint: { + all: { + src: 'css/edit.css', + rules: { + 'adjoining-classes': false, + 'ids': false, + 'outline-none': false, + 'box-model': false, + 'overqualified-elements': false + } + } } }); - // Load local tasks; we should add local tasks later. - // grunt.loadTasks("tasks"); - - // Set default - grunt.registerTask('default', 'lint concat min'); - + grunt.loadNpmTasks('grunt-css'); + grunt.registerTask('default', 'lint concat min csslint'); }; diff --git a/images/attention.png b/images/attention.png index 3c833c9..6a35d1d 100644 Binary files a/images/attention.png and b/images/attention.png differ diff --git a/images/close.png b/images/close.png index 2f5d665..e3f98b8 100644 Binary files a/images/close.png and b/images/close.png differ diff --git a/images/icon-edit-active.png b/images/icon-edit-active.png new file mode 100644 index 0000000..ad84761 Binary files /dev/null and b/images/icon-edit-active.png differ diff --git a/images/icon-edit.png b/images/icon-edit.png new file mode 100644 index 0000000..4f0dcc2 Binary files /dev/null and b/images/icon-edit.png differ diff --git a/images/throbber.gif b/images/throbber.gif index 58f4a42..f2603e8 100644 Binary files a/images/throbber.gif and b/images/throbber.gif differ diff --git a/includes/form.inc b/includes/form.inc index 6936a2f..cdf9935 100644 --- a/includes/form.inc +++ b/includes/form.inc @@ -2,15 +2,16 @@ /** * @file - * Form callbacks for the Edit module. + * Form handlers for the Edit module. */ /** - * Field editing form. (For editing a field instance.) + * Form constructor for the field editing form. * - * Proudly found elsewhere: FAPE module's fape_field_edit_form() and friends. + * @see edit_field_form_validate() + * @ingroup forms */ -function edit_field_form($form, &$form_state) { +function edit_field_form(array $form, array &$form_state) { $entity = $form_state['entity']; $langcode = $form_state['langcode']; @@ -19,40 +20,6 @@ function edit_field_form($form, &$form_state) { field_attach_form($entity->entityType(), $entity, $form, $form_state, $langcode, $options); $form['#validate'][] = 'edit_field_form_validate'; - // @todo Verify that this is indeed not necessary anymore, see edit_field_form_validate(). - // $form['#submit'][] = ''; - - // Add revisions form items if necessary. - // @todo We may be able to get rid of this when http://drupal.org/node/1678002 is solved. - list($use_revisions, $control_revisions) = edit_entity_allows_revisions($entity->entityType(), $entity->bundle(), $entity); - if ($use_revisions) { - $form_state['use revisions'] = TRUE; - $form['revision_information'] = array( - '#weight' => 11, - ); - - $form['revision_information']['revision'] = array( - '#type' => 'checkbox', - '#title' => t('Create new revision'), - '#default_value' => $entity->revision, - '#id' => 'edit-revision', - '#access' => $control_revisions, - ); - - if ($control_revisions || $entity->revision) { - $form['revision_information']['log'] = array( - '#type' => 'textarea', - '#title' => t('Log message'), - '#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'), - '#default_value' => $entity->log, - ); - - if ($control_revisions) { - $form['revision_information']['log']['#dependency'] = array('edit-revision' => array(1)); - } - } - $form['#submit'][] = 'edit_field_form_revision_submit'; - } $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( @@ -67,9 +34,12 @@ function edit_field_form($form, &$form_state) { } /** - * Helper function to simplify the field edit form for in-place editing. + * Simplifies the field edit form for in-place editing. + * + * @param array $form + * An associative array containing the structure of the form. */ -function _simplify_edit_field_edit_form(&$form) { +function _simplify_edit_field_edit_form(array &$form) { $elements = element_children($form); // Required internal form properties. @@ -116,9 +86,12 @@ function _simplify_edit_field_edit_form(&$form) { /** * Validate field editing form. * - * @todo: clean up once http://drupal.org/node/1846648 is solved. + * @see edit_field_form() + * + * @todo: BLOCKED_ON(Drupal core, http://drupal.org/node/1846648) + * Clean up once that issue is solved. */ -function edit_field_form_validate($form, &$form_state) { +function edit_field_form_validate(array $form, array &$form_state) { $entity = $form_state['entity']; $options = array('field_name' => $form_state['field_name']); @@ -130,14 +103,3 @@ function edit_field_form_validate($form, &$form_state) { // Validation. field_attach_form_validate($entity->entityType(), $entity, $form, $form_state, $options); } - -/** - * Submit callback that handles entity revisioning. - */ -function edit_field_form_revision_submit($form, &$form_state) { - $entity = $form_state['entity']; - if (!empty($form_state['use revisions'])) { - $entity->revision = $form_state['values']['revision']; - $entity->log = $form_state['values']['log']; - } -} diff --git a/includes/missing-api.inc b/includes/missing-api.inc deleted file mode 100644 index 1b2f08a..0000000 --- a/includes/missing-api.inc +++ /dev/null @@ -1,43 +0,0 @@ -revision = $retval[0]; - $entity->log = ''; - return $retval; -} - -/** - * @} End of "ingroup Missing in Entity API.". - */ diff --git a/js/app.js b/js/app.js index b18675c..82d390f 100644 --- a/js/app.js +++ b/js/app.js @@ -28,7 +28,7 @@ // VIE instance for Edit. this.vie = new VIE(); // Use our custom DOM parsing service until RDFa is available. - this.vie.use(new this.vie.SparkEditService()); + this.vie.use(new this.vie.EditService()); this.domService = this.vie.service('edit'); // Instantiate configuration for state handling. @@ -83,9 +83,10 @@ * Should be called whenever EditAppModel's "isViewing" changes. */ appStateChange: function() { - // @todo: we're currently setting the state on EditableEntity widgets - // instead of PropertyEditor widgets, because of - // https://github.com/bergie/create/issues/140 + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133, https://github.com/bergie/create/issues/140) + // We're currently setting the state on EditableEntity widgets instead of + // PropertyEditor widgets, because of + // https://github.com/bergie/create/issues/133. var newState = (this.model.get('isViewing')) ? 'inactive' : 'candidate'; this.$entityElements.each(function() { $(this).createEditable('setState', newState); @@ -248,7 +249,8 @@ * The PropertyEditor widget object. */ editorStateChange: function(from, to, editor) { - // @todo get rid of this once https://github.com/bergie/create/issues/133 is solved. + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) + // Get rid of this once that issue is solved. if (!editor) { return; } @@ -273,7 +275,8 @@ } // Propagate the state change to the decoration and toolbar views. - // @todo enable this once https://github.com/bergie/create/issues/133 is solved. + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) + // Uncomment this once that issue is solved. // editor.decorationView.stateChange(from, to); // editor.toolbarView.stateChange(from, to); }, @@ -297,13 +300,14 @@ }); // Decorate the editor's DOM element depending on its state. - editor.decorationView = new Drupal.edit.views.FieldDecorationView({ + editor.decorationView = new Drupal.edit.views.PropertyEditorDecorationView({ el: editor.element, editor: editor, toolbarId: editor.toolbarView.getId() }); - // @todo get rid of this once https://github.com/bergie/create/issues/133 is solved. + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) + // Get rid of this once that issue is solved. editor.options.widget.element.bind('createeditablestatechange', function(event, data) { editor.decorationView.stateChange(data.previous, data.current); editor.toolbarView.stateChange(data.previous, data.current); diff --git a/js/backbone.drupalform.js b/js/backbone.drupalform.js index 3285af7..aee82c1 100644 --- a/js/backbone.drupalform.js +++ b/js/backbone.drupalform.js @@ -27,8 +27,6 @@ Backbone.sync = function(method, model, options) { * form when there are validation errors and ensure no Drupal.ajax memory leaks. * * @see Drupal.edit.util.form - * - * @todo: HTTP status handling. */ Backbone.syncDrupalFormWidget = function(method, model, options) { if (method === 'update') { @@ -44,7 +42,10 @@ Backbone.syncDrupalFormWidget = function(method, model, options) { // Call Backbone.sync's success callback with the rerendered field. var changedAttributes = {}; - changedAttributes[predicate] = '@todo: JSON-LD representation N/A yet.'; + // @todo: POSTPONED_ON(Drupal core, http://drupal.org/node/1784216) + // Once full JSON-LD support in Drupal core lands, we can ensure that the + // models that VIE maintains are properly updated. + changedAttributes[predicate] = 'JSON-LD representation N/A yet.'; changedAttributes[predicate + '/rendered'] = response.data; options.success(changedAttributes); }; @@ -84,8 +85,6 @@ Backbone.syncDrupalFormWidget = function(method, model, options) { * * @see Backbone.syncDrupalFormWidget() * @see Drupal.edit.util.form - * - * @todo: HTTP status handling. */ Backbone.syncDirect = function(method, model, options) { if (method === 'update') { diff --git a/js/createjs/editable.js b/js/createjs/editable.js index cc34498..beabc2c 100644 --- a/js/createjs/editable.js +++ b/js/createjs/editable.js @@ -30,8 +30,8 @@ }, _propertyEditorName: function (data) { - if (Drupal.settings.edit.wysiwyg && jQuery(this.element).hasClass('edit-type-direct')) { - if (jQuery(this.element).hasClass('edit-type-direct-with-wysiwyg')) { + if (jQuery(this.element).hasClass('edit-type-direct')) { + if (Drupal.settings.edit.wysiwyg && jQuery(this.element).hasClass('edit-type-direct-with-wysiwyg')) { return 'direct-with-wysiwyg'; } return 'direct'; diff --git a/js/createjs/editingWidgets/drupalalohawidget.js b/js/createjs/editingWidgets/drupalalohawidget.js index c972911..6ef3a93 100644 --- a/js/createjs/editingWidgets/drupalalohawidget.js +++ b/js/createjs/editingWidgets/drupalalohawidget.js @@ -10,15 +10,16 @@ jQuery.widget('Drupal.drupalAlohaWidget', jQuery.Create.alohaWidget, { - // @todo: actually use this when restoring original content, but for that we + // @todo BLOCKED_ON(Create.js/VIE.js, how to restore original content when canceling editing) + // Actually use this when restoring original content, but for that we // first need to know how to restore content in a Create.js context originalTransformedContent: null, /** * Implements jQuery UI widget factory's _init() method. * - * @todo: get rid of this once https://github.com/bergie/create/issues/142 - * is solved. + * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142) + * Get rid of this once that issue is solved. */ _init: function() {}, @@ -37,8 +38,9 @@ /** * Binds to events. * - * @todo: get rid of this helper function and move it into _initialize() - * once https://github.com/alohaeditor/Aloha-Editor/issues/693 is solved. + * @todo: POSTPONED_ON(Aloha Editor, https://github.com/alohaeditor/Aloha-Editor/issues/693) + * Get rid of this helper function and move it into _initialize() once that + * issue is solved. Also see http://drupal.org/node/1725032. */ _bindEvents: function() { var that = this; @@ -62,9 +64,6 @@ /** * Makes this PropertyEditor widget react to state changes. - * - * @todo revisit this once https://github.com/bergie/create/issues/133 is - * solved. */ stateChange: function(from, to) { switch (to) { @@ -75,11 +74,6 @@ Drupal.aloha.detach(this.element); this._removeValidationErrors(); this._cleanUp(); - - // TRICKY: work-around for major AE bug. See: - // - http://drupal.org/node/1725032 - // - https://github.com/alohaeditor/Aloha-Editor/issues/693. - // @todo: get rid of this once https://github.com/alohaeditor/Aloha-Editor/issues/693 is solved. this._bindEvents(); } break; diff --git a/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/js/createjs/editingWidgets/drupalcontenteditablewidget.js index a9c9c46..65f5116 100644 --- a/js/createjs/editingWidgets/drupalcontenteditablewidget.js +++ b/js/createjs/editingWidgets/drupalcontenteditablewidget.js @@ -11,8 +11,8 @@ /** * Implements jQuery UI widget factory's _init() method. * - * @todo: get rid of this once https://github.com/bergie/create/issues/142 - * is solved. + * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142) + * Get rid of this once that issue is solved. */ _init: function() {}, @@ -45,9 +45,6 @@ /** * Makes this PropertyEditor widget react to state changes. - * - * @todo revisit this once https://github.com/bergie/create/issues/133 is - * solved. */ stateChange: function(from, to) { switch (to) { diff --git a/js/createjs/editingWidgets/formwidget.js b/js/createjs/editingWidgets/formwidget.js index 94ad3f5..889ee87 100644 --- a/js/createjs/editingWidgets/formwidget.js +++ b/js/createjs/editingWidgets/formwidget.js @@ -14,8 +14,8 @@ /** * Implements jQuery UI widget factory's _init() method. * - * @todo: get rid of this once https://github.com/bergie/create/issues/142 - * is solved. + * @todo: POSTPONED_ON(Create.js, https://github.com/bergie/create/issues/142) + * Get rid of this once that issue is solved. */ _init: function() {}, @@ -34,9 +34,6 @@ /** * Makes this PropertyEditor widget react to state changes. - * - * @todo revisit this once https://github.com/bergie/create/issues/133 is - * solved. */ stateChange: function(from, to) { switch (to) { @@ -87,7 +84,8 @@ // Insert form container in DOM. if ($editorElement.css('display') === 'inline') { - // @todo: this is untested in Drupal 8, because in Drupal 8 we don't yet + // @todo: POSTPONED_ON(Drupal core, title/author/date as Entity Properties) + // This is untested in Drupal 8, because in Drupal 8 we don't yet // have the ability to edit the node title/author/date, because they // haven't been converted into Entity Properties yet, and they're the // only examples in core of "display: inline" properties. diff --git a/js/edit.js b/js/edit.js index 22c976c..0c813fb 100644 --- a/js/edit.js +++ b/js/edit.js @@ -10,7 +10,9 @@ Drupal.edit = Drupal.edit || {}; Drupal.behaviors.editDiscoverEditables = { attach: function(context) { - // @todo: we need to separate the discovery of editables if we want updated + // @todo BLOCKED_ON(VIE.js, how to let VIE know that some content was removed and how to scan new content for VIE entities, to make them editable?) + // Also see ToolbarView.save(). + // We need to separate the discovery of editables if we want updated // or new content (added by code other than Edit) to be detected // automatically. Once we implement this, we'll be able to get rid of all // calls to Drupal.edit.domService.findSubjectElements() :) diff --git a/js/lib/create.js b/js/lib/create.js deleted file mode 100644 index 1d76bff..0000000 --- a/js/lib/create.js +++ /dev/null @@ -1,1643 +0,0 @@ -// Create.js - On-site web editing interface -// (c) 2011-2012 Henri Bergius, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false window:false console:false */ - 'use strict'; - - // # Widget for adding items to a collection - jQuery.widget('Midgard.midgardCollectionAdd', { - options: { - editingWidgets: null, - collection: null, - model: null, - definition: null, - view: null, - disabled: false, - vie: null, - editableOptions: null, - templates: { - button: '' - } - }, - - _create: function () { - this.addButtons = []; - var widget = this; - if (!widget.options.collection.localStorage) { - try { - widget.options.collection.url = widget.options.model.url(); - } catch (e) { - if (window.console) { - console.log(e); - } - } - } - - widget.options.collection.bind('add', function (model) { - model.primaryCollection = widget.options.collection; - widget.options.vie.entities.add(model); - model.collection = widget.options.collection; - }); - - // Re-check collection constraints - widget.options.collection.bind('add remove reset', widget.checkCollectionConstraints, widget); - - widget._bindCollectionView(widget.options.view); - }, - - _bindCollectionView: function (view) { - var widget = this; - view.bind('add', function (itemView) { - itemView.$el.effect('slide', function () { - widget._makeEditable(itemView); - }); - }); - }, - - _makeEditable: function (itemView) { - this.options.editableOptions.disabled = this.options.disabled; - this.options.editableOptions.model = itemView.model; - itemView.$el.midgardEditable(this.options.editableOptions); - }, - - _init: function () { - if (this.options.disabled) { - this.disable(); - return; - } - this.enable(); - }, - - hideButtons: function () { - _.each(this.addButtons, function (button) { - button.hide(); - }); - }, - - showButtons: function () { - _.each(this.addButtons, function (button) { - button.show(); - }); - }, - - checkCollectionConstraints: function () { - if (this.options.disabled) { - return; - } - - if (!this.options.view.canAdd()) { - this.hideButtons(); - return; - } - - if (!this.options.definition) { - // We have now information on the constraints applying to this collection - this.showButtons(); - return; - } - - if (!this.options.definition.max || this.options.definition.max === -1) { - // No maximum constraint - this.showButtons(); - return; - } - - if (this.options.collection.length < this.options.definition.max) { - this.showButtons(); - return; - } - // Collection is already full by its definition - this.hideButtons(); - }, - - enable: function () { - var widget = this; - - var addButton = jQuery(_.template(this.options.templates.button, { - icon: 'plus', - label: this.options.editableOptions.localize('Add', this.options.editableOptions.language) - })).button(); - addButton.addClass('midgard-create-add'); - addButton.click(function () { - widget.addItem(addButton); - }); - jQuery(widget.options.view.el).after(addButton); - - widget.addButtons.push(addButton); - widget.checkCollectionConstraints(); - }, - - disable: function () { - _.each(this.addButtons, function (button) { - button.remove(); - }); - this.addButtons = []; - }, - - _getTypeActions: function (options) { - var widget = this; - var actions = []; - _.each(this.options.definition.range, function (type) { - var nsType = widget.options.collection.vie.namespaces.uri(type); - if (!widget.options.view.canAdd(nsType)) { - return; - } - actions.push({ - name: type, - label: type, - cb: function () { - widget.options.collection.add({ - '@type': type - }, options); - }, - className: 'create-ui-btn' - }); - }); - return actions; - }, - - addItem: function (button, options) { - if (options === undefined) { - options = {}; - } - var addOptions = _.extend({}, options, { validate: false }); - - var itemData = {}; - if (this.options.definition && this.options.definition.range) { - if (this.options.definition.range.length === 1) { - // Items can be of single type, add that - itemData['@type'] = this.options.definition.range[0]; - } else { - // Ask user which type to add - jQuery('body').midgardNotifications('create', { - bindTo: button, - gravity: 'L', - body: this.options.editableOptions.localize('Choose type to add', this.options.editableOptions.language), - timeout: 0, - actions: this._getTypeActions(addOptions) - }); - return; - } - } else { - // Check the view templates for possible non-Thing type to use - var keys = _.keys(this.options.view.templates); - if (keys.length == 2) { - itemData['@type'] = keys[0]; - } - } - this.options.collection.add(itemData, addOptions); - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2011-2012 Henri Bergius, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false window:false console:false */ - 'use strict'; - - // # Widget for adding items anywhere inside a collection - jQuery.widget('Midgard.midgardCollectionAddBetween', jQuery.Midgard.midgardCollectionAdd, { - _bindCollectionView: function (view) { - var widget = this; - view.bind('add', function (itemView) { - //itemView.el.effect('slide'); - widget._makeEditable(itemView); - widget._refreshButtons(); - }); - view.bind('remove', function () { - widget._refreshButtons(); - }); - }, - - _refreshButtons: function () { - var widget = this; - window.setTimeout(function () { - widget.disable(); - widget.enable(); - }, 1); - }, - - prepareButton: function (index) { - var widget = this; - var addButton = jQuery(_.template(this.options.templates.button, { - icon: 'plus', - label: '' - })).button(); - addButton.addClass('midgard-create-add'); - addButton.click(function () { - widget.addItem(addButton, { - at: index - }); - }); - return addButton; - }, - - enable: function () { - var widget = this; - - var firstAddButton = widget.prepareButton(0); - jQuery(widget.options.view.el).prepend(firstAddButton); - widget.addButtons.push(firstAddButton); - jQuery.each(widget.options.view.entityViews, function (cid, view) { - var index = widget.options.collection.indexOf(view.model); - var addButton = widget.prepareButton(index + 1); - jQuery(view.el).append(addButton); - widget.addButtons.push(addButton); - }); - - this.checkCollectionConstraints(); - }, - - disable: function () { - var widget = this; - jQuery.each(widget.addButtons, function (idx, button) { - button.remove(); - }); - widget.addButtons = []; - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2011-2012 Henri Bergius, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false window:false VIE:false */ - 'use strict'; - - // Define Create's EditableEntity widget. - jQuery.widget('Midgard.midgardEditable', { - options: { - propertyEditors: {}, - collections: [], - model: null, - // the configuration (mapping and options) of property editor widgets - propertyEditorWidgetsConfiguration: { - hallo: { - widget: 'halloWidget', - options: {} - } - }, - // the available property editor widgets by data type - propertyEditorWidgets: { - 'default': 'hallo' - }, - collectionWidgets: { - 'default': 'midgardCollectionAdd' - }, - toolbarState: 'full', - vie: null, - domService: 'rdfa', - predicateSelector: '[property]', - disabled: false, - localize: function (id, language) { - return window.midgardCreate.localize(id, language); - }, - language: null, - // Current state of the Editable - state: null, - // Callback function for validating changes between states. Receives the previous state, new state, possibly property, and a callback - acceptStateChange: true, - // Callback function for listening (and reacting) to state changes. - stateChange: null, - // Callback function for decorating the full editable. Will be called on instantiation - decorateEditableEntity: null, - // Callback function for decorating a single property editor widget. Will - // be called on editing widget instantiation. - decoratePropertyEditor: null, - - // Deprecated. - editables: [], // Now `propertyEditors`. - editors: {}, // Now `propertyEditorWidgetsConfiguration`. - widgets: {} // Now `propertyEditorW - }, - - // Aids in consistently passing parameters to events and callbacks. - _params: function(predicate, extended) { - var entityParams = { - entity: this.options.model, - editableEntity: this, - entityElement: this.element, - - // Deprecated. - editable: this, - element: this.element, - instance: this.options.model - }; - - var propertyParams = (predicate) ? { - predicate: predicate, - propertyEditor: this.options.propertyEditors[predicate], - propertyElement: this.options.propertyEditors[predicate].element, - - // Deprecated. - property: predicate, - element: this.options.propertyEditors[predicate].element - } : {}; - - return _.extend(entityParams, propertyParams, extended); - }, - - _create: function () { - // Backwards compatibility: - // - this.options.propertyEditorWidgets used to be this.options.widgets - // - this.options.propertyEditorWidgetsConfiguration used to be - // this.options.editors - if (this.options.widgets) { - this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); - } - if (this.options.editors) { - this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); - } - - this.vie = this.options.vie; - this.domService = this.vie.service(this.options.domService); - if (!this.options.model) { - var widget = this; - this.vie.load({ - element: this.element - }).from(this.options.domService).execute().done(function (entities) { - widget.options.model = entities[0]; - }); - } - if (_.isFunction(this.options.decorateEditableEntity)) { - this.options.decorateEditableEntity(this._params()); - } - }, - - _init: function () { - // Backwards compatibility: - // - this.options.propertyEditorWidgets used to be this.options.widgets - // - this.options.propertyEditorWidgetsConfiguration used to be - // this.options.editors - if (this.options.widgets) { - this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); - } - if (this.options.editors) { - this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); - } - - // Old way of setting the widget inactive - if (this.options.disabled === true) { - this.setState('inactive'); - return; - } - - if (this.options.disabled === false && this.options.state === 'inactive') { - this.setState('candidate'); - return; - } - this.options.disabled = false; - - if (this.options.state) { - this.setState(this.options.state); - return; - } - this.setState('candidate'); - }, - - // Method used for cycling between the different states of the Editable widget: - // - // * Inactive: editable is loaded but disabled - // * Candidate: editable is enabled but not activated - // * Highlight: user is hovering over the editable (not set by Editable widget directly) - // * Activating: an editor widget is being activated for user to edit with it (skipped for editors that activate instantly) - // * Active: user is actually editing something inside the editable - // * Changed: user has made changes to the editable - // * Invalid: the contents of the editable have validation errors - // - // In situations where state changes are triggered for a particular property editor, the `predicate` - // argument will provide the name of that property. - // - // State changes may carry optional context information in a JavaScript object. The payload of these context objects is not - // standardized, and is meant to be set and used by the application controller - // - // The callback parameter is optional and will be invoked after a state change has been accepted (after the 'statechange' - // event) or rejected. - setState: function (state, predicate, context, callback) { - var previous = this.options.state; - var current = state; - if (current === previous) { - return; - } - - if (this.options.acceptStateChange === undefined || !_.isFunction(this.options.acceptStateChange)) { - // Skip state transition validation - this._doSetState(previous, current, predicate, context); - if (_.isFunction(callback)) { - callback(true); - } - return; - } - - var widget = this; - this.options.acceptStateChange(previous, current, predicate, context, function (accepted) { - if (accepted) { - widget._doSetState(previous, current, predicate, context); - } - if (_.isFunction(callback)) { - callback(accepted); - } - return; - }); - }, - - getState: function () { - return this.options.state; - }, - - _doSetState: function (previous, current, predicate, context) { - this.options.state = current; - if (current === 'inactive') { - this.disable(); - } else if ((previous === null || previous === 'inactive') && current !== 'inactive') { - this.enable(); - } - - this._trigger('statechange', null, this._params(predicate, { - previous: previous, - current: current, - context: context - })); - }, - - findEditablePredicateElements: function (callback) { - this.domService.findPredicateElements(this.options.model.id, jQuery(this.options.predicateSelector, this.element), false).each(callback); - }, - - getElementPredicate: function (element) { - return this.domService.getElementPredicate(element); - }, - - enable: function () { - var editableEntity = this; - if (!this.options.model) { - return; - } - - this.findEditablePredicateElements(function () { - editableEntity._enablePropertyEditor(jQuery(this)); - }); - - this._trigger('enable', null, this._params()); - - _.each(this.domService.views, function (view) { - if (view instanceof this.vie.view.Collection && this.options.model === view.owner) { - var predicate = view.collection.predicate; - var editableOptions = _.clone(this.options); - editableOptions.state = null; - var collection = this.enableCollection({ - model: this.options.model, - collection: view.collection, - property: predicate, - definition: this.getAttributeDefinition(predicate), - view: view, - element: view.el, - vie: editableEntity.vie, - editableOptions: editableOptions - }); - editableEntity.options.collections.push(collection); - } - }, this); - }, - - disable: function () { - _.each(this.options.propertyEditors, function (editable) { - this.disableEditor({ - widget: this, - editable: editable, - entity: this.options.model, - element: jQuery(editable) - }); - }, this); - this.options.propertyEditors = {}; - - // Deprecated. - this.options.editables = []; - - _.each(this.options.collections, function (collectionWidget) { - var editableOptions = _.clone(this.options); - editableOptions.state = 'inactive'; - this.disableCollection({ - widget: this, - model: this.options.model, - element: collectionWidget, - vie: this.vie, - editableOptions: editableOptions - }); - }, this); - this.options.collections = []; - - this._trigger('disable', null, this._params()); - }, - - _enablePropertyEditor: function (element) { - var widget = this; - var predicate = this.getElementPredicate(element); - if (!predicate) { - return true; - } - if (this.options.model.get(predicate) instanceof Array) { - // For now we don't deal with multivalued properties in the editable - return true; - } - - var propertyElement = this.enablePropertyEditor({ - widget: this, - element: element, - entity: this.options.model, - property: predicate, - vie: this.vie, - decorate: this.options.decoratePropertyEditor, - decorateParams: _.bind(this._params, this), - changed: function (content) { - widget.setState('changed', predicate); - - var changedProperties = {}; - changedProperties[predicate] = content; - widget.options.model.set(changedProperties, { - silent: true - }); - - widget._trigger('changed', null, widget._params(predicate)); - }, - activating: function () { - widget.setState('activating', predicate); - }, - activated: function () { - widget.setState('active', predicate); - widget._trigger('activated', null, widget._params(predicate)); - }, - deactivated: function () { - widget.setState('candidate', predicate); - widget._trigger('deactivated', null, widget._params(predicate)); - } - }); - - if (!propertyElement) { - return; - } - var widgetType = propertyElement.data('createWidgetName'); - this.options.propertyEditors[predicate] = propertyElement.data(widgetType); - - // Deprecated. - this.options.editables.push(propertyElement); - - this._trigger('enableproperty', null, this._params(predicate)); - }, - - // returns the name of the property editor widget to use for the given property - _propertyEditorName: function (data) { - if (this.options.propertyEditorWidgets[data.property] !== undefined) { - // Property editor widget configuration set for specific RDF predicate - return this.options.propertyEditorWidgets[data.property]; - } - - // Load the property editor widget configuration for the data type - var propertyType = 'default'; - var attributeDefinition = this.getAttributeDefinition(data.property); - if (attributeDefinition) { - propertyType = attributeDefinition.range[0]; - } - if (this.options.propertyEditorWidgets[propertyType] !== undefined) { - return this.options.propertyEditorWidgets[propertyType]; - } - return this.options.propertyEditorWidgets['default']; - }, - - _propertyEditorWidget: function (editor) { - return this.options.propertyEditorWidgetsConfiguration[editor].widget; - }, - - _propertyEditorOptions: function (editor) { - return this.options.propertyEditorWidgetsConfiguration[editor].options; - }, - - getAttributeDefinition: function (property) { - var type = this.options.model.get('@type'); - if (!type) { - return; - } - if (!type.attributes) { - return; - } - return type.attributes.get(property); - }, - - // Deprecated. - enableEditor: function (data) { - return this.enablePropertyEditor(data); - }, - - enablePropertyEditor: function (data) { - var editorName = this._propertyEditorName(data); - if (editorName === null) { - return; - } - - var editorWidget = this._propertyEditorWidget(editorName); - - data.editorOptions = this._propertyEditorOptions(editorName); - data.toolbarState = this.options.toolbarState; - data.disabled = false; - // Pass metadata that could be useful for some implementations. - data.editorName = editorName; - data.editorWidget = editorWidget; - - if (typeof jQuery(data.element)[editorWidget] !== 'function') { - throw new Error(editorWidget + ' widget is not available'); - } - - jQuery(data.element)[editorWidget](data); - jQuery(data.element).data('createWidgetName', editorWidget); - return jQuery(data.element); - }, - - // Deprecated. - disableEditor: function (data) { - return this.disablePropertyEditor(data); - }, - - disablePropertyEditor: function (data) { - var widgetName = jQuery(data.element).data('createWidgetName'); - - data.disabled = true; - - if (widgetName) { - // only if there has been an editing widget registered - jQuery(data.element)[widgetName](data); - jQuery(data.element).removeClass('ui-state-disabled'); - - if (data.element.is(':focus')) { - data.element.blur(); - } - } - }, - - collectionWidgetName: function (data) { - if (this.options.collectionWidgets[data.property] !== undefined) { - // Widget configuration set for specific RDF predicate - return this.options.collectionWidgets[data.property]; - } - - var propertyType = 'default'; - var attributeDefinition = this.getAttributeDefinition(data.property); - if (attributeDefinition) { - propertyType = attributeDefinition.range[0]; - } - if (this.options.collectionWidgets[propertyType] !== undefined) { - return this.options.collectionWidgets[propertyType]; - } - return this.options.collectionWidgets['default']; - }, - - enableCollection: function (data) { - var widgetName = this.collectionWidgetName(data); - if (widgetName === null) { - return; - } - data.disabled = false; - if (typeof jQuery(data.element)[widgetName] !== 'function') { - throw new Error(widgetName + ' widget is not available'); - } - jQuery(data.element)[widgetName](data); - jQuery(data.element).data('createCollectionWidgetName', widgetName); - return jQuery(data.element); - }, - - disableCollection: function (data) { - var widgetName = jQuery(data.element).data('createCollectionWidgetName'); - if (widgetName === null) { - return; - } - data.disabled = true; - if (widgetName) { - // only if there has been an editing widget registered - jQuery(data.element)[widgetName](data); - jQuery(data.element).removeClass('ui-state-disabled'); - } - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2012 Tobias Herrmann, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false document:false */ - 'use strict'; - - // # Base property editor widget - // - // This property editor widget provides a very simplistic `contentEditable` - // property editor that can be used as standalone, but should more usually be - // used as the base class for other property editor widgets. - // This property editor widget is only useful for textual properties! - // - // Subclassing this base property editor widget is easy: - // - // jQuery.widget('Namespace.MyWidget', jQuery.Create.editWidget, { - // // override any properties - // }); - jQuery.widget('Create.editWidget', { - options: { - disabled: false, - vie: null - }, - // override to enable the widget - enable: function () { - this.element.attr('contenteditable', 'true'); - }, - // override to disable the widget - disable: function (disable) { - this.element.attr('contenteditable', 'false'); - }, - // called by the jQuery UI plugin factory when creating the property editor - // widget instance - _create: function () { - this._registerWidget(); - this._initialize(); - - if (_.isFunction(this.options.decorate) && _.isFunction(this.options.decorateParams)) { - // TRICKY: we can't use this.options.decorateParams()'s 'propertyName' - // parameter just yet, because it will only be available after this - // object has been created, but we're currently in the constructor! - // Hence we have to duplicate part of its logic here. - this.options.decorate(this.options.decorateParams(null, { - propertyName: this.options.property, - propertyEditor: this, - propertyElement: this.element, - // Deprecated. - editor: this, - predicate: this.options.property, - element: this.element - })); - } - }, - // called every time the property editor widget is called - _init: function () { - if (this.options.disabled) { - this.disable(); - return; - } - this.enable(); - }, - // override this function to initialize the property editor widget functions - _initialize: function () { - var self = this; - this.element.bind('focus', function () { - if (self.options.disabled) { - return; - } - self.options.activated(); - }); - this.element.bind('blur', function () { - if (self.options.disabled) { - return; - } - self.options.deactivated(); - }); - var before = this.element.html(); - this.element.bind('keyup paste', function (event) { - if (self.options.disabled) { - return; - } - var current = jQuery(this).html(); - if (before !== current) { - before = current; - self.options.changed(current); - } - }); - }, - // used to register the property editor widget name with the DOM element - _registerWidget: function () { - this.element.data("createWidgetName", this.widgetName); - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2012 Tobias Herrmann, IKS Consortium -// (c) 2011 Rene Kapusta, Evo42 -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false document:false Aloha:false */ - 'use strict'; - - // # Aloha editing widget - // - // This widget allows editing textual contents using the - // [Aloha](http://aloha-editor.org) rich text editor. - // - // Due to licensing incompatibilities, Aloha Editor needs to be installed - // and configured separately. - jQuery.widget('Create.alohaWidget', jQuery.Create.editWidget, { - _initialize: function () {}, - enable: function () { - var options = this.options; - var editable; - var currentElement = Aloha.jQuery(options.element.get(0)).aloha(); - _.each(Aloha.editables, function (aloha) { - // Find the actual editable instance so we can hook to the events - // correctly - if (aloha.obj.get(0) === currentElement.get(0)) { - editable = aloha; - } - }); - if (!editable) { - return; - } - editable.vieEntity = options.entity; - - // Subscribe to activation and deactivation events - Aloha.bind('aloha-editable-activated', function (event, data) { - if (data.editable !== editable) { - return; - } - options.activated(); - }); - Aloha.bind('aloha-editable-deactivated', function (event, data) { - if (data.editable !== editable) { - return; - } - options.deactivated(); - }); - - Aloha.bind('aloha-smart-content-changed', function (event, data) { - if (data.editable !== editable) { - return; - } - if (!data.editable.isModified()) { - return true; - } - options.changed(data.editable.getContents()); - data.editable.setUnmodified(); - }); - this.options.disabled = false; - }, - disable: function () { - Aloha.jQuery(this.options.element.get(0)).mahalo(); - this.options.disabled = true; - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2012 Tobias Herrmann, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false document:false CKEDITOR:false */ - 'use strict'; - - // # CKEditor editing widget - // - // This widget allows editing textual content areas with the - // [CKEditor](http://ckeditor.com/) rich text editor. - jQuery.widget('Create.ckeditorWidget', jQuery.Create.editWidget, { - enable: function () { - this.element.attr('contentEditable', 'true'); - this.editor = CKEDITOR.inline(this.element.get(0)); - this.options.disabled = false; - - var widget = this; - this.editor.on('focus', function () { - widget.options.activated(); - }); - this.editor.on('blur', function () { - widget.options.activated(); - }); - this.editor.on('key', function () { - widget.options.changed(widget.editor.getData()); - }); - this.editor.on('paste', function () { - widget.options.changed(widget.editor.getData()); - }); - this.editor.on('afterCommandExec', function () { - widget.options.changed(widget.editor.getData()); - }); - }, - - disable: function () { - if (!this.editor) { - return; - } - this.element.attr('contentEditable', 'false'); - this.editor.destroy(); - this.editor = null; - }, - - _initialize: function () { - CKEDITOR.disableAutoInline = true; - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2012 Tobias Herrmann, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false document:false */ - 'use strict'; - - // # Hallo editing widget - // - // This widget allows editing textual content areas with the - // [Hallo](http://hallojs.org) rich text editor. - jQuery.widget('Create.halloWidget', jQuery.Create.editWidget, { - options: { - editorOptions: {}, - disabled: true, - toolbarState: 'full', - vie: null, - entity: null - }, - enable: function () { - jQuery(this.element).hallo({ - editable: true - }); - this.options.disabled = false; - }, - - disable: function () { - jQuery(this.element).hallo({ - editable: false - }); - this.options.disabled = true; - }, - - _initialize: function () { - jQuery(this.element).hallo(this.getHalloOptions()); - var self = this; - jQuery(this.element).bind('halloactivated', function (event, data) { - self.options.activated(); - }); - jQuery(this.element).bind('hallodeactivated', function (event, data) { - self.options.deactivated(); - }); - jQuery(this.element).bind('hallomodified', function (event, data) { - self.options.changed(data.content); - data.editable.setUnmodified(); - }); - - jQuery(document).bind('midgardtoolbarstatechange', function(event, data) { - // Switch between Hallo configurations when toolbar state changes - if (data.display === self.options.toolbarState) { - return; - } - self.options.toolbarState = data.display; - var newOptions = self.getHalloOptions(); - self.element.hallo('changeToolbar', newOptions.parentElement, newOptions.toolbar, true); - }); - }, - - getHalloOptions: function() { - var defaults = { - plugins: { - halloformat: {}, - halloblock: {}, - hallolists: {}, - hallolink: {}, - halloimage: { - entity: this.options.entity - } - }, - buttonCssClass: 'create-ui-btn-small', - placeholder: '[' + this.options.property + ']' - }; - - if (typeof this.element.annotate === 'function' && this.options.vie.services.stanbol) { - // Enable Hallo Annotate plugin by default if user has annotate.js - // loaded and VIE has Stanbol enabled - defaults.plugins.halloannotate = { - vie: this.options.vie - }; - } - - if (this.options.toolbarState === 'full') { - // Use fixed toolbar in the Create tools area - defaults.parentElement = jQuery('.create-ui-toolbar-dynamictoolarea .create-ui-tool-freearea'); - defaults.toolbar = 'halloToolbarFixed'; - } else { - // Tools area minimized, use floating toolbar - defaults.parentElement = 'body'; - defaults.toolbar = 'halloToolbarContextual'; - } - return _.extend(defaults, this.options.editorOptions); - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2012 Henri Bergius, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false document:false */ - 'use strict'; - - // # Redactor editing widget - // - // This widget allows editing textual content areas with the - // [Redactor](http://redactorjs.com/) rich text editor. - jQuery.widget('Create.redactorWidget', jQuery.Create.editWidget, { - editor: null, - - options: { - editorOptions: {}, - disabled: true - }, - - enable: function () { - jQuery(this.element).redactor(this.getRedactorOptions()); - this.options.disabled = false; - }, - - disable: function () { - jQuery(this.element).destroyEditor(); - this.options.disabled = true; - }, - - _initialize: function () { - var self = this; - jQuery(this.element).bind('focus', function (event) { - self.options.activated(); - }); - /* - jQuery(this.element).bind('blur', function (event) { - self.options.deactivated(); - }); - */ - }, - - getRedactorOptions: function () { - var self = this; - var overrides = { - keyupCallback: function (obj, event) { - self.options.changed(jQuery(self.element).getCode()); - }, - execCommandCallback: function (obj, command) { - self.options.changed(jQuery(self.element).getCode()); - } - }; - - return _.extend(self.options.editorOptions, overrides); - } - }); -})(jQuery); -// Create.js - On-site web editing interface -// (c) 2011-2012 Henri Bergius, IKS Consortium -// Create may be freely distributed under the MIT license. -// For all details and documentation: -// http://createjs.org/ -(function (jQuery, undefined) { - // Run JavaScript in strict mode - /*global jQuery:false _:false window:false */ - 'use strict'; - - jQuery.widget('Midgard.midgardStorage', { - saveEnabled: true, - options: { - // Whether to use localstorage - localStorage: false, - removeLocalstorageOnIgnore: true, - // VIE instance to use for storage handling - vie: null, - // URL callback for Backbone.sync - url: '', - // Whether to enable automatic saving - autoSave: false, - // How often to autosave in milliseconds - autoSaveInterval: 5000, - // Whether to save entities that are referenced by entities - // we're saving to the server. - saveReferencedNew: false, - saveReferencedChanged: false, - // Namespace used for events from midgardEditable-derived widget - editableNs: 'midgardeditable', - // CSS selector for the Edit button, leave to null to not bind - // notifications to any element - editSelector: '#midgardcreate-edit a', - localize: function (id, language) { - return window.midgardCreate.localize(id, language); - }, - language: null - }, - - _create: function () { - var widget = this; - this.changedModels = []; - - if (window.localStorage) { - this.options.localStorage = true; - } - - this.vie = this.options.vie; - - this.vie.entities.bind('add', function (model) { - // Add the back-end URL used by Backbone.sync - model.url = widget.options.url; - model.toJSON = model.toJSONLD; - }); - - widget._bindEditables(); - if (widget.options.autoSave) { - widget._autoSave(); - } - }, - - _autoSave: function () { - var widget = this; - widget.saveEnabled = true; - - var doAutoSave = function () { - if (!widget.saveEnabled) { - return; - } - - if (widget.changedModels.length === 0) { - return; - } - - widget.saveRemoteAll({ - // We make autosaves silent so that potential changes from server - // don't disrupt user while writing. - silent: true - }); - }; - - var timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); - - this.element.bind('startPreventSave', function () { - if (timeout) { - window.clearInterval(timeout); - timeout = null; - } - widget.disableAutoSave(); - }); - this.element.bind('stopPreventSave', function () { - if (!timeout) { - timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); - } - widget.enableAutoSave(); - }); - - }, - - enableAutoSave: function () { - this.saveEnabled = true; - }, - - disableAutoSave: function () { - this.saveEnabled = false; - }, - - _bindEditables: function () { - var widget = this; - this.restorables = []; - var restorer; - - widget.element.bind(widget.options.editableNs + 'changed', function (event, options) { - if (_.indexOf(widget.changedModels, options.instance) === -1) { - widget.changedModels.push(options.instance); - } - widget._saveLocal(options.instance); - }); - - widget.element.bind(widget.options.editableNs + 'disable', function (event, options) { - widget._restoreLocal(options.instance); - }); - - widget.element.bind(widget.options.editableNs + 'enable', function (event, options) { - if (!options.instance._originalAttributes) { - options.instance._originalAttributes = _.clone(options.instance.attributes); - } - - if (!options.instance.isNew() && widget._checkLocal(options.instance)) { - // We have locally-stored modifications, user needs to be asked - widget.restorables.push(options.instance); - } - - /*_.each(options.instance.attributes, function (attributeValue, property) { - if (attributeValue instanceof widget.vie.Collection) { - widget._readLocalReferences(options.instance, property, attributeValue); - } - });*/ - }); - - widget.element.bind('midgardcreatestatechange', function (event, options) { - if (options.state === 'browse' || widget.restorables.length === 0) { - widget.restorables = []; - if (restorer) { - restorer.close(); - } - return; - } - restorer = widget.checkRestore(); - }); - - widget.element.bind('midgardstorageloaded', function (event, options) { - if (_.indexOf(widget.changedModels, options.instance) === -1) { - widget.changedModels.push(options.instance); - } - }); - }, - - checkRestore: function () { - var widget = this; - if (widget.restorables.length === 0) { - return; - } - - var message; - var restorer; - if (widget.restorables.length === 1) { - message = _.template(widget.options.localize('localModification', widget.options.language), { - label: widget.restorables[0].getSubjectUri() - }); - } else { - message = _.template(widget.options.localize('localModifications', widget.options.language), { - number: widget.restorables.length - }); - } - - var doRestore = function (event, notification) { - widget.restoreLocal(); - restorer.close(); - }; - - var doIgnore = function (event, notification) { - widget.ignoreLocal(); - restorer.close(); - }; - - restorer = jQuery('body').midgardNotifications('create', { - bindTo: widget.options.editSelector, - gravity: 'TR', - body: message, - timeout: 0, - actions: [ - { - name: 'restore', - label: widget.options.localize('Restore', widget.options.language), - cb: doRestore, - className: 'create-ui-btn' - }, - { - name: 'ignore', - label: widget.options.localize('Ignore', widget.options.language), - cb: doIgnore, - className: 'create-ui-btn' - } - ], - callbacks: { - beforeShow: function () { - if (!window.Mousetrap) { - return; - } - window.Mousetrap.bind(['command+shift+r', 'ctrl+shift+r'], function (event) { - event.preventDefault(); - doRestore(); - }); - window.Mousetrap.bind(['command+shift+i', 'ctrl+shift+i'], function (event) { - event.preventDefault(); - doIgnore(); - }); - }, - afterClose: function () { - if (!window.Mousetrap) { - return; - } - window.Mousetrap.unbind(['command+shift+r', 'ctrl+shift+r']); - window.Mousetrap.unbind(['command+shift+i', 'ctrl+shift+i']); - } - } - }); - return restorer; - }, - - restoreLocal: function () { - _.each(this.restorables, function (instance) { - this.readLocal(instance); - }, this); - this.restorables = []; - }, - - ignoreLocal: function () { - if (this.options.removeLocalstorageOnIgnore) { - _.each(this.restorables, function (instance) { - this._removeLocal(instance); - }, this); - } - this.restorables = []; - }, - - saveReferences: function (model) { - _.each(model.attributes, function (value, property) { - if (!value || !value.isCollection) { - return; - } - - value.each(function (referencedModel) { - if (this.changedModels.indexOf(referencedModel) !== -1) { - // The referenced model is already in the save queue - return; - } - - if (referencedModel.isNew() && this.options.saveReferencedNew) { - return referencedModel.save(); - } - - if (referencedModel.hasChanged() && this.options.saveReferencedChanged) { - return referencedModel.save(); - } - }, this); - }, this); - }, - - saveRemote: function (model, options) { - // Optionally handle entities referenced in this model first - this.saveReferences(model); - - this._trigger('saveentity', null, { - entity: model, - options: options - }); - - var widget = this; - model.save(null, _.extend({}, options, { - success: function (m, response) { - // From now on we're going with the values we have on server - model._originalAttributes = _.clone(model.attributes); - widget._removeLocal(model); - window.setTimeout(function () { - // Remove the model from the list of changed models after saving - widget.changedModels.splice(widget.changedModels.indexOf(model), 1); - }, 0); - if (_.isFunction(options.success)) { - options.success(m, response); - } - widget._trigger('savedentity', null, { - entity: model, - options: options - }); - }, - error: function (m, response) { - if (_.isFunction(options.error)) { - options.error(m, response); - } - } - })); - }, - - saveRemoteAll: function (options) { - var widget = this; - if (widget.changedModels.length === 0) { - return; - } - - widget._trigger('save', null, { - entities: widget.changedModels, - options: options, - // Deprecated - models: widget.changedModels - }); - - var notification_msg; - var needed = widget.changedModels.length; - if (needed > 1) { - notification_msg = _.template(widget.options.localize('saveSuccessMultiple', widget.options.language), { - number: needed - }); - } else { - notification_msg = _.template(widget.options.localize('saveSuccess', widget.options.language), { - label: widget.changedModels[0].getSubjectUri() - }); - } - - widget.disableAutoSave(); - _.each(widget.changedModels, function (model) { - this.saveRemote(model, { - success: function (m, response) { - needed--; - if (needed <= 0) { - // All models were happily saved - widget._trigger('saved', null, { - options: options - }); - if (options && _.isFunction(options.success)) { - options.success(m, response); - } - jQuery('body').midgardNotifications('create', { - body: notification_msg - }); - widget.enableAutoSave(); - } - }, - error: function (m, err) { - if (options && _.isFunction(options.error)) { - options.error(m, err); - } - jQuery('body').midgardNotifications('create', { - body: _.template(widget.options.localize('saveError', widget.options.language), { - error: err.responseText || '' - }), - timeout: 0 - }); - - widget._trigger('error', null, { - instance: model - }); - } - }); - }, this); - }, - - _saveLocal: function (model) { - if (!this.options.localStorage) { - return; - } - - if (model.isNew()) { - // Anonymous object, save as refs instead - if (!model.primaryCollection) { - return; - } - return this._saveLocalReferences(model.primaryCollection.subject, model.primaryCollection.predicate, model); - } - window.localStorage.setItem(model.getSubjectUri(), JSON.stringify(model.toJSONLD())); - }, - - _getReferenceId: function (model, property) { - return model.id + ':' + property; - }, - - _saveLocalReferences: function (subject, predicate, model) { - if (!this.options.localStorage) { - return; - } - - if (!subject || !predicate) { - return; - } - - var widget = this; - var identifier = subject + ':' + predicate; - var json = model.toJSONLD(); - if (window.localStorage.getItem(identifier)) { - var referenceList = JSON.parse(window.localStorage.getItem(identifier)); - var index = _.pluck(referenceList, '@').indexOf(json['@']); - if (index !== -1) { - referenceList[index] = json; - } else { - referenceList.push(json); - } - window.localStorage.setItem(identifier, JSON.stringify(referenceList)); - return; - } - window.localStorage.setItem(identifier, JSON.stringify([json])); - }, - - _checkLocal: function (model) { - if (!this.options.localStorage) { - return false; - } - - var local = window.localStorage.getItem(model.getSubjectUri()); - if (!local) { - return false; - } - - return true; - }, - - hasLocal: function (model) { - if (!this.options.localStorage) { - return false; - } - - if (!window.localStorage.getItem(model.getSubjectUri())) { - return false; - } - return true; - }, - - readLocal: function (model) { - if (!this.options.localStorage) { - return; - } - - var local = window.localStorage.getItem(model.getSubjectUri()); - if (!local) { - return; - } - if (!model._originalAttributes) { - model._originalAttributes = _.clone(model.attributes); - } - var parsed = JSON.parse(local); - var entity = this.vie.entities.addOrUpdate(parsed, { - overrideAttributes: true - }); - - this._trigger('loaded', null, { - instance: entity - }); - }, - - _readLocalReferences: function (model, property, collection) { - if (!this.options.localStorage) { - return; - } - - var identifier = this._getReferenceId(model, property); - var local = window.localStorage.getItem(identifier); - if (!local) { - return; - } - collection.add(JSON.parse(local)); - }, - - _restoreLocal: function (model) { - var widget = this; - - // Remove unsaved collection members - if (!model) { return; } - _.each(model.attributes, function (attributeValue, property) { - if (attributeValue instanceof widget.vie.Collection) { - var removables = []; - attributeValue.forEach(function (model) { - if (model.isNew()) { - removables.push(model); - } - }); - attributeValue.remove(removables); - } - }); - - // Restore original object properties - if (!model.changedAttributes()) { - if (model._originalAttributes) { - model.set(model._originalAttributes); - } - return; - } - - model.set(model.previousAttributes()); - }, - - _removeLocal: function (model) { - if (!this.options.localStorage) { - return; - } - - window.localStorage.removeItem(model.getSubjectUri()); - } - }); -})(jQuery); -if (window.midgardCreate === undefined) { - window.midgardCreate = {}; -} -if (window.midgardCreate.locale === undefined) { - window.midgardCreate.locale = {}; -} - -window.midgardCreate.locale.en = { - // Session-state buttons for the main toolbar - 'Save': 'Save', - 'Saving': 'Saving', - 'Cancel': 'Cancel', - 'Edit': 'Edit', - // Storage status messages - 'localModification': 'Item "<%= label %>" has local modifications', - 'localModifications': '<%= number %> items on this page have local modifications', - 'Restore': 'Restore', - 'Ignore': 'Ignore', - 'saveSuccess': 'Item "<%= label %>" saved successfully', - 'saveSuccessMultiple': '<%= number %> items saved successfully', - 'saveError': 'Error occurred while saving
<%= error %>', - // Tagging - 'Item tags': 'Item tags', - 'Suggested tags': 'Suggested tags', - 'Tags': 'Tags', - 'add a tag': 'add a tag', - // Collection widgets - 'Add': 'Add', - 'Choose type to add': 'Choose type to add' -}; diff --git a/js/lib/vie.js b/js/lib/vie.js deleted file mode 100644 index ca5c079..0000000 --- a/js/lib/vie.js +++ /dev/null @@ -1,3682 +0,0 @@ -/*Copyright (c) 2011 Henri Bergius, IKS Consortium -Copyright (c) 2011 Sebastian Germesin, IKS Consortium - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. -*/(function(){// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ - -/*global console:false exports:false require:false */ - -var root = this, - jQuery = root.jQuery, - Backbone = root.Backbone, - _ = root._; - - -// ## VIE constructor -// -// The VIE constructor is the way to initialize VIE for your -// application. The instance of VIE handles all management of -// semantic interaction, including keeping track of entities, -// changes to them, the possible RDFa views on the page where -// the entities are displayed, and connections to external -// services like Stanbol and DBPedia. -// -// To get a VIE instance, simply run: -// -// var vie = new VIE(); -// -// You can also pass configurations to the VIE instance through -// the constructor. For example, to set a different default -// namespace to be used for names that don't have a namespace -// specified, do: -// -// var vie = new VIE({ -// baseNamespace: 'http://example.net' -// }); -// -// ### Differences with VIE 1.x -// -// VIE 1.x used singletons for managing entities and views loaded -// from a page. This has been changed with VIE 2.x, and now all -// data managed by VIE is tied to the instance of VIE being used. -// -// This means that VIE needs to be instantiated before using. So, -// when previously you could get entities from page with: -// -// VIE.RDFaEntities.getInstances(); -// -// Now you need to instantiate VIE first. This example uses the -// Classic API compatibility layer instead of the `load` method: -// -// var vie = new VIE(); -// vie.RDFaEntities.getInstances(); -// -// Currently the Classic API is enabled by default, but it is -// recommended to ensure it is enabled before using it. So: -// -// var vie = new VIE({classic: true}); -// vie.RDFaEntities.getInstances(); -var VIE = root.VIE = function(config) { - this.config = (config) ? config : {}; - this.services = {}; - this.jQuery = jQuery; - this.entities = new this.Collection([], { - vie: this - }); - - this.Entity.prototype.entities = this.entities; - this.Entity.prototype.entityCollection = this.Collection; - this.Entity.prototype.vie = this; - - this.Namespaces.prototype.vie = this; -// ### Namespaces in VIE -// VIE supports different ontologies and an easy use of them. -// Namespace prefixes reduce the amount of code you have to -// write. In VIE, it does not matter if you access an entitie's -// property with -// `entity.get('')` or -// `entity.get('dbprop:capitalOf')` or even -// `entity.get('capitalOf')` once the corresponding namespace -// is registered as *baseNamespace*. -// By default `"http://viejs.org/ns/"`is set as base namespace. -// For more information about how to set, get and list all -// registered namespaces, refer to the -// Namespaces documentation. - this.namespaces = new this.Namespaces( - (this.config.baseNamespace) ? this.config.baseNamespace : "http://viejs.org/ns/", - -// By default, VIE is shipped with common namespace prefixes: - -// + owl : "http://www.w3.org/2002/07/owl#" -// + rdfs : "http://www.w3.org/2000/01/rdf-schema#" -// + rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -// + schema : 'http://schema.org/' -// + foaf : 'http://xmlns.com/foaf/0.1/' -// + geo : 'http://www.w3.org/2003/01/geo/wgs84_pos#' -// + dbpedia: "http://dbpedia.org/ontology/" -// + dbprop : "http://dbpedia.org/property/" -// + skos : "http://www.w3.org/2004/02/skos/core#" -// + xsd : "http://www.w3.org/2001/XMLSchema#" -// + sioc : "http://rdfs.org/sioc/ns#" -// + dcterms: "http://purl.org/dc/terms/" - { - owl : "http://www.w3.org/2002/07/owl#", - rdfs : "http://www.w3.org/2000/01/rdf-schema#", - rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - schema : 'http://schema.org/', - foaf : 'http://xmlns.com/foaf/0.1/', - geo : 'http://www.w3.org/2003/01/geo/wgs84_pos#', - dbpedia: "http://dbpedia.org/ontology/", - dbprop : "http://dbpedia.org/property/", - skos : "http://www.w3.org/2004/02/skos/core#", - xsd : "http://www.w3.org/2001/XMLSchema#", - sioc : "http://rdfs.org/sioc/ns#", - dcterms: "http://purl.org/dc/terms/" - } - ); - - - this.Type.prototype.vie = this; - this.Types.prototype.vie = this; - this.Attribute.prototype.vie = this; - this.Attributes.prototype.vie = this; -// ### Type hierarchy in VIE -// VIE takes care about type hierarchy of entities -// (aka. *schema* or *ontology*). -// Once a type hierarchy is known to VIE, we can leverage -// this information, to easily ask, whether an entity -// is of type, e.g., *foaf:Person* or *schema:Place*. -// For more information about how to generate such a type -// hierarchy, refer to the -// Types documentation. - this.types = new this.Types(); -// By default, there is a parent type in VIE, called -// *owl:Thing*. All types automatically inherit from this -// type and all registered entities, are of this type. - this.types.add("owl:Thing"); - -// As described above, the Classic API of VIE 1.x is loaded -// by default. As this might change in the future, it is -// recommended to ensure it is enabled before using it. So: -// -// var vie = new VIE({classic: true}); -// vie.RDFaEntities.getInstances(); - if (this.config.classic === true) { - /* Load Classic API as well */ - this.RDFa = new this.ClassicRDFa(this); - this.RDFaEntities = new this.ClassicRDFaEntities(this); - this.EntityManager = new this.ClassicEntityManager(this); - - this.cleanup = function() { - this.entities.reset(); - }; - } -}; - -// ### use(service, name) -// This method registers services within VIE. -// **Parameters**: -// *{string|object}* **service** The service to be registered. -// *{string}* **name** An optional name to register the service with. If this -// is not set, the default name that comes with the service is taken. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE}* : The current VIE instance. -// **Example usage**: -// -// var vie = new VIE(); -// var conf1 = {...}; -// var conf2 = {...}; -// vie.use(new vie.StanbolService()); -// vie.use(new vie.StanbolService(conf1), "stanbol_1"); -// vie.use(new vie.StanbolService(conf2), "stanbol_2"); -// // <-- this means that there are now 3 services registered! -VIE.prototype.use = function(service, name) { - if (!name && !service.name) { - throw new Error("Please provide a name for the service!"); - } - service.vie = this; - service.name = (name)? name : service.name; - if (service.init) { - service.init(); - } - this.services[service.name] = service; - - return this; -}; - -// ### service(name) -// This method returns the service object that is -// registered under the given name. -// **Parameters**: -// *{string}* **name** ... -// **Throws**: -// *{Error}* if no service could be found. -// **Returns**: -// *{object}* : The service to be queried. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var service = vie.service("stanbol"); -VIE.prototype.service = function(name) { - if (!this.hasService(name)) { - throw "Undefined service " + name; - } - return this.services[name]; -}; - -// ### hasService(name) -// This method returns a boolean telling whether VIE has a particular -// service loaded. -// **Parameters**: -// *{string}* **name** -// **Returns**: -// *{boolean}* whether service is available -VIE.prototype.hasService = function(name) { - if (!this.services[name]) { - return false; - } - return true; -}; - -// ### getServicesArray() -// This method returns an array of all registered services. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{array}* : An array of service instances. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var services = vie.getServicesArray(); -// services.length; // <-- 1 -VIE.prototype.getServicesArray = function() { - return _.map(this.services, function (v) {return v;}); -}; - -// ### load(options) -// This method instantiates a new VIE.Loadable in order to -// perform queries on the services. -// **Parameters**: -// *{object}* **options** Options to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Loadable}* : A new instance of VIE.Loadable. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var loader = vie.load({...}); -VIE.prototype.load = function(options) { - if (!options) { options = {}; } - options.vie = this; - return new this.Loadable(options); -}; - -// ### save(options) -// This method instantiates a new VIE.Savable in order to -// perform queries on the services. -// **Parameters**: -// *{object}* **options** Options to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Savable}* : A new instance of VIE.Savable. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var saver = vie.save({...}); -VIE.prototype.save = function(options) { - if (!options) { options = {}; } - options.vie = this; - return new this.Savable(options); -}; - -// ### remove(options) -// This method instantiates a new VIE.Removable in order to -// perform queries on the services. -// **Parameters**: -// *{object}* **options** Options to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Removable}* : A new instance of VIE.Removable. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var remover = vie.remove({...}); -VIE.prototype.remove = function(options) { - if (!options) { options = {}; } - options.vie = this; - return new this.Removable(options); -}; - -// ### analyze(options) -// This method instantiates a new VIE.Analyzable in order to -// perform queries on the services. -// **Parameters**: -// *{object}* **options** Options to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Analyzable}* : A new instance of VIE.Analyzable. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var analyzer = vie.analyze({...}); -VIE.prototype.analyze = function(options) { - if (!options) { options = {}; } - options.vie = this; - return new this.Analyzable(options); -}; - -// ### find(options) -// This method instantiates a new VIE.Findable in order to -// perform queries on the services. -// **Parameters**: -// *{object}* **options** Options to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Findable}* : A new instance of VIE.Findable. -// **Example usage**: -// -// var vie = new VIE(); -// vie.use(new vie.StanbolService(), "stanbol"); -// var finder = vie.find({...}); -VIE.prototype.find = function(options) { - if (!options) { options = {}; } - options.vie = this; - return new this.Findable(options); -}; - -// ### loadSchema(url, options) -// VIE only knows the *owl:Thing* type by default. -// You can use this method to import another -// schema (ontology) from an external resource. -// (Currently, this supports only the JSON format!!) -// As this method works asynchronously, you might want -// to register `success` and `error` callbacks via the -// options. -// **Parameters**: -// *{string}* **url** The url, pointing to the schema to import. -// *{object}* **options** Options to be set. -// (Set ```success``` and ```error``` as callbacks.). -// **Throws**: -// *{Error}* if the url is not set. -// **Returns**: -// *{VIE}* : The VIE instance itself. -// **Example usage**: -// -// var vie = new VIE(); -// vie.loadSchema("http://schema.rdfs.org/all.json", -// { -// baseNS : "http://schema.org/", -// success : function () {console.log("success");}, -// error : function (msg) {console.warn(msg);} -// }); -VIE.prototype.loadSchema = function(url, options) { - options = (!options)? {} : options; - - if (!url) { - throw new Error("Please provide a proper URL"); - } - else { - var vie = this; - jQuery.getJSON(url) - .success(function(data) { - try { - VIE.Util.loadSchemaOrg(vie, data, options.baseNS); - if (options.success) { - options.success.call(vie); - } - } catch (e) { - options.error.call(vie, e); - return; - } - }) - .error(function(data, textStatus, jqXHR) { - if (options.error) { - console.warn(data, textStatus, jqXHR); - options.error.call(vie, "Could not load schema from URL (" + url + ")"); - } - }); - } - - return this; -}; - -// ### getTypedEntityClass(type) -// This method generates a special type of `Entity` based on the given type. -// **Parameters**: -// *{string}* **type** The type. -// **Throws**: -// *{Error}* if the type is unknown to VIE. -// **Returns**: -// *{VIE.Entity}* : A subclass of `VIE.Entity`. -// **Example usage**: -// -// var vie = new VIE(); -// vie.types.add("Person"); -// var PersonClass = vie.getTypedEntityClass("Person"); -// var Person = new PersonClass({"name", "Sebastian"}); -VIE.prototype.getTypedEntityClass = function (type) { - var typeType = this.types.get(type); - if (!typeType) { - throw new Error("Unknown type " + type); - } - var TypedEntityClass = function (attrs, opts) { - if (!attrs) { - attrs = {}; - } - attrs["@type"] = type; - this.set(attrs, opts); - }; - TypedEntityClass.prototype = new this.Entity(); - TypedEntityClass.prototype.schema = function () { - return VIE.Util.getFormSchemaForType(typeType); - }; - return TypedEntityClass; -}; - -// ## Running VIE on Node.js -// -// When VIE is running under Node.js we can use the CommonJS -// require interface to load our dependencies automatically. -// -// This means Node.js users don't need to care about dependencies -// and can just run VIE with: -// -// var VIE = require('vie'); -// -// In browser environments the dependencies have to be included -// before including VIE itself. -if (typeof exports === 'object') { - exports.VIE = VIE; - - if (!jQuery) { - jQuery = require('jquery'); - } - if (!Backbone) { - Backbone = require('backbone'); - Backbone.setDomLibrary(jQuery); - } - if (!_) { - _ = require('underscore')._; - } -} -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ - -// ## VIE.Able -// VIE implements asynchronius service methods through -// [jQuery.Deferred](http://api.jquery.com/category/deferred-object/) objects. -// Loadable, Analysable, Savable, etc. are part of the VIE service API and -// are implemented with the generic VIE.Able class. -// Example: -// -// VIE.prototype.Loadable = function (options) { -// this.init(options,"load"); -// }; -// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); -// -// This defines -// -// someVIEService.load(options) -// .using(...) -// .execute() -// .success(...) -// .fail(...) -// which will run the asynchronius `load` function of the service with the created Loadable -// object. - -// ### VIE.Able() -// This is the constructor of a VIE.Able. This should not be called -// globally but using the inherited classes below. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Able}* : A **new** VIE.Able object. -// Example: -// -// VIE.prototype.Loadable = function (options) { -// this.init(options,"load"); -// }; -// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); -VIE.prototype.Able = function(){ - -// ### init(options, methodName) -// Internal method, called during initialization. -// **Parameters**: -// *{object}* **options** the *able* options coming from the API call -// *{string}* **methodName** the service method called on `.execute`. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Able}* : The current instance. -// **Example usage**: -// -// VIE.prototype.Loadable = function (options) { -// this.init(options,"load"); -// }; -// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); - this.init = function(options, methodName) { - this.options = options; - this.services = options.from || options.using || options.to || []; - this.vie = options.vie; - - this.methodName = methodName; - - // Instantiate the deferred object - this.deferred = jQuery.Deferred(); - -// In order to get more information and documentation about the passed-through -// deferred methods and their synonyms, please see the documentation of -// the [jQuery.Deferred object](http://api.jquery.com/category/deferred-object/) - /* Public deferred-methods */ - this.resolve = this.deferred.resolve; - this.resolveWith = this.deferred.resolveWith; - this.reject = this.deferred.reject; - this.rejectWith = this.deferred.rejectWith; - this.success = this.done = this.deferred.done; - this.fail = this.deferred.fail; - this.then = this.deferred.then; - this.always = this.deferred.always; - this.from = this.using; - this.to = this.using; - - return this; - }; - - -// ### using(services) -// This method registers services with the current able instance. -// **Parameters**: -// *{string|array}* **services** An id of a service or an array of strings. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Able}* : The current instance. -// **Example usage**: -// -// var loadable = vie.load({id: "http://example.com/entity/1234"}); -// able.using("myService"); - this.using = function(services) { - var self = this; - services = (_.isArray(services))? services : [ services ]; - _.each (services, function (s) { - var obj = (typeof s === "string")? self.vie.service(s) : s; - self.services.push(obj); - }); - return this; - }; - -// ### execute() -// This method runs the actual method on all registered services. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* ... -// **Returns**: -// *{VIE.Able}* : The current instance. -// **Example usage**: -// -// var able = new vie.Able().init(); -// able.using("stanbol") -// .done(function () {alert("finished");}) -// .execute(); - this.execute = function() { - /* call service[methodName] */ - var able = this; - _(this.services).each(function(service){ - service[able.methodName](able); - }); - return this; - }; -}; - -// ## VIE.Loadable -// A ```VIE.Loadable``` is a wrapper around the deferred object -// to **load** semantic data from a semantic web service. -VIE.prototype.Loadable = function (options) { - this.init(options,"load"); -}; -VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); - -// ## VIE.Savable -// A ```VIE.Savable``` is a wrapper around the deferred object -// to **save** entities by a VIE service. The RDFaService would write the data -// in the HTML as RDFa, the StanbolService stores the data in its Entityhub, etc. -VIE.prototype.Savable = function(options){ - this.init(options, "save"); -}; -VIE.prototype.Savable.prototype = new VIE.prototype.Able(); - -// ## VIE.Removable -// A ```VIE.Removable``` is a wrapper around the deferred object -// to **remove** semantic data from a semantic web service. -VIE.prototype.Removable = function(options){ - this.init(options, "remove"); -}; -VIE.prototype.Removable.prototype = new VIE.prototype.Able(); - -// ## VIE.Analyzable -// A ```VIE.Analyzable``` is a wrapper around the deferred object -// to **analyze** data and extract semantic information with the -// help of a semantic web service. -VIE.prototype.Analyzable = function (options) { - this.init(options, "analyze"); -}; -VIE.prototype.Analyzable.prototype = new VIE.prototype.Able(); - -// ## VIE.Findable -// A ```VIE.Findable``` is a wrapper around the deferred object -// to **find** semantic data on a semantic storage. -VIE.prototype.Findable = function (options) { - this.init(options, "find"); -}; -VIE.prototype.Findable.prototype = new VIE.prototype.Able(); - -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ - -// ## VIE Utils -// -// The here-listed methods are utility methods for the day-to-day -// VIE.js usage. All methods are within the static namespace ```VIE.Util```. -VIE.Util = { - -// ### VIE.Util.toCurie(uri, safe, namespaces) -// This method converts a given -// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object. -// If the given uri is already a URI, it is left untouched and directly returned. -// If no prefix could be found, an ```Error``` is thrown. -// **Parameters**: -// *{string}* **uri** The URI to be transformed. -// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs. -// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes. -// **Throws**: -// *{Error}* If no prefix could be found in the passed namespaces. -// **Returns**: -// *{string}* The CURIE or SCURIE. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var uri = ""; -// VIE.Util.toCurie(uri, false, ns); // --> dbp:Person -// VIE.Util.toCurie(uri, true, ns); // --> [dbp:Person] - toCurie : function (uri, safe, namespaces) { - if (VIE.Util.isCurie(uri, namespaces)) { - return uri; - } - var delim = ":"; - for (var k in namespaces.toObj()) { - if (uri.indexOf(namespaces.get(k)) === 1) { - var pattern = new RegExp("^" + "$/, '') + - ((safe)? "]" : ""); - } - } - throw new Error("No prefix found for URI '" + uri + "'!"); - }, - -// ### VIE.Util.isCurie(curie, namespaces) -// This method checks, whether -// the given string is a CURIE and returns ```true``` if so and ```false```otherwise. -// **Parameters**: -// *{string}* **curie** The CURIE (or SCURIE) to be checked. -// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var uri = ""; -// var curie = "dbp:Person"; -// var scurie = "[dbp:Person]"; -// var text = "This is some text."; -// VIE.Util.isCurie(uri, ns); // --> false -// VIE.Util.isCurie(curie, ns); // --> true -// VIE.Util.isCurie(scurie, ns); // --> true -// VIE.Util.isCurie(text, ns); // --> false - isCurie : function (curie, namespaces) { - if (VIE.Util.isUri(curie)) { - return false; - } else { - try { - VIE.Util.toUri(curie, namespaces); - return true; - } catch (e) { - return false; - } - } - }, - -// ### VIE.Util.toUri(curie, namespaces) -// This method converts a -// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object. -// **Parameters**: -// *{string}* **curie** The CURIE to be transformed. -// *{VIE.Namespaces}* **namespaces** The namespaces object -// **Throws**: -// *{Error}* If no URI could be assembled. -// **Returns**: -// *{string}* : A string, representing the URI. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var curie = "dbp:Person"; -// var scurie = "[dbp:Person]"; -// VIE.Util.toUri(curie, ns); -// --> -// VIE.Util.toUri(scurie, ns); -// --> - toUri : function (curie, namespaces) { - if (VIE.Util.isUri(curie)) { - return curie; - } - var delim = ":"; - for (var prefix in namespaces.toObj()) { - if (prefix !== "" && (curie.indexOf(prefix + ":") === 0 || curie.indexOf("[" + prefix + ":") === 0)) { - var pattern = new RegExp("^" + "\\[{0,1}" + prefix + delim); - return "<" + curie.replace(pattern, namespaces.get(prefix)).replace(/\]{0,1}$/, '') + ">"; - } - } - /* check for the default namespace */ - if (curie.indexOf(delim) === -1) { - return "<" + namespaces.base() + curie + ">"; - } - throw new Error("No prefix found for CURIE '" + curie + "'!"); - }, - -// ### VIE.Util.isUri(something) -// This method checks, whether the given string is a URI. -// **Parameters**: -// *{string}* **something** : The string to be checked. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise. -// **Example usage**: -// -// var uri = ""; -// var curie = "dbp:Person"; -// VIE.Util.isUri(uri); // --> true -// VIE.Util.isUri(curie); // --> false - isUri : function (something) { - return (typeof something === "string" && something.search(/^<.+>$/) === 0); - }, - -// ### VIE.Util.mapAttributeNS(attr, ns) -// This method maps an attribute of an entity into namespaces if they have CURIEs. -// **Parameters**: -// *{string}* **attr** : The attribute to be transformed. -// *{VIE.Namespaces}* **ns** : The namespaces. -// **Throws**: -// *nothing* -// **Returns**: -// *{string}* : The transformed attribute's name. -// **Example usage**: -// -// var attr = "name"; -// var ns = myVIE.namespaces; -// VIE.Util.mapAttributeNS(attr, ns); // '<' + ns.base() + attr + '>'; - mapAttributeNS : function (attr, ns) { - var a = attr; - if (ns.isUri (attr) || attr.indexOf('@') === 0) { - //ignore - } else if (ns.isCurie(attr)) { - a = ns.uri(attr); - } else if (!ns.isUri(attr)) { - if (attr.indexOf(":") === -1) { - a = '<' + ns.base() + attr + '>'; - } else { - a = '<' + attr + '>'; - } - } - return a; - }, - -// ### VIE.Util.rdf2Entities(service, results) -// This method converts *rdf/json* data from an external service -// into VIE.Entities. -// **Parameters**: -// *{object}* **service** The service that retrieved the data. -// *{object}* **results** The data to be transformed. -// **Throws**: -// *nothing* -// **Returns**: -// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data. - rdf2Entities: function (service, results) { - if (typeof jQuery.rdf !== 'function') { - /* fallback if no rdfQuery has been loaded */ - return VIE.Util._rdf2EntitiesNoRdfQuery(service, results); - } - try { - var rdf = (results instanceof jQuery.rdf)? - results.base(service.vie.namespaces.base()) : - jQuery.rdf().base(service.vie.namespaces.base()).load(results, {}); - - /* if the service contains rules to apply special transformation, they are executed here.*/ - if (service.rules) { - var rules = jQuery.rdf.ruleset(); - for (var prefix in service.vie.namespaces.toObj()) { - if (prefix !== "") { - rules.prefix(prefix, service.vie.namespaces.get(prefix)); - } - } - for (var i = 0; i < service.rules.length; i++)if(service.rules.hasOwnProperty(i)) { - var rule = service.rules[i]; - rules.add(rule.left, rule.right); - } - rdf = rdf.reason(rules, 10); /* execute the rules only 10 times to avoid looping */ - } - var entities = {}; - rdf.where('?subject ?property ?object').each(function() { - var subject = this.subject.toString(); - if (!entities[subject]) { - entities[subject] = { - '@subject': subject, - '@context': service.vie.namespaces.toObj(true), - '@type': [] - }; - } - var propertyUri = this.property.toString(); - var propertyCurie; - - try { - propertyCurie = service.vie.namespaces.curie(propertyUri); - //jQuery.createCurie(propertyUri, {namespaces: service.vie.namespaces.toObj(true)}); - } catch (e) { - propertyCurie = propertyUri; - // console.warn(propertyUri + " doesn't have a namespace definition in '", service.vie.namespaces.toObj()); - } - entities[subject][propertyCurie] = entities[subject][propertyCurie] || []; - - function getValue(rdfQueryLiteral){ - if(typeof rdfQueryLiteral.value === "string"){ - if (rdfQueryLiteral.lang){ - var literal = { - toString: function(){ - return this["@value"]; - }, - "@value": rdfQueryLiteral.value.replace(/^"|"$/g, ''), - "@language": rdfQueryLiteral.lang - }; - return literal; - } - else - return rdfQueryLiteral.value; - return rdfQueryLiteral.value.toString(); - } else if (rdfQueryLiteral.type === "uri"){ - return rdfQueryLiteral.toString(); - } else { - return rdfQueryLiteral.value; - } - } - entities[subject][propertyCurie].push(getValue(this.object)); - }); - - _(entities).each(function(ent){ - ent["@type"] = ent["@type"].concat(ent["rdf:type"]); - delete ent["rdf:type"]; - _(ent).each(function(value, property){ - if(value.length === 1){ - ent[property] = value[0]; - } - }); - }); - - var vieEntities = []; - jQuery.each(entities, function() { - var entityInstance = new service.vie.Entity(this); - entityInstance = service.vie.entities.addOrUpdate(entityInstance); - vieEntities.push(entityInstance); - }); - return vieEntities; - } catch (e) { - console.warn("Something went wrong while parsing the returned results!", e); - return []; - } - }, - - /* - VIE.Util.getPreferredLangForPreferredProperty(entity, preferredFields, preferredLanguages) - looks for specific ranking fields and languages. It calculates all possibilities and gives them - a score. It returns the value with the best score. - */ - getPreferredLangForPreferredProperty: function(entity, preferredFields, preferredLanguages) { - var l, labelArr, lang, p, property, resArr, valueArr, _len, _len2, - _this = this; - resArr = []; - /* Try to find a label in the preferred language - */ - _.each(preferredLanguages, function (lang) { - _.each(preferredFields, function (property) { - labelArr = null; - /* property can be a string e.g. "skos:prefLabel" - */ - if (typeof property === "string" && entity.get(property)) { - labelArr = _.flatten([entity.get(property)]); - _(labelArr).each(function(label) { - /* - The score is a natural number with 0 for the - best candidate with the first preferred language - and first preferred property - */ - var labelLang, score, value; - score = p; - labelLang = label["@language"]; - /* - legacy code for compatibility with uotdated stanbol, - to be removed after may 2012 - */ - if (typeof label === "string" && (label.indexOf("@") === label.length - 3 || label.indexOf("@") === label.length - 5)) { - labelLang = label.replace(/(^\"*|\"*@)..(..)?$/g, ""); - } - /* end of legacy code - */ - if (labelLang) { - if (labelLang === lang) { - score += l; - } else { - score += 20; - } - } else { - score += 10; - } - value = label.toString(); - /* legacy code for compatibility with uotdated stanbol, to be removed after may 2012 - */ - value = value.replace(/(^\"*|\"*@..$)/g, ""); - /* end of legacy code - */ - return resArr.push({ - score: score, - value: value - }); - }); - /* - property can be an object like - { - property: "skos:broader", - makeLabel: function(propertyValueArr) { return "..."; } - } - */ - } else if (typeof property === "object" && entity.get(property.property)) { - valueArr = _.flatten([entity.get(property.property)]); - valueArr = _(valueArr).map(function(termUri) { - if (termUri.isEntity) { - return termUri.getSubject(); - } else { - return termUri; - } - }); - resArr.push({ - score: p, - value: property.makeLabel(valueArr) - }); - } - }); - }); - /* - take the result with the best score - */ - resArr = _(resArr).sortBy(function(a) { - return a.score; - }); - if(resArr.length) { - return resArr[0].value; - } else { - return "n/a"; - } - }, - - -// ### VIE.Util._rdf2EntitiesNoRdfQuery(service, results) -// This is a **private** method which should -// only be accessed through ```VIE.Util._rdf2Entities()``` and is a helper method in case there is no -// rdfQuery loaded (*not recommended*). -// **Parameters**: -// *{object}* **service** The service that retrieved the data. -// *{object}* **results** The data to be transformed. -// **Throws**: -// *nothing* -// **Returns**: -// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data. - _rdf2EntitiesNoRdfQuery: function (service, results) { - var jsonLD = []; - _.forEach(results, function(value, key) { - var entity = {}; - entity['@subject'] = '<' + key + '>'; - _.forEach(value, function(triples, predicate) { - predicate = '<' + predicate + '>'; - _.forEach(triples, function(triple) { - if (triple.type === 'uri') { - triple.value = '<' + triple.value + '>'; - } - - if (entity[predicate] && !_.isArray(entity[predicate])) { - entity[predicate] = [entity[predicate]]; - } - - if (_.isArray(entity[predicate])) { - entity[predicate].push(triple.value); - return; - } - entity[predicate] = triple.value; - }); - }); - jsonLD.push(entity); - }); - return jsonLD; - }, - -// ### VIE.Util.loadSchemaOrg(vie, SchemaOrg, baseNS) -// This method is a wrapper around -// the schema.org ontology. It adds all the -// given types and properties as ```VIE.Type``` instances to the given VIE instance. -// If the paramenter **baseNS** is set, the method automatically sets the namespace -// to the provided one. If it is not set, it will keep the base namespace of VIE untouched. -// **Parameters**: -// *{VIE}* **vie** The instance of ```VIE```. -// *{object}* **SchemaOrg** The data imported from schema.org. -// *{string|undefined}* **baseNS** If set, this will become the new baseNamespace within the given ```VIE``` instance. -// **Throws**: -// *{Error}* If the parameter was not given. -// **Returns**: -// *nothing* - loadSchemaOrg : function (vie, SchemaOrg, baseNS) { - - if (!SchemaOrg) { - throw new Error("Please load the schema.json file."); - } - vie.types.remove(""); - - var baseNSBefore = (baseNS)? baseNS : vie.namespaces.base(); - vie.namespaces.base(baseNS); - - var datatypeMapping = { - 'DataType': 'xsd:anyType', - 'Boolean' : 'xsd:boolean', - 'Date' : 'xsd:date', - 'DateTime': 'xsd:dateTime', - 'Time' : 'xsd:time', - 'Float' : 'xsd:float', - 'Integer' : 'xsd:integer', - 'Number' : 'xsd:anySimpleType', - 'Text' : 'xsd:string', - 'URL' : 'xsd:anyURI' - }; - - var dataTypeHelper = function (ancestors, id) { - var type = vie.types.add(id, [{'id' : 'value', 'range' : datatypeMapping[id]}]); - - for (var i = 0; i < ancestors.length; i++) { - var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) : - dataTypeHelper.call(vie, SchemaOrg.datatypes[ancestors[i]].supertypes, ancestors[i]); - type.inherit(supertype); - } - return type; - }; - - for (var dt in SchemaOrg.datatypes) { - if (!vie.types.get(dt)) { - var ancestors = SchemaOrg.datatypes[dt].supertypes; - dataTypeHelper.call(vie, ancestors, dt); - } - } - - var metadataHelper = function (definition) { - var metadata = {}; - - if (definition.label) { - metadata.label = definition.label; - } - - if (definition.url) { - metadata.url = definition.url; - } - - if (definition.comment) { - metadata.comment = definition.comment; - } - - if (definition.metadata) { - metadata = _.extend(metadata, definition.metadata); - } - return metadata; - }; - - var typeProps = function (id) { - var props = []; - _.each(SchemaOrg.types[id].specific_properties, function (pId) { - var property = SchemaOrg.properties[pId]; - props.push({ - 'id' : property.id, - 'range' : property.ranges, - 'min' : property.min, - 'max' : property.max, - 'metadata': metadataHelper(property) - }); - }); - return props; - }; - - var typeHelper = function (ancestors, id, props, metadata) { - var type = vie.types.add(id, props, metadata); - - for (var i = 0; i < ancestors.length; i++) { - var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) : - typeHelper.call(vie, SchemaOrg.types[ancestors[i]].supertypes, ancestors[i], typeProps.call(vie, ancestors[i])); - type.inherit(supertype); - } - if (id === "Thing" && !type.isof("owl:Thing")) { - type.inherit("owl:Thing"); - } - return type; - }; - - _.each(SchemaOrg.types, function (typeDef) { - if (vie.types.get(typeDef.id)) { - return; - } - var ancestors = typeDef.supertypes; - var metadata = metadataHelper(typeDef); - typeHelper.call(vie, ancestors, typeDef.id, typeProps.call(vie, typeDef.id), metadata); - }); - - /* set the namespace to either the old value or the provided baseNS value */ - vie.namespaces.base(baseNSBefore); - }, - -// ### VIE.Util.getEntityTypeUnion(entity) -// This generates a entity-specific VIE type that is a subtype of all the -// types of the entity. This makes it easier to deal with attribute definitions -// specific to an entity because they're merged to a single list. This custom -// type is transient, meaning that it won't be automatilly added to the entity -// or the VIE type registry. - getEntityTypeUnion : function(entity) { - var vie = entity.vie; - return new vie.Type('Union').inherit(entity.get('@type')); - }, - -// ### VIE.Util.getFormSchemaForType(type) -// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms) -// -compatible form schema for any VIE Type. - getFormSchemaForType : function(type, allowNested) { - var schema = {}; - - // Generate a schema - _.each(type.attributes.toArray(), function (attribute) { - var key = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces); - schema[key] = VIE.Util.getFormSchemaForAttribute(attribute); - }); - - // Clean up unknown attribute types - _.each(schema, function (field, id) { - if (!field.type) { - delete schema[id]; - } - - if (field.type === 'URL') { - field.type = 'Text'; - field.dataType = 'url'; - } - - if (field.type === 'List' && !field.listType) { - delete schema[id]; - } - - if (!allowNested) { - if (field.type === 'NestedModel' || field.listType === 'NestedModel') { - delete schema[id]; - } - } - }); - - return schema; - }, - -/// ### VIE.Util.getFormSchemaForAttribute(attribute) - getFormSchemaForAttribute : function(attribute) { - var primaryType = attribute.range[0]; - var schema = {}; - - var getWidgetForType = function (type) { - switch (type) { - case 'xsd:anySimpleType': - case 'xsd:float': - case 'xsd:integer': - return 'Number'; - case 'xsd:string': - return 'Text'; - case 'xsd:date': - return 'Date'; - case 'xsd:dateTime': - return 'DateTime'; - case 'xsd:boolean': - return 'Checkbox'; - case 'xsd:anyURI': - return 'URL'; - default: - var typeType = attribute.vie.types.get(type); - if (!typeType) { - return null; - } - if (typeType.attributes.get('value')) { - // Convert to proper xsd type - return getWidgetForType(typeType.attributes.get('value').range[0]); - } - return 'NestedModel'; - } - }; - - // TODO: Generate a nicer label - schema.title = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces); - - // TODO: Handle attributes linking to other VIE entities - - if (attribute.min > 0) { - schema.validators = ['required']; - } - - if (attribute.max > 1) { - schema.type = 'List'; - schema.listType = getWidgetForType(primaryType); - if (schema.listType === 'NestedModel') { - schema.nestedModelType = primaryType; - } - return schema; - } - - schema.type = getWidgetForType(primaryType); - if (schema.type === 'NestedModel') { - schema.nestedModelType = primaryType; - } - return schema; - }, - -// ### VIE.Util.getFormSchema(entity) -// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms) -// -compatible form schema for any VIE Entity. The form schema creation -// utilizes type information attached to the entity. -// **Parameters**: -// *{```Entity```}* **entity** An instance of VIE ```Entity```. -// **Throws**: -// *nothing*.. -// **Returns**: -// *{object}* a JavaScript object representation of the form schema - getFormSchema : function(entity) { - if (!entity || !entity.isEntity) { - return {}; - } - - var unionType = VIE.Util.getEntityTypeUnion(entity); - var schema = VIE.Util.getFormSchemaForType(unionType, true); - - // Handle nested models - _.each(schema, function (property, id) { - if (property.type !== 'NestedModel' && property.listType !== 'NestedModel') { - return; - } - schema[id].model = entity.vie.getTypedEntityClass(property.nestedModelType); - }); - - return schema; - }, - -// ### VIE.Util.xsdDateTime(date) -// This transforms a ```Date``` instance into an xsd:DateTime format. -// **Parameters**: -// *{```Date```}* **date** An instance of a javascript ```Date```. -// **Throws**: -// *nothing*.. -// **Returns**: -// *{string}* A string representation of the dateTime in the xsd:dateTime format. - xsdDateTime : function(date) { - function pad(n) { - var s = n.toString(); - return s.length < 2 ? '0'+s : s; - } - - var yyyy = date.getFullYear(); - var mm1 = pad(date.getMonth()+1); - var dd = pad(date.getDate()); - var hh = pad(date.getHours()); - var mm2 = pad(date.getMinutes()); - var ss = pad(date.getSeconds()); - - return yyyy +'-' +mm1 +'-' +dd +'T' +hh +':' +mm2 +':' +ss; - }, - -// ### VIE.Util.extractLanguageString(entity, attrs, langs) -// This method extracts a literal string from an entity, searching through the given attributes and languages. -// **Parameters**: -// *{```VIE.Entity```}* **entity** An instance of a VIE.Entity. -// *{```array|string```}* **attrs** Either a string or an array of possible attributes. -// *{```array|string```}* **langs** Either a string or an array of possible languages. -// **Throws**: -// *nothing*.. -// **Returns**: -// *{string|undefined}* The string that was found at the attribute with the wanted language, undefined if nothing could be found. -// **Example usage**: -// -// var attrs = ["name", "rdfs:label"]; -// var langs = ["en", "de"]; -// VIE.Util.extractLanguageString(someEntity, attrs, langs); // "Barack Obama"; - extractLanguageString : function(entity, attrs, langs) { - var p, attr, name, i, n; - if (entity && typeof entity !== "string") { - attrs = (_.isArray(attrs))? attrs : [ attrs ]; - langs = (_.isArray(langs))? langs : [ langs ]; - for (p = 0; p < attrs.length; p++) { - for (var l = 0; l < langs.length; l++) { - var lang = langs[l]; - attr = attrs[p]; - if (entity.has(attr)) { - name = entity.get(attr); - name = (_.isArray(name))? name : [ name ]; - for (i = 0; i < name.length; i++) { - n = name[i]; - if (n.isEntity) { - n = VIE.Util.extractLanguageString(n, attrs, lang); - } else if (typeof n === "string") { - n = n; - } else { - n = ""; - } - if (n && n.indexOf('@' + lang) > -1) { - return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim(); - } - } - } - } - } - /* let's do this again in case we haven't found a name but are dealing with - broken data where no language is given */ - for (p = 0; p < attrs.length; p++) { - attr = attrs[p]; - if (entity.has(attr)) { - name = entity.get(attr); - name = (_.isArray(name))? name : [ name ]; - for (i = 0; i < name.length; i++) { - n = name[i]; - if (n.isEntity) { - n = VIE.Util.extractLanguageString(n, attrs, []); - } - if (n && (typeof n === "string") && n.indexOf('@') === -1) { - return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim(); - } - } - } - } - } - return undefined; - }, - -// ### VIE.Util.transformationRules(service) -// This returns a default set of rdfQuery rules that transform semantic data into the -// VIE entity types. -// **Parameters**: -// *{object}* **service** An instance of a vie.service. -// **Throws**: -// *nothing*.. -// **Returns**: -// *{array}* An array of rules with 'left' and 'right' side. - transformationRules : function (service) { - var res = [ - // rule(s) to transform a dbpedia:Person into a VIE:Person - { - 'left' : [ - '?subject a dbpedia:Person', - '?subject rdfs:label ?label' - ], - 'right': function(ns){ - return function(){ - return [ - jQuery.rdf.triple(this.subject.toString(), - 'a', - '<' + ns.base() + 'Person>', { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'name>', - this.label, { - namespaces: ns.toObj() - }) - ]; - }; - }(service.vie.namespaces) - }, - // rule(s) to transform a foaf:Person into a VIE:Person - { - 'left' : [ - '?subject a foaf:Person', - '?subject rdfs:label ?label' - ], - 'right': function(ns){ - return function(){ - return [ - jQuery.rdf.triple(this.subject.toString(), - 'a', - '<' + ns.base() + 'Person>', { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'name>', - this.label, { - namespaces: ns.toObj() - }) - ]; - }; - }(service.vie.namespaces) - }, - // rule(s) to transform a dbpedia:Place into a VIE:Place - { - 'left' : [ - '?subject a dbpedia:Place', - '?subject rdfs:label ?label' - ], - 'right': function(ns) { - return function() { - return [ - jQuery.rdf.triple(this.subject.toString(), - 'a', - '<' + ns.base() + 'Place>', { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'name>', - this.label.toString(), { - namespaces: ns.toObj() - }) - ]; - }; - }(service.vie.namespaces) - }, - // rule(s) to transform a dbpedia:City into a VIE:City - { - 'left' : [ - '?subject a dbpedia:City', - '?subject rdfs:label ?label', - '?subject dbpedia:abstract ?abs', - '?subject dbpedia:country ?country' - ], - 'right': function(ns) { - return function() { - return [ - jQuery.rdf.triple(this.subject.toString(), - 'a', - '<' + ns.base() + 'City>', { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'name>', - this.label.toString(), { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'description>', - this.abs.toString(), { - namespaces: ns.toObj() - }), - jQuery.rdf.triple(this.subject.toString(), - '<' + ns.base() + 'containedIn>', - this.country.toString(), { - namespaces: ns.toObj() - }) - ]; - }; - }(service.vie.namespaces) - } - ]; - return res; - }, - - getAdditionalRules : function (service) { - - var mapping = { - Work : "CreativeWork", - Film : "Movie", - TelevisionEpisode : "TVEpisode", - TelevisionShow : "TVSeries", // not listed as equivalent class on dbpedia.org - Website : "WebPage", - Painting : "Painting", - Sculpture : "Sculpture", - - Event : "Event", - SportsEvent : "SportsEvent", - MusicFestival : "Festival", - FilmFestival : "Festival", - - Place : "Place", - Continent : "Continent", - Country : "Country", - City : "City", - Airport : "Airport", - Station : "TrainStation", // not listed as equivalent class on dbpedia.org - Hospital : "GovernmentBuilding", - Mountain : "Mountain", - BodyOfWater : "BodyOfWater", - - Company : "Organization", - Person : "Person" - }; - - var additionalRules = []; - _.each(mapping, function (map, key) { - var tripple = { - 'left' : [ '?subject a dbpedia:' + key, '?subject rdfs:label ?label' ], - 'right' : function(ns) { - return function() { - return [ jQuery.rdf.triple(this.subject.toString(), 'a', '<' + ns.base() + map + '>', { - namespaces : ns.toObj() - }), jQuery.rdf.triple(this.subject.toString(), '<' + ns.base() + 'name>', this.label.toString(), { - namespaces : ns.toObj() - }) ]; - }; - }(service.vie.namespaces) - }; - additionalRules.push(tripple); - }); - return additionalRules; - } -}; -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ - -// ## VIE Entities -// -// In VIE there are two low-level model types for storing data. -// **Collections** and **Entities**. Considering `var v = new VIE();` a VIE instance, -// `v.entities` is a Collection with `VIE Entity` objects in it. -// VIE internally uses JSON-LD to store entities. -// -// Each Entity has a few special attributes starting with an `@`. VIE has an API -// for correctly using these attributes, so in order to stay compatible with later -// versions of the library, possibly using a later version of JSON-LD, use the API -// to interact with your entities. -// -// * `@subject` stands for the identifier of the entity. Use `e.getSubject()` -// * `@type` stores the explicit entity types. VIE internally handles Type hierarchy, -// which basically enables to define subtypes and supertypes. Every entity has -// the type 'owl:Thing'. Read more about Types in VIE.Type. -// * `@context` stores namespace definitions used in the entity. Read more about -// Namespaces in VIE Namespaces. -VIE.prototype.Entity = function(attrs, opts) { - - attrs = (attrs)? attrs : {}; - opts = (opts)? opts : {}; - - var self = this; - - if (attrs['@type'] !== undefined) { - attrs['@type'] = (_.isArray(attrs['@type']))? attrs['@type'] : [ attrs['@type'] ]; - attrs['@type'] = _.map(attrs['@type'], function(val){ - if (!self.vie.types.get(val)) { - //if there is no such type -> add it and let it inherit from "owl:Thing" - self.vie.types.add(val).inherit("owl:Thing"); - } - return self.vie.types.get(val).id; - }); - attrs['@type'] = (attrs['@type'].length === 1)? attrs['@type'][0] : attrs['@type']; - } else { - // provide "owl:Thing" as the default type if none was given - attrs['@type'] = self.vie.types.get("owl:Thing").id; - } - - //the following provides full seamless namespace support - //for attributes. It should not matter, if you - //query for `model.get('name')` or `model.get('foaf:name')` - //or even `model.get('http://xmlns.com/foaf/0.1/name');` - //However, if we just overwrite `set()` and `get()`, this - //raises a lot of side effects, so we need to expand - //the attributes before we create the model. - _.each (attrs, function (value, key) { - var newKey = VIE.Util.mapAttributeNS(key, this.namespaces); - if (key !== newKey) { - delete attrs[key]; - attrs[newKey] = value; - } - }, self.vie); - - var Model = Backbone.Model.extend({ - idAttribute: '@subject', - - initialize: function(attributes, options) { - if (attributes['@subject']) { - this.id = this['@subject'] = this.toReference(attributes['@subject']); - } else { - this.id = this['@subject'] = attributes['@subject'] = this.cid.replace('c', '_:bnode'); - } - return this; - }, - - schema: function() { - return VIE.Util.getFormSchema(this); - }, - - // ### Getter, Has, Setter - // #### `.get(attr)` - // To be able to communicate to a VIE Entity you can use a simple get(property) - // command as in `entity.get('rdfs:label')` which will give you one or more literals. - // If the property points to a collection, its entities can be browsed further. - get: function (attr) { - attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); - var value = Backbone.Model.prototype.get.call(this, attr); - value = (_.isArray(value))? value : [ value ]; - - value = _.map(value, function(v) { - if (v !== undefined && attr === '@type' && self.vie.types.get(v)) { - return self.vie.types.get(v); - } else if (v !== undefined && self.vie.entities.get(v)) { - return self.vie.entities.get(v); - } else { - return v; - } - }, this); - if(value.length === 0) { - return undefined; - } - // if there is only one element, just return that one - value = (value.length === 1)? value[0] : value; - return value; - }, - - // #### `.has(attr)` - // Sometimes you'd like to determine if a specific attribute is set - // in an entity. For this reason you can call for example `person.has('friend')` - // to determine if a person entity has friends. - has: function(attr) { - attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); - return Backbone.Model.prototype.has.call(this, attr); - }, - - // #### `.set(attrName, value, opts)`, - // The `options` parameter always refers to a `Backbone.Model.set` `options` object. - // - // **`.set(attributes, options)`** is the most universal way of calling the - // `.set` method. In this case the `attributes` object is a map of all - // attributes to be changed. - set : function(attrs, options, opts) { - if (!attrs) { - return this; - } - - if (attrs['@subject']) { - attrs['@subject'] = this.toReference(attrs['@subject']); - } - - // Use **`.set(attrName, value, options)`** for setting or changing exactly one - // entity attribute. - if (typeof attrs === "string") { - var obj = {}; - obj[attrs] = options; - return this.set(obj, opts); - } - // **`.set(entity)`**: In case you'd pass a VIE entity, - // the passed entities attributes are being set for the entity. - if (attrs.attributes) { - attrs = attrs.attributes; - } - var self = this; - var coll; - // resolve shortened URIs like rdfs:label.. - _.each (attrs, function (value, key) { - var newKey = VIE.Util.mapAttributeNS(key, self.vie.namespaces); - if (key !== newKey) { - delete attrs[key]; - attrs[newKey] = value; - } - }, this); - // Finally iterate through the *attributes* to be set and prepare - // them for the Backbone.Model.set method. - _.each (attrs, function (value, key) { - if (!value) { return; } - if (key.indexOf('@') === -1) { - if (value.isCollection) { - // ignore - value.each(function (child) { - self.vie.entities.addOrUpdate(child); - }); - } else if (value.isEntity) { - self.vie.entities.addOrUpdate(value); - coll = new self.vie.Collection(value, { - vie: self.vie, - predicate: key - }); - attrs[key] = coll; - } else if (_.isArray(value)) { - if (this.attributes[key] && this.attributes[key].isCollection) { - var newEntities = this.attributes[key].addOrUpdate(value); - attrs[key] = this.attributes[key]; - attrs[key].reset(newEntities); - } - } else if (value["@value"]) { - // The value is a literal object, ignore - } else if (_.isObject(value) && !_.isDate(value)) { - // The value is another VIE Entity - var child = new self.vie.Entity(value, options); - // which is being stored in `v.entities` - self.vie.entities.addOrUpdate(child); - // and set as VIE Collection attribute on the original entity - coll = new self.vie.Collection(value, { - vie: self.vie, - predicate: key - }); - attrs[key] = coll; - } else { - // ignore - } - } - }, this); - return Backbone.Model.prototype.set.call(this, attrs, options); - }, - - // **`.unset(attr, opts)` ** removes an attribute from the entity. - unset: function (attr, opts) { - attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); - return Backbone.Model.prototype.unset.call(this, attr, opts); - }, - - // Validation based on type rules. - // - // There are two ways to skip validation for entity operations: - // - // * `options.silent = true` - // * `options.validate = false` - validate: function (attrs, opts) { - if (opts && opts.validate === false) { - return; - } - var types = this.get('@type'); - if (_.isArray(types)) { - var results = []; - _.each(types, function (type) { - var res = this.validateByType(type, attrs, opts); - if (res) { - results.push(res); - } - }, this); - if (_.isEmpty(results)) { - return; - } - return _.flatten(results); - } - - return this.validateByType(types, attrs, opts); - }, - - validateByType: function (type, attrs, opts) { - var messages = { - max: '<%= property %> cannot contain more than <%= num %> items', - min: '<%= property %> must contain at least <%= num %> items', - required: '<%= property %> is required' - }; - - if (!type.attributes) { - return; - } - - var toError = function (definition, constraint, messageValues) { - return { - property: definition.id, - constraint: constraint, - message: _.template(messages[constraint], _.extend({ - property: definition.id - }, messageValues)) - }; - }; - - var checkMin = function (definition, attrs) { - if (!attrs[definition.id] || _.isEmpty(attrs[definition.id])) { - return toError(definition, 'required', {}); - } - }; - - // Check the number of items in attr against max - var checkMax = function (definition, attrs) { - if (!attrs[definition.id]) { - return; - } - - if (!attrs[definition.id].isCollection && !_.isArray(attrs[definition.id])) { - return; - } - - if (attrs[definition.id].length > definition.max) { - return toError(definition, 'max', { - num: definition.max - }); - } - }; - - var results = []; - _.each(type.attributes.list(), function (definition) { - var res; - if (definition.max && definition.max != -1) { - res = checkMax(definition, attrs); - if (res) { - results.push(res); - } - } - - if (definition.min && definition.min > 0) { - res = checkMin(definition, attrs); - if (res) { - results.push(res); - } - } - }); - - if (_.isEmpty(results)) { - return; - } - return results; - }, - - isNew: function() { - if (this.getSubjectUri().substr(0, 7) === '_:bnode') { - return true; - } - return false; - }, - - hasChanged: function(attr) { - if (this.markedChanged) { - return true; - } - - return Backbone.Model.prototype.hasChanged.call(this, attr); - }, - - // Force hasChanged to return true - forceChanged: function(changed) { - this.markedChanged = changed ? true : false; - }, - - // **`getSubject()`** is the getter for the entity identifier. - getSubject: function(){ - if (typeof this.id === "undefined") { - this.id = this.attributes[this.idAttribute]; - } - if (typeof this.id === 'string') { - if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') { - return this.toReference(this.id); - } - return this.id; - } - return this.cid.replace('c', '_:bnode'); - }, - - // TODO describe - getSubjectUri: function(){ - return this.fromReference(this.getSubject()); - }, - - isReference: function(uri){ - var matcher = new RegExp("^\\<([^\\>]*)\\>$"); - if (matcher.exec(uri)) { - return true; - } - return false; - }, - - toReference: function(uri){ - if (_.isArray(uri)) { - var self = this; - return _.map(uri, function(part) { - return self.toReference(part); - }); - } - var ns = this.vie.namespaces; - var ret = uri; - if (uri.substring(0, 2) === "_:") { - ret = uri; - } - else if (ns.isCurie(uri)) { - ret = ns.uri(uri); - if (ret === "<" + ns.base() + uri + ">") { - /* no base namespace extension with IDs */ - ret = '<' + uri + '>'; - } - } else if (!ns.isUri(uri)) { - ret = '<' + uri + '>'; - } - return ret; - }, - - fromReference: function(uri){ - var ns = this.vie.namespaces; - if (!ns.isUri(uri)) { - return uri; - } - return uri.substring(1, uri.length - 1); - }, - - as: function(encoding){ - if (encoding === "JSON") { - return this.toJSON(); - } - if (encoding === "JSONLD") { - return this.toJSONLD(); - } - throw new Error("Unknown encoding " + encoding); - }, - - toJSONLD: function(){ - var instanceLD = {}; - var instance = this; - _.each(instance.attributes, function(value, name){ - var entityValue = value; //instance.get(name); - - if (value instanceof instance.vie.Collection) { - entityValue = value.map(function(instance) { - return instance.getSubject(); - }); - } - - // TODO: Handle collections separately - instanceLD[name] = entityValue; - }); - - instanceLD['@subject'] = instance.getSubject(); - - return instanceLD; - }, - - // **`.setOrAdd(arg1, arg2)`** similar to `.set(..)`, `.setOrAdd(..)` can - // be used for setting one or more attributes of an entity, but in - // this case it's a collection of values, not just one. That means, if the - // entity already has the attribute set, make the value to a VIE Collection - // and use the collection as value. The collection can contain entities - // or literals, but not both at the same time. - setOrAdd: function (arg1, arg2, option) { - var entity = this; - if (typeof arg1 === "string" && arg2) { - // calling entity.setOrAdd("rdfs:type", "example:Musician") - entity._setOrAddOne(arg1, arg2, option); - } - else - if (typeof arg1 === "object") { - // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...}) - _(arg1).each(function(val, key){ - entity._setOrAddOne(key, val, arg2); - }); - } - return this; - }, - - - /* attr is always of type string */ - /* value can be of type: string,int,double,object,VIE.Entity,VIE.Collection */ - /* val can be of type: undefined,string,int,double,array,VIE.Collection */ - - /* depending on the type of value and the type of val, different actions need to be made */ - _setOrAddOne: function (attr, value, options) { - if (!attr || !value) - return; - options = (options)? options : {}; - var v; - - attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); - - if (_.isArray(value)) { - for (v = 0; v < value.length; v++) { - this._setOrAddOne(attr, value[v], options); - } - return; - } - - if (attr === "@type" && value instanceof self.vie.Type) { - value = value.id; - } - - var obj = {}; - var existing = Backbone.Model.prototype.get.call(this, attr); - - if (!existing) { - obj[attr] = value; - this.set(obj, options); - } else if (existing.isCollection) { - if (value.isCollection) { - value.each(function (model) { - existing.add(model); - }); - } else if (value.isEntity) { - existing.add(value); - } else if (typeof value === "object") { - value = new this.vie.Entity(value); - existing.add(value); - } else { - throw new Error("you cannot add a literal to a collection of entities!"); - } - this.trigger('change:' + attr, this, value, {}); - this.change({}); - } else if (_.isArray(existing)) { - if (value.isCollection) { - for (v = 0; v < value.size(); v++) { - this._setOrAddOne(attr, value.at(v).getSubject(), options); - } - } else if (value.isEntity) { - this._setOrAddOne(attr, value.getSubject(), options); - } else if (typeof value === "object") { - value = new this.vie.Entity(value); - this._setOrAddOne(attr, value, options); - } else { - /* yes, we (have to) allow multiple equal values */ - existing.push(value); - obj[attr] = existing; - this.set(obj); - } - } else { - var arr = [ existing ]; - arr.push(value); - obj[attr] = arr; - return this.set(obj, options); - } - }, - - // **`.hasType(type)`** determines if the entity has the explicit `type` set. - hasType: function(type){ - type = self.vie.types.get(type); - return this.hasPropertyValue("@type", type); - }, - - // TODO describe - hasPropertyValue: function(property, value) { - var t = this.get(property); - if (!(value instanceof Object)) { - value = self.vie.entities.get(value); - } - if (t instanceof Array) { - return t.indexOf(value) !== -1; - } - else { - return t === value; - } - }, - - // **`.isof(type)`** determines if the entity is of `type` by explicit or implicit - // declaration. E.g. if Employee is a subtype of Person and e Entity has - // explicitly set type Employee, e.isof(Person) will evaluate to true. - isof: function (type) { - var types = this.get('@type'); - - if (types === undefined) { - return false; - } - types = (_.isArray(types))? types : [ types ]; - - type = (self.vie.types.get(type))? self.vie.types.get(type) : new self.vie.Type(type); - for (var t = 0; t < types.length; t++) { - if (self.vie.types.get(types[t])) { - if (self.vie.types.get(types[t]).isof(type)) { - return true; - } - } else { - var typeTmp = new self.vie.Type(types[t]); - if (typeTmp.id === type.id) { - return true; - } - } - } - return false; - }, - // TODO describe - addTo : function (collection, update) { - var self = this; - if (collection instanceof self.vie.Collection) { - if (update) { - collection.addOrUpdate(self); - } else { - collection.add(self); - } - return this; - } - throw new Error("Please provide a proper collection of type VIE.Collection as argument!"); - }, - - isEntity: true, - - vie: self.vie - }); - - return new Model(attrs, opts); -}; -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ -VIE.prototype.Collection = Backbone.Collection.extend({ - model: VIE.prototype.Entity, - - initialize: function (models, options) { - if (!options || !options.vie) { - throw new Error('Each collection needs a VIE reference'); - } - this.vie = options.vie; - this.predicate = options.predicate; - }, - - canAdd: function (type) { - return true; - }, - - get: function(id) { - if (id === null) { - return null; - } - - id = (id.getSubject)? id.getSubject() : id; - if (typeof id === "string" && id.indexOf("_:") === 0) { - if (id.indexOf("bnode") === 2) { - //bnode! - id = id.replace("_:bnode", 'c'); - return this._byCid[id]; - } else { - return this._byId["<" + id + ">"]; - } - } else { - id = this.toReference(id); - return this._byId[id]; - } - }, - - addOrUpdate: function(model, options) { - options = options || {}; - - var collection = this; - var existing; - if (_.isArray(model)) { - var entities = []; - _.each(model, function(item) { - entities.push(collection.addOrUpdate(item, options)); - }); - return entities; - } - - if (model === undefined) { - throw new Error("No model given"); - } - - if (_.isString(model)) { - model = { - '@subject': model, - id: model - }; - } - - if (!model.isEntity) { - model = new this.model(model); - } - - if (model.id && this.get(model.id)) { - existing = this.get(model.id); - } - if (this.getByCid(model.cid)) { - existing = this.getByCid(model.cid); - } - if (existing) { - var newAttribs = {}; - _.each(model.attributes, function(value, attribute) { - if (!existing.has(attribute)) { - newAttribs[attribute] = value; - return true; - } - - if (attribute === '@subject') { - if (model.isNew() && !existing.isNew()) { - // Save order issue, skip - return true; - } - } - - if (existing.get(attribute) === value) { - return true; - } - //merge existing attribute values with new ones! - //not just overwrite 'em!! - var oldVals = existing.attributes[attribute]; - var newVals = value; - if (oldVals instanceof collection.vie.Collection) { - // TODO: Merge collections - return true; - } - if (options.overrideAttributes) { - newAttribs[attribute] = value; - return true; - } - if (attribute === '@context') { - newAttribs[attribute] = jQuery.extend(true, {}, oldVals, newVals); - } else { - oldVals = (jQuery.isArray(oldVals))? oldVals : [ oldVals ]; - newVals = (jQuery.isArray(newVals))? newVals : [ newVals ]; - newAttribs[attribute] = _.uniq(oldVals.concat(newVals)); - newAttribs[attribute] = (newAttribs[attribute].length === 1)? newAttribs[attribute][0] : newAttribs[attribute]; - } - }); - - if (!_.isEmpty(newAttribs)) { - existing.set(newAttribs, options.updateOptions); - } - return existing; - } - this.add(model, options.addOptions); - return model; - }, - - isReference: function(uri){ - var matcher = new RegExp("^\\<([^\\>]*)\\>$"); - if (matcher.exec(uri)) { - return true; - } - return false; - }, - - toReference: function(uri){ - if (this.isReference(uri)) { - return uri; - } - return '<' + uri + '>'; - }, - - fromReference: function(uri){ - if (!this.isReference(uri)) { - return uri; - } - return uri.substring(1, uri.length - 1); - }, - - isCollection: true -}); -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ -// - -// ## VIE.Types -// Within VIE, we provide special capabilities of handling types of entites. This helps -// for example to query easily for certain entities (e.g., you only need to query for *Person*s -// and not for all subtypes). -if (VIE.prototype.Type) { - throw new Error("ERROR: VIE.Type is already defined. Please check your installation!"); -} -if (VIE.prototype.Types) { - throw new Error("ERROR: VIE.Types is already defined. Please check your installation!"); -} - -// ### VIE.Type(id, attrs, metadata) -// This is the constructor of a VIE.Type. -// **Parameters**: -// *{string}* **id** The id of the type. -// *{string|array|VIE.Attribute}* **attrs** A string, proper ```VIE.Attribute``` or an array of these which -// *{object}* **metadata** Possible metadata about the type -// are the possible attributes of the type -// **Throws**: -// *{Error}* if one of the given paramenters is missing. -// **Returns**: -// *{VIE.Type}* : A **new** VIE.Type object. -// **Example usage**: -// -// var person = new vie.Type("Person", ["name", "knows"]); -VIE.prototype.Type = function (id, attrs, metadata) { - if (id === undefined || typeof id !== 'string') { - throw "The type constructor needs an 'id' of type string! E.g., 'Person'"; - } - -// ### id -// This field stores the id of the type's instance. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{string}* : The id of the type as a URI. -// **Example usage**: -// -// console.log(person.id); -// // --> "" - this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); - - /* checks whether such a type is already defined. */ - if (this.vie.types.get(this.id)) { - throw new Error("The type " + this.id + " is already defined!"); - } - -// ### supertypes -// This field stores all parent types of the type's instance. This -// is set if the current type inherits from another type. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{VIE.Types}* : The supertypes (parents) of the type. -// **Example usage**: -// -// console.log(person.supertypes); - this.supertypes = new this.vie.Types(); - -// ### subtypes -// This field stores all children types of the type's instance. This -// will be set if another type inherits from the current type. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{VIE.Types}* : The subtypes (parents) of the type. -// **Example usage**: -// -// console.log(person.subtypes); - this.subtypes = new this.vie.Types(); - -// ### attributes -// This field stores all attributes of the type's instance as -// a proper ```VIE.Attributes``` class. (see also VIE.Attributes) -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{VIE.Attributes}* : The attributes of the type. -// **Example usage**: -// -// console.log(person.attributes); - this.attributes = new this.vie.Attributes(this, (attrs)? attrs : []); - -// ### metadata -// This field stores possible additional information about the type, like -// a human-readable label. - this.metadata = metadata ? metadata : {}; - -// ### isof(type) -// This method checks whether the current type is a child of the given type. -// **Parameters**: -// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked. -// **Throws**: -// *{Error}* If the type is not valid. -// **Returns**: -// *{boolean}* : ```true``` if the current type inherits from the type, ```false``` otherwise. -// **Example usage**: -// -// console.log(person.isof("owl:Thing")); -// // <-- true - this.isof = function (type) { - type = this.vie.types.get(type); - if (type) { - return type.subsumes(this.id); - } else { - throw new Error("No valid type given"); - } - }; - -// ### subsumes(type) -// This method checks whether the current type is a parent of the given type. -// **Parameters**: -// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked. -// **Throws**: -// *{Error}* If the type is not valid. -// **Returns**: -// *{boolean}* : ```true``` if the current type is a parent of the type, ```false``` otherwise. -// **Example usage**: -// -// var x = new vie.Type(...); -// var y = new vie.Type(...).inherit(x); -// y.isof(x) === x.subsumes(y); - this.subsumes = function (type) { - type = this.vie.types.get(type); - if (type) { - if (this.id === type.id) { - return true; - } - var subtypes = this.subtypes.list(); - for (var c = 0; c < subtypes.length; c++) { - var childObj = subtypes[c]; - if (childObj) { - if (childObj.id === type.id || childObj.subsumes(type)) { - return true; - } - } - } - return false; - } else { - throw new Error("No valid type given"); - } - }; - -// ### inherit(supertype) -// This method invokes inheritance throught the types. This adds the current type to the -// subtypes of the supertype and vice versa. -// **Parameters**: -// *{string|VIE.Type|array}* **supertype** The type to be inherited from. If this is an array -// the inherit method is called sequentially on all types. -// **Throws**: -// *{Error}* If the type is not valid. -// **Returns**: -// *{VIE.Type}* : The instance itself. -// **Example usage**: -// -// var x = new vie.Type(...); -// var y = new vie.Type(...).inherit(x); -// y.isof(x) // <-- true - this.inherit = function (supertype) { - if (typeof supertype === "string") { - this.inherit(this.vie.types.get(supertype)); - } - else if (supertype instanceof this.vie.Type) { - supertype.subtypes.addOrOverwrite(this); - this.supertypes.addOrOverwrite(supertype); - try { - /* only for validation of attribute-inheritance! - if this throws an error (inheriting two attributes - that cannot be combined) we reverse all changes. */ - this.attributes.list(); - } catch (e) { - supertype.subtypes.remove(this); - this.supertypes.remove(supertype); - throw e; - } - } else if (jQuery.isArray(supertype)) { - for (var i = 0, slen = supertype.length; i < slen; i++) { - this.inherit(supertype[i]); - } - } else { - throw new Error("Wrong argument in VIE.Type.inherit()"); - } - return this; - }; - -// ### hierarchy() -// This method serializes the hierarchy of child types into an object. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{object}* : The hierachy of child types as an object. -// **Example usage**: -// -// var x = new vie.Type(...); -// var y = new vie.Type(...).inherit(x); -// x.hierarchy(); - this.hierarchy = function () { - var obj = {id : this.id, subtypes: []}; - var list = this.subtypes.list(); - for (var c = 0, llen = list.length; c < llen; c++) { - var childObj = this.vie.types.get(list[c]); - obj.subtypes.push(childObj.hierarchy()); - } - return obj; - }; - -// ### instance() -// This method creates a ```VIE.Entity``` instance from this type. -// **Parameters**: -// *{object}* **attrs** see constructor of VIE.Entity -// *{object}* **opts** see constructor of VIE.Entity -// **Throws**: -// *{Error}* if the instance could not be built -// **Returns**: -// *{VIE.Entity}* : A **new** instance of a ```VIE.Entity``` with the current type. -// **Example usage**: -// -// var person = new vie.Type("person"); -// var sebastian = person.instance( -// {"@subject" : "#me", -// "name" : "Sebastian"}); -// console.log(sebastian.get("name")); // <-- "Sebastian" - this.instance = function (attrs, opts) { - attrs = (attrs)? attrs : {}; - opts = (opts)? opts : {}; - - /* turn type/attribute checking on by default! */ - if (opts.typeChecking !== false) { - for (var a in attrs) { - if (a.indexOf('@') !== 0 && !this.attributes.get(a)) { - throw new Error("Cannot create an instance of " + this.id + " as the type does not allow an attribute '" + a + "'!"); - } - } - } - - if (attrs['@type']) { - attrs['@type'].push(this.id); - } else { - attrs['@type'] = this.id; - } - - return new this.vie.Entity(attrs, opts); - }; - -// ### toString() -// This method returns the id of the type. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{string}* : The id of the type. -// **Example usage**: -// -// var x = new vie.Type(...); -// x.toString() === x.id; - this.toString = function () { - return this.id; - }; -}; - -// ### VIE.Types() -// This is the constructor of a VIE.Types. This is a convenience class -// to store ```VIE.Type``` instances properly. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Types}* : A **new** VIE.Types object. -// **Example usage**: -// -// var types = new vie.Types(); -VIE.prototype.Types = function () { - - this._types = {}; - -// ### add(id, attrs, metadata) -// This method adds a `VIE.Type` to the types. -// **Parameters**: -// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added. -// *{string|object}* **attrs** Only used if ```id``` is a string. -// *{object}* **metadata** potential additional metadata about the type. -// **Throws**: -// *{Error}* if a type with the given id already exists a ```VIE.Entity``` instance from this type. -// **Returns**: -// *{VIE.Types}* : The instance itself. -// **Example usage**: -// -// var types = new vie.Types(); -// types.add("Person", ["name", "knows"]); - this.add = function (id, attrs, metadata) { - if (_.isArray(id)) { - _.each(id, function (type) { - this.add(type); - }, this); - return this; - } - - if (this.get(id)) { - throw new Error("Type '" + id + "' already registered."); - } else { - if (typeof id === "string") { - var t = new this.vie.Type(id, attrs, metadata); - this._types[t.id] = t; - return t; - } else if (id instanceof this.vie.Type) { - this._types[id.id] = id; - return id; - } else { - throw new Error("Wrong argument to VIE.Types.add()!"); - } - } - return this; - }; - -// ### addOrOverwrite(id, attrs) -// This method adds or overwrites a `VIE.Type` to the types. This is the same as -// ``this.remove(id); this.add(id, attrs);`` -// **Parameters**: -// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added. -// *{string|object}* **attrs** Only used if ```id``` is a string. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Types}* : The instance itself. -// **Example usage**: -// -// var types = new vie.Types(); -// types.addOrOverwrite("Person", ["name", "knows"]); - this.addOrOverwrite = function(id, attrs){ - if (this.get(id)) { - this.remove(id); - } - return this.add(id, attrs); - }; - -// ### get(id) -// This method retrieves a `VIE.Type` from the types by it's id. -// **Parameters**: -// *{string|VIE.Type}* **id** The id or the type itself. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Type}* : The instance of the type or ```undefined```. -// **Example usage**: -// -// var types = new vie.Types(); -// types.addOrOverwrite("Person", ["name", "knows"]); -// types.get("Person"); - this.get = function (id) { - if (!id) { - return undefined; - } - if (typeof id === 'string') { - var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); - return this._types[lid]; - } else if (id instanceof this.vie.Type) { - return this.get(id.id); - } - return undefined; - }; - -// ### remove(id) -// This method removes a type of given id from the type. This also -// removes all children if their only parent were this -// type. Furthermore, this removes the link from the -// super- and subtypes. -// **Parameters**: -// *{string|VIE.Type}* **id** The id or the type itself. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Type}* : The removed type. -// **Example usage**: -// -// var types = new vie.Types(); -// types.addOrOverwrite("Person", ["name", "knows"]); -// types.remove("Person"); - this.remove = function (id) { - var t = this.get(id); - /* test whether the type actually exists in VIE - * and prevents removing *owl:Thing*. - */ - if (!t) { - return this; - } - if (!t || t.subsumes("owl:Thing")) { - console.warn("You are not allowed to remove 'owl:Thing'."); - return this; - } - delete this._types[t.id]; - - var subtypes = t.subtypes.list(); - for (var c = 0; c < subtypes.length; c++) { - var childObj = subtypes[c]; - if (childObj.supertypes.list().length === 1) { - /* recursively remove all children - that inherit only from this type */ - this.remove(childObj); - } else { - childObj.supertypes.remove(t.id); - } - } - return t; - }; - -// ### toArray() === list() -// This method returns an array of all types. -// **Parameters**: -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{array}* : An array of ```VIE.Type``` instances. -// **Example usage**: -// -// var types = new vie.Types(); -// types.addOrOverwrite("Person", ["name", "knows"]); -// types.list(); - this.toArray = this.list = function () { - var ret = []; - for (var i in this._types) { - ret.push(this._types[i]); - } - return ret; - }; - -// ### sort(types, desc) -// This method sorts an array of types in their order, given by the -// inheritance. This returns a copy and leaves the original array untouched. -// **Parameters**: -// *{array|VIE.Type}* **types** The array of ```VIE.Type``` instances or ids of types to be sorted. -// *{boolean}* **desc** If 'desc' is given and 'true', the array will be sorted -// in descendant order. -// *nothing* -// **Throws**: -// *nothing* -// **Returns**: -// *{array}* : A sorted copy of the array. -// **Example usage**: -// -// var types = new vie.Types(); -// types.addOrOverwrite("Person", ["name", "knows"]); -// types.sort(types.list(), true); - this.sort = function (types, desc) { - var self = this; - types = (jQuery.isArray(types))? types : [ types ]; - desc = (desc)? true : false; - - if (types.length === 0) return []; - var copy = [ types[0] ]; - var x, tlen; - for (x = 1, tlen = types.length; x < tlen; x++) { - var insert = types[x]; - var insType = self.get(insert); - if (insType) { - for (var y = 0; y < copy.length; y++) { - if (insType.subsumes(copy[y])) { - copy.splice(y,0,insert); - break; - } else if (y === copy.length - 1) { - copy.push(insert); - } - } - } - } - - //unduplicate - for (x = 0; x < copy.length; x++) { - if (copy.lastIndexOf(copy[x]) !== x) { - copy.splice(x, 1); - x--; - } - } - - if (!desc) { - copy.reverse(); - } - return copy; - }; -}; -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ -// - -// ## VIE.Attributes -// Within VIE, we provide special capabilities of handling attributes of types of entites. This -// helps first of all to list all attributes of an entity type, but furthermore fully supports -// inheritance of attributes from the type-class to inherit from. -if (VIE.prototype.Attribute) { - throw new Error("ERROR: VIE.Attribute is already defined. Please check your VIE installation!"); -} -if (VIE.prototype.Attributes) { - throw new Error("ERROR: VIE.Attributes is already defined. Please check your VIE installation!"); -} - -// ### VIE.Attribute(id, range, domain, minCount, maxCount, metadata) -// This is the constructor of a VIE.Attribute. -// **Parameters**: -// *{string}* **id** The id of the attribute. -// *{string|array}* **range** A string or an array of strings of the target range of -// the attribute. -// *{string}* **domain** The domain of the attribute. -// *{number}* **minCount** The minimal number this attribute can occur. (needs to be >= 0) -// *{number}* **maxCount** The maximal number this attribute can occur. (needs to be >= minCount, use `-1` for unlimited) -// *{object}* **metadata** Possible metadata about the attribute -// **Throws**: -// *{Error}* if one of the given paramenters is missing. -// **Returns**: -// *{VIE.Attribute}* : A **new** VIE.Attribute object. -// **Example usage**: -// -// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person", 0, 10); -// // Creates an attribute to describe a *knows*-relationship -// // between persons. Each person can only have -VIE.prototype.Attribute = function (id, range, domain, minCount, maxCount, metadata) { - if (id === undefined || typeof id !== 'string') { - throw new Error("The attribute constructor needs an 'id' of type string! E.g., 'Person'"); - } - if (range === undefined) { - throw new Error("The attribute constructor of " + id + " needs 'range'."); - } - if (domain === undefined) { - throw new Error("The attribute constructor of " + id + " needs a 'domain'."); - } - - this._domain = domain; - -// ### id -// This field stores the id of the attribute's instance. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{string}* : A URI, representing the id of the attribute. -// **Example usage**: -// -// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); -// console.log(knowsAttr.id); -// // --> - this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); - -// ### range -// This field stores the ranges of the attribute's instance. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{array}* : An array of strings which represent the types. -// **Example usage**: -// -// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); -// console.log(knowsAttr.range); -// // --> ["Person"] - this.range = (_.isArray(range))? range : [ range ]; - -// ### min -// This field stores the minimal amount this attribute can occur in the type's instance. The number -// needs to be greater or equal to zero. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{int}* : The minimal amount this attribute can occur. -// **Example usage**: -// -// console.log(person.min); -// // --> 0 - minCount = minCount ? minCount : 0; - this.min = (minCount > 0) ? minCount : 0; - -// ### max -// This field stores the maximal amount this attribute can occur in the type's instance. -// This number cannot be smaller than min -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{int}* : The maximal amount this attribute can occur. -// **Example usage**: -// -// console.log(person.max); -// // --> 1.7976931348623157e+308 - maxCount = maxCount ? maxCount : 1; - if (maxCount === -1) { - maxCount = Number.MAX_VALUE; - } - this.max = (maxCount >= this.min)? maxCount : this.min; - -// ### metadata -// This field holds potential metadata about the attribute. - this.metadata = metadata ? metadata : {}; - -// ### applies(range) -// This method checks, whether the current attribute applies in the given range. -// If ```range``` is a string and cannot be transformed into a ```VIE.Type```, -// this performs only string comparison, if it is a VIE.Type -// or an ID of a VIE.Type, then inheritance is checked as well. -// **Parameters**: -// *{string|VIE.Type}* **range** The ```VIE.Type``` (or it's string representation) to be checked. -// **Throws**: -// nothing -// **Returns**: -// *{boolean}* : ```true``` if the given type applies to this attribute and ```false``` otherwise. -// **Example usage**: -// -// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); -// console.log(knowsAttr.applies("Person")); // --> true -// console.log(knowsAttr.applies("Place")); // --> false - this.applies = function (range) { - if (this.vie.types.get(range)) { - range = this.vie.types.get(range); - } - for (var r = 0, len = this.range.length; r < len; r++) { - var x = this.vie.types.get(this.range[r]); - if (x === undefined && typeof range === "string") { - if (range === this.range[r]) { - return true; - } - } - else { - if (range.isof(this.range[r])) { - return true; - } - } - } - return false; - }; - -}; - -// ## VIE.Attributes(domain, attrs) -// This is the constructor of a VIE.Attributes. Basically a convenience class -// that represents a list of ```VIE.Attribute```. As attributes are part of a -// certain ```VIE.Type```, it needs to be passed for inheritance checks. -// **Parameters**: -// *{string}* **domain** The domain of the attributes (the type they will be part of). -// *{string|VIE.Attribute|array}* **attrs** Either a string representation of an attribute, -// a proper instance of ```VIE.Attribute``` or an array of both. -// *{string}* **domain** The domain of the attribute. -// **Throws**: -// *{Error}* if one of the given paramenters is missing. -// **Returns**: -// *{VIE.Attribute}* : A **new** VIE.Attribute instance. -// **Example usage**: -// -// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); -// var personAttrs = new vie.Attributes("Person", knowsAttr); -VIE.prototype.Attributes = function (domain, attrs) { - - this._local = {}; - this._attributes = {}; - -// ### domain -// This field stores the domain of the attributes' instance. -// **Parameters**: -// nothing -// **Throws**: -// nothing -// **Returns**: -// *{string}* : The string representation of the domain. -// **Example usage**: -// -// console.log(personAttrs.domain); -// // --> ["Person"] - this.domain = domain; - -// ### add(id, range, min, max, metadata) -// This method adds a ```VIE.Attribute``` to the attributes instance. -// **Parameters**: -// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper -// instance of a ```VIE.Attribute```. -// *{string|array}* **range** An array representing the target range of the attribute. -// *{number}* **min** The minimal amount this attribute can appear. -// instance of a ```VIE.Attribute```. -// *{number}* **max** The maximal amount this attribute can appear. -// *{object}* **metadata** Additional metadata for the attribute. -// **Throws**: -// *{Error}* If an atribute with the given id is already registered. -// *{Error}* If the ```id``` parameter is not a string, nor a ```VIE.Type``` instance. -// **Returns**: -// *{VIE.Attribute}* : The generated or passed attribute. -// **Example usage**: -// -// personAttrs.add("name", "Text", 0, 1); - this.add = function (id, range, min, max, metadata) { - if (_.isArray(id)) { - _.each(id, function (attribute) { - this.add(attribute); - }, this); - return this; - } - - if (this.get(id)) { - throw new Error("Attribute '" + id + "' already registered for domain " + this.domain.id + "!"); - } else { - if (typeof id === "string") { - var a = new this.vie.Attribute(id, range, this.domain, min, max, metadata); - this._local[a.id] = a; - return a; - } else if (id instanceof this.vie.Attribute) { - id.domain = this.domain; - id.vie = this.vie; - this._local[id.id] = id; - return id; - } else { - throw new Error("Wrong argument to VIE.Types.add()!"); - } - } - }; - -// ### remove(id) -// This method removes a ```VIE.Attribute``` from the attributes instance. -// **Parameters**: -// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper -// instance of a ```VIE.Attribute```. -// **Throws**: -// *{Error}* When the attribute is inherited from a parent ```VIE.Type``` and thus cannot be removed. -// **Returns**: -// *{VIE.Attribute}* : The removed attribute. -// **Example usage**: -// -// personAttrs.remove("knows"); - this.remove = function (id) { - var a = this.get(id); - if (a.id in this._local) { - delete this._local[a.id]; - return a; - } - throw new Error("The attribute " + id + " is inherited and cannot be removed from the domain " + this.domain.id + "!"); - }; - -// ### get(id) -// This method returns a ```VIE.Attribute``` from the attributes instance by it's id. -// **Parameters**: -// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper -// instance of a ```VIE.Attribute```. -// **Throws**: -// *{Error}* When the method is called with an unknown datatype. -// **Returns**: -// *{VIE.Attribute}* : The attribute. -// **Example usage**: -// -// personAttrs.get("knows"); - this.get = function (id) { - if (typeof id === 'string') { - var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); - return this._inherit()._attributes[lid]; - } else if (id instanceof this.vie.Attribute) { - return this.get(id.id); - } else { - throw new Error("Wrong argument in VIE.Attributes.get()"); - } - }; - -// ### _inherit() -// The private method ```_inherit``` creates a full list of all attributes. This includes -// local attributes as well as inherited attributes from the parents. The ranges of attributes -// with the same id will be merged. This method is called everytime an attribute is requested or -// the list of all attributes. Usually this method should not be invoked outside of the class. -// **Parameters**: -// *nothing* -// instance of a ```VIE.Attribute```. -// **Throws**: -// *nothing* -// **Returns**: -// *nothing* -// **Example usage**: -// -// personAttrs._inherit(); - this._inherit = function () { - var a, x, id; - var attributes = jQuery.extend(true, {}, this._local); - - var inherited = _.map(this.domain.supertypes.list(), - function (x) { - return x.attributes; - } - ); - - var add = {}; - var merge = {}; - var ilen, alen; - for (a = 0, ilen = inherited.length; a < ilen; a++) { - var attrs = inherited[a].list(); - for (x = 0, alen = attrs.length; x < alen; x++) { - id = attrs[x].id; - if (!(id in attributes)) { - if (!(id in add) && !(id in merge)) { - add[id] = attrs[x]; - } - else { - if (!merge[id]) { - merge[id] = {range : [], mins : [], maxs: [], metadatas: []}; - } - if (id in add) { - merge[id].range = jQuery.merge(merge[id].range, add[id].range); - merge[id].mins = jQuery.merge(merge[id].mins, [ add[id].min ]); - merge[id].maxs = jQuery.merge(merge[id].maxs, [ add[id].max ]); - merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ add[id].metadata ]); - delete add[id]; - } - merge[id].range = jQuery.merge(merge[id].range, attrs[x].range); - merge[id].mins = jQuery.merge(merge[id].mins, [ attrs[x].min ]); - merge[id].maxs = jQuery.merge(merge[id].maxs, [ attrs[x].max ]); - merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ attrs[x].metadata ]); - merge[id].range = _.uniq(merge[id].range); - merge[id].mins = _.uniq(merge[id].mins); - merge[id].maxs = _.uniq(merge[id].maxs); - merge[id].metadatas = _.uniq(merge[id].metadatas); - } - } - } - } - - /* adds inherited attributes that do not need to be merged */ - jQuery.extend(attributes, add); - - /* merges inherited attributes */ - for (id in merge) { - var mranges = merge[id].range; - var mins = merge[id].mins; - var maxs = merge[id].maxs; - var metadatas = merge[id].metadatas; - var ranges = []; - //merging ranges - for (var r = 0, mlen = mranges.length; r < mlen; r++) { - var p = this.vie.types.get(mranges[r]); - var isAncestorOf = false; - if (p) { - for (x = 0; x < mlen; x++) { - if (x === r) { - continue; - } - var c = this.vie.types.get(mranges[x]); - if (c && c.isof(p)) { - isAncestorOf = true; - break; - } - } - } - if (!isAncestorOf) { - ranges.push(mranges[r]); - } - } - - var maxMin = _.max(mins); - var minMax = _.min(maxs); - if (maxMin <= minMax && minMax >= 0 && maxMin >= 0) { - attributes[id] = new this.vie.Attribute(id, ranges, this, maxMin, minMax, metadatas[0]); - } else { - throw new Error("This inheritance is not allowed because of an invalid minCount/maxCount pair!"); - } - } - - this._attributes = attributes; - return this; - }; - -// ### toArray() === list() -// This method return an array of ```VIE.Attribute```s from the attributes instance. -// **Parameters**: -// *nothing. -// **Throws**: -// *nothing* -// **Returns**: -// *{array}* : An array of ```VIE.Attribute```. -// **Example usage**: -// -// personAttrs.list(); - this.toArray = this.list = function (range) { - var ret = []; - var attributes = this._inherit()._attributes; - for (var a in attributes) { - if (!range || attributes[a].applies(range)) { - ret.push(attributes[a]); - } - } - return ret; - }; - - attrs = _.isArray(attrs) ? attrs : [ attrs ]; - _.each(attrs, function (attr) { - this.add(attr.id, attr.range, attr.min, attr.max, attr.metadata); - }, this); -}; -// VIE - Vienna IKS Editables -// (c) 2011 Henri Bergius, IKS Consortium -// (c) 2011 Sebastian Germesin, IKS Consortium -// (c) 2011 Szaby Grünwald, IKS Consortium -// VIE may be freely distributed under the MIT license. -// For all details and documentation: -// http://viejs.org/ -if (VIE.prototype.Namespaces) { - throw new Error("ERROR: VIE.Namespaces is already defined. " + - "Please check your VIE installation!"); -} - -// ## VIE Namespaces -// -// In general, a namespace is a container that provides context for the identifiers. -// Within VIE, namespaces are used to distinguish different ontolgies or vocabularies -// of identifiers, types and attributes. However, because of their verbosity, namespaces -// tend to make their usage pretty circuitous. The ``VIE.Namespaces(...)`` class provides VIE -// with methods to maintain abbreviations (akak **prefixes**) for namespaces in order to -// alleviate their usage. By default, every VIE instance is equipped with a main instance -// of the namespaces in ``myVIE.namespaces``. Furthermore, VIE uses a **base namespace**, -// which is used if no prefix is given (has an empty prefix). -// In the upcoming sections, we will explain the -// methods to add, access and remove prefixes. - - - -// ## VIE.Namespaces(base, namespaces) -// This is the constructor of a VIE.Namespaces. The constructor initially -// needs a *base namespace* and can optionally be initialised with an -// associative array of prefixes and namespaces. The base namespace is used in a way -// that every non-prefixed, non-expanded attribute or type is assumed to be of that -// namespace. This helps, e.g., in an environment where only one namespace is given. -// **Parameters**: -// *{string}* **base** The base namespace. -// *{object}* **namespaces** Initial namespaces to bootstrap the namespaces. (optional) -// **Throws**: -// *{Error}* if the base namespace is missing. -// **Returns**: -// *{VIE.Attribute}* : A **new** VIE.Attribute object. -// **Example usage**: -// -// var ns = new myVIE.Namespaces("http://viejs.org/ns/", -// { -// "foaf": "http://xmlns.com/foaf/0.1/" -// }); -VIE.prototype.Namespaces = function (base, namespaces) { - - if (!base) { - throw new Error("Please provide a base namespace!"); - } - this._base = base; - - this._namespaces = (namespaces)? namespaces : {}; - if (typeof this._namespaces !== "object" || _.isArray(this._namespaces)) { - throw new Error("If you want to initialise VIE namespace prefixes, " + - "please provide a proper object!"); - } -}; - - -// ### base(ns) -// This is a **getter** and **setter** for the base -// namespace. If called like ``base();`` it -// returns the actual base namespace as a string. If provided -// with a string, e.g., ``base("http://viejs.org/ns/");`` -// it sets the current base namespace and retuns the namespace object -// for the purpose of chaining. If provided with anything except a string, -// it throws an Error. -// **Parameters**: -// *{string}* **ns** The namespace to be set. (optional) -// **Throws**: -// *{Error}* if the namespace is not of type string. -// **Returns**: -// *{string}* : The current base namespace. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// console.log(namespaces.base()); // <-- "http://base.ns/" -// namespaces.base("http://viejs.org/ns/"); -// console.log(namespaces.base()); // <-- "http://viejs.org/ns/" -VIE.prototype.Namespaces.prototype.base = function (ns) { - if (!ns) { - return this._base; - } - else if (typeof ns === "string") { - /* remove another mapping */ - this.removeNamespace(ns); - this._base = ns; - return this._base; - } else { - throw new Error("Please provide a valid namespace!"); - } -}; - -// ### add(prefix, namespace) -// This method adds new prefix mappings to the -// current instance. If a prefix or a namespace is already -// present (in order to avoid ambiguities), an Error is thrown. -// ``prefix`` can also be an object in which case, the method -// is called sequentially on all elements. -// **Parameters**: -// *{string|object}* **prefix** The prefix to be set. If it is an object, the -// method will be applied to all key,value pairs sequentially. -// *{string}* **namespace** The namespace to be set. -// **Throws**: -// *{Error}* If a prefix or a namespace is already -// present (in order to avoid ambiguities). -// **Returns**: -// *{VIE.Namespaces}* : The current namespaces instance. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.add("", "http://..."); -// // is always equal to -// namespaces.base("http://..."); // <-- setter of base namespace -VIE.prototype.Namespaces.prototype.add = function (prefix, namespace) { - if (typeof prefix === "object") { - for (var k1 in prefix) { - this.add(k1, prefix[k1]); - } - return this; - } - if (prefix === "") { - this.base(namespace); - return this; - } - /* checking if we overwrite existing mappings */ - else if (this.contains(prefix) && namespace !== this._namespaces[prefix]) { - throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" + - "There is already a mapping existing: '(" + prefix + "," + this.get(prefix) + ")'!"); - } else { - jQuery.each(this._namespaces, function (k1,v1) { - if (v1 === namespace && k1 !== prefix) { - throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" + - "There is already a mapping existing: '(" + k1 + "," + namespace + ")'!"); - } - }); - } - /* if not, just add them */ - this._namespaces[prefix] = namespace; - return this; -}; - -// ### addOrReplace(prefix, namespace) -// This method adds new prefix mappings to the -// current instance. This will overwrite existing mappings. -// **Parameters**: -// *{string|object}* **prefix** The prefix to be set. If it is an object, the -// method will be applied to all key,value pairs sequentially. -// *{string}* **namespace** The namespace to be set. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Namespaces}* : The current namespaces instance. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("", "http://..."); -// // is always equal to -// namespaces.base("http://..."); // <-- setter of base namespace -VIE.prototype.Namespaces.prototype.addOrReplace = function (prefix, namespace) { - if (typeof prefix === "object") { - for (var k1 in prefix) { - this.addOrReplace(k1, prefix[k1]); - } - return this; - } - this.remove(prefix); - this.removeNamespace(namespace); - return this.add(prefix, namespace); -}; - -// ### get(prefix) -// This method retrieves a namespaces, given a prefix. If the -// prefix is the empty string, the base namespace is returned. -// **Parameters**: -// *{string}* **prefix** The prefix to be retrieved. -// **Throws**: -// *nothing* -// **Returns**: -// *{string|undefined}* : The namespace or ```undefined``` if no namespace could be found. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("test", "http://test.ns"); -// console.log(namespaces.get("test")); // <-- "http://test.ns" -VIE.prototype.Namespaces.prototype.get = function (prefix) { - if (prefix === "") { - return this.base(); - } - return this._namespaces[prefix]; -}; - -// ### getPrefix(namespace) -// This method retrieves a prefix, given a namespace. -// **Parameters**: -// *{string}* **namespace** The namespace to be retrieved. -// **Throws**: -// *nothing* -// **Returns**: -// *{string|undefined}* : The prefix or ```undefined``` if no prefix could be found. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("test", "http://test.ns"); -// console.log(namespaces.getPrefix("http://test.ns")); // <-- "test" -VIE.prototype.Namespaces.prototype.getPrefix = function (namespace) { - var prefix; - if (namespace.indexOf('<') === 0) { - namespace = namespace.substring(1, namespace.length - 1); - } - jQuery.each(this._namespaces, function (k1,v1) { - if (namespace.indexOf(v1) === 0) { - prefix = k1; - } - - if (namespace.indexOf(k1 + ':') === 0) { - prefix = k1; - } - }); - return prefix; -}; - -// ### contains(prefix) -// This method checks, whether a prefix is stored in the instance. -// **Parameters**: -// *{string}* **prefix** The prefix to be checked. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* : ```true``` if the prefix could be found, ```false``` otherwise. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("test", "http://test.ns"); -// console.log(namespaces.contains("test")); // <-- true -VIE.prototype.Namespaces.prototype.contains = function (prefix) { - return (prefix in this._namespaces); -}; - -// ### containsNamespace(namespace) -// This method checks, whether a namespace is stored in the instance. -// **Parameters**: -// *{string}* **namespace** The namespace to be checked. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* : ```true``` if the namespace could be found, ```false``` otherwise. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("test", "http://test.ns"); -// console.log(namespaces.containsNamespace("http://test.ns")); // <-- true -VIE.prototype.Namespaces.prototype.containsNamespace = function (namespace) { - return this.getPrefix(namespace) !== undefined; -}; - -// ### update(prefix, namespace) -// This method overwrites the namespace that is stored under the -// prefix ``prefix`` with the new namespace ``namespace``. -// If a namespace is already bound to another prefix, an Error is thrown. -// **Parameters**: -// *{string}* **prefix** The prefix. -// *{string}* **namespace** The namespace. -// **Throws**: -// *{Error}* If a namespace is already bound to another prefix. -// **Returns**: -// *{VIE.Namespaces}* : The namespace instance. -// **Example usage**: -// -// ... -VIE.prototype.Namespaces.prototype.update = function (prefix, namespace) { - this.remove(prefix); - return this.add(prefix, namespace); -}; - -// ### updateNamespace(prefix, namespace) -// This method overwrites the prefix that is bound to the -// namespace ``namespace`` with the new prefix ``prefix``. If another namespace is -// already registered with the given ``prefix``, an Error is thrown. -// **Parameters**: -// *{string}* **prefix** The prefix. -// *{string}* **namespace** The namespace. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Namespaces}* : The namespace instance. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.add("test", "http://test.ns"); -// namespaces.updateNamespace("test2", "http://test.ns"); -// namespaces.get("test2"); // <-- "http://test.ns" -VIE.prototype.Namespaces.prototype.updateNamespace = function (prefix, namespace) { - this.removeNamespace(prefix); - return this.add(prefix, namespace); -}; - -// ### remove(prefix) -// This method removes the namespace that is stored under the prefix ``prefix``. -// **Parameters**: -// *{string}* **prefix** The prefix to be removed. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Namespaces}* : The namespace instance. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.add("test", "http://test.ns"); -// namespaces.get("test"); // <-- "http://test.ns" -// namespaces.remove("test"); -// namespaces.get("test"); // <-- undefined -VIE.prototype.Namespaces.prototype.remove = function (prefix) { - if (prefix) { - delete this._namespaces[prefix]; - } - return this; -}; - -// ### removeNamespace(namespace) -// This method removes removes the namespace ``namespace`` from the instance. -// **Parameters**: -// *{string}* **namespace** The namespace to be removed. -// **Throws**: -// *nothing* -// **Returns**: -// *{VIE.Namespaces}* : The namespace instance. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.add("test", "http://test.ns"); -// namespaces.get("test"); // <-- "http://test.ns" -// namespaces.removeNamespace("http://test.ns"); -// namespaces.get("test"); // <-- undefined -VIE.prototype.Namespaces.prototype.removeNamespace = function (namespace) { - var prefix = this.getPrefix(namespace); - if (prefix) { - delete this._namespaces[prefix]; - } - return this; -}; - -// ### toObj() -// This method serializes the namespace instance into an associative -// array representation. The base namespace is given an empty -// string as key. -// **Parameters**: -// *{boolean}* **omitBase** If set to ```true``` this omits the baseNamespace. -// **Throws**: -// *nothing* -// **Returns**: -// *{object}* : A serialization of the namespaces as an object. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.add("test", "http://test.ns"); -// console.log(namespaces.toObj()); -// // <-- {"" : "http://base.ns/", -// "test": "http://test.ns"} -// console.log(namespaces.toObj(true)); -// // <-- {"test": "http://test.ns"} -VIE.prototype.Namespaces.prototype.toObj = function (omitBase) { - if (omitBase) { - return jQuery.extend({}, this._namespaces); - } - return jQuery.extend({'' : this._base}, this._namespaces); -}; - -// ### curie(uri, safe) -// This method converts a given -// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object. -// If the given uri is already a URI, it is left untouched and directly returned. -// If no prefix could be found, an ```Error``` is thrown. -// **Parameters**: -// *{string}* **uri** The URI to be transformed. -// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs. -// **Throws**: -// *{Error}* If no prefix could be found in the passed namespaces. -// **Returns**: -// *{string}* The CURIE or SCURIE. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var uri = ""; -// ns.curie(uri, false); // --> dbp:Person -// ns.curie(uri, true); // --> [dbp:Person] -VIE.prototype.Namespaces.prototype.curie = function(uri, safe){ - return VIE.Util.toCurie(uri, safe, this); -}; - -// ### isCurie(curie) -// This method checks, whether -// the given string is a CURIE and returns ```true``` if so and ```false```otherwise. -// **Parameters**: -// *{string}* **curie** The CURIE (or SCURIE) to be checked. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var uri = ""; -// var curie = "dbp:Person"; -// var scurie = "[dbp:Person]"; -// var text = "This is some text."; -// ns.isCurie(uri); // --> false -// ns.isCurie(curie); // --> true -// ns.isCurie(scurie); // --> true -// ns.isCurie(text); // --> false -VIE.prototype.Namespaces.prototype.isCurie = function (something) { - return VIE.Util.isCurie(something, this); -}; - -// ### uri(curie) -// This method converts a -// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object. -// **Parameters**: -// *{string}* **curie** The CURIE to be transformed. -// **Throws**: -// *{Error}* If no URI could be assembled. -// **Returns**: -// *{string}* : A string, representing the URI. -// **Example usage**: -// -// var ns = new myVIE.Namespaces( -// "http://viejs.org/ns/", -// { "dbp": "http://dbpedia.org/ontology/" } -// ); -// var curie = "dbp:Person"; -// var scurie = "[dbp:Person]"; -// ns.uri(curie); -// --> -// ns.uri(scurie); -// --> -VIE.prototype.Namespaces.prototype.uri = function (curie) { - return VIE.Util.toUri(curie, this); -}; - -// ### isUri(something) -// This method checks, whether the given string is a URI. -// **Parameters**: -// *{string}* **something** : The string to be checked. -// **Throws**: -// *nothing* -// **Returns**: -// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise. -// **Example usage**: -// -// var namespaces = new vie.Namespaces("http://base.ns/"); -// namespaces.addOrReplace("test", "http://test.ns"); -// var uri = ""; -// var curie = "test:Person"; -// namespaces.isUri(uri); // --> true -// namespaces.isUri(curie); // --> false -VIE.prototype.Namespaces.prototype.isUri = VIE.Util.isUri; -})(); \ No newline at end of file diff --git a/js/theme.js b/js/theme.js index 4853246..f92c308 100644 --- a/js/theme.js +++ b/js/theme.js @@ -2,7 +2,7 @@ * @file * Provides overridable theme functions for all of Edit's client-side HTML. */ -(function($) { +(function($, Drupal) { "use strict"; @@ -153,4 +153,4 @@ Drupal.theme.editFormContainer = function(settings) { return html; }; -})(jQuery); +})(jQuery, Drupal); diff --git a/js/viejs/EditService.js b/js/viejs/EditService.js new file mode 100644 index 0000000..cde0e2e --- /dev/null +++ b/js/viejs/EditService.js @@ -0,0 +1,201 @@ +/** + * @file + * VIE DOM parsing service for Edit. + */ +(function(jQuery, _, Drupal, VIE) { + +"use strict"; + + VIE.prototype.EditService = function (options) { + var defaults = { + name: 'edit', + subjectSelector: '.edit-field.edit-allowed' + }; + this.options = _.extend({}, defaults, options); + + this.views = []; + this.vie = null; + this.name = this.options.name; + }; + + VIE.prototype.EditService.prototype = { + load: function (loadable) { + var correct = loadable instanceof this.vie.Loadable; + if (!correct) { + throw new Error('Invalid Loadable passed'); + } + + var element; + if (!loadable.options.element) { + if (typeof document === 'undefined') { + return loadable.resolve([]); + } else { + element = Drupal.settings.edit.context; + } + } else { + element = loadable.options.element; + } + + var entities = this.readEntities(element); + loadable.resolve(entities); + }, + + // The edit-id data attribute contains the full identifier of + // each entity element in the format + // `::::`. + _getID: function (element) { + var id = jQuery(element).attr('data-edit-id'); + if (!id) { + id = jQuery(element).closest('[data-edit-id]').attr('data-edit-id'); + } + return id; + }, + + // Returns the "URI" of an entity of an element in format + // `/`. + getElementSubject: function (element) { + return this._getID(element).split(':').slice(0, 2).join('/'); + }, + + // Returns the field name for an element in format + // `//`. + // (Slashes instead of colons because the field name is no namespace.) + getElementPredicate: function (element) { + if (!this._getID(element)) { + throw new Error('Could not find predicate for element'); + } + return this._getID(element).split(':').slice(2, 5).join('/'); + }, + + getElementType: function (element) { + return this._getID(element).split(':').slice(0, 1)[0]; + }, + + // Reads all editable entities (currently each Drupal field is considered an + // entity, in the future Drupal entities should be mapped to VIE entities) + // from DOM and returns the VIE enties it found. + readEntities: function (element) { + var service = this; + var entities = []; + var entityElements = jQuery(this.options.subjectSelector, element); + entityElements = entityElements.add(jQuery(element).filter(this.options.subjectSelector)); + entityElements.each(function () { + var entity = service._readEntity(jQuery(this)); + if (entity) { + entities.push(entity); + } + }); + return entities; + }, + + // Returns a filled VIE Entity instance for a DOM element. The Entity + // is also registered in the VIE entities collection. + _readEntity: function (element) { + var subject = this.getElementSubject(element); + var type = this.getElementType(element); + var entity = this._readEntityPredicates(subject, element, false); + if (jQuery.isEmptyObject(entity)) { + return null; + } + entity['@subject'] = subject; + if (type) { + entity['@type'] = this._registerType(type, element); + } + + // Register with VIE + return this._registerEntity(entity); + }, + + _registerEntity: function (entityData) { + var entityInstance = new this.vie.Entity(entityData); + return this.vie.entities.addOrUpdate(entityInstance, { + updateOptions: { + silent: true + } + }); + }, + + _registerType: function (typeId, element) { + typeId = ''; + var type = this.vie.types.get(typeId); + if (!type) { + this.vie.types.add(typeId, []); + type = this.vie.types.get(typeId); + } + + var predicate = this.getElementPredicate(element); + if (type.attributes.get(predicate)) { + return type; + } + + var label = element.data('edit-field-label'); + var range = 'Form'; + if (element.hasClass('edit-type-direct')) { + range = 'Direct'; + } + if (element.hasClass('edit-type-direct-with-wysiwyg')) { + range = 'Wysiwyg'; + } + type.attributes.add(predicate, [range], 0, 1, { + label: element.data('edit-field-label') + }); + + return type; + }, + + _readEntityPredicates: function (subject, element, emptyValues) { + var entityPredicates = {}; + var service = this; + this.findPredicateElements(subject, element, true).each(function () { + var predicateElement = jQuery(this); + var predicate = service.getElementPredicate(predicateElement); + if (!predicate) { + return; + } + var value = service._readElementValue(predicateElement); + if (value === null && !emptyValues) { + return; + } + + entityPredicates[predicate] = value; + }); + return entityPredicates; + }, + + _readElementValue: function (element) { + return jQuery.trim(element.html()); + }, + + // Subject elements are the DOM elements containing a single or multiple + // editable fields. + findSubjectElements: function (element) { + if (!element) { + element = Drupal.settings.edit.context; + } + return jQuery(this.options.subjectSelector, element); + }, + + // Predicate Elements are the actual DOM elements that users will be able + // to edit. + findPredicateElements: function (subject, element, allowNestedPredicates, stop) { + var predicates = jQuery(); + + // Form-type predicates + predicates = predicates.add(element.filter('.edit-type-form')); + + // Direct-type predicates + var direct = element.filter('.edit-type-direct'); + predicates = predicates.add(direct.find('.field-item')); + + if (!predicates.length && !stop) { + var parentElement = element.parent(this.options.subjectSelector); + if (parentElement.length) { + return this.findPredicateElements(subject, parentElement, allowNestedPredicates, true); + } + } + + return predicates; + } + }; + +})(jQuery, _, Drupal, VIE); diff --git a/js/viejs/SparkEditService.js b/js/viejs/SparkEditService.js deleted file mode 100644 index ff95266..0000000 --- a/js/viejs/SparkEditService.js +++ /dev/null @@ -1,208 +0,0 @@ -/** - * @file - * VIE DOM parsing service for Edit. - */ -(function(jQuery, _, Drupal, VIE) { - -"use strict"; - - VIE.prototype.SparkEditService = function (options) { - var defaults = { - name: 'edit', - subjectSelector: '.edit-field.edit-allowed' - }; - this.options = _.extend({}, defaults, options); - - this.views = []; - this.vie = null; - this.name = this.options.name; - }; - - VIE.prototype.SparkEditService.prototype = { - load: function (loadable) { - var correct = loadable instanceof this.vie.Loadable; - if (!correct) { - throw new Error('Invalid Loadable passed'); - } - - var element; - if (!loadable.options.element) { - if (typeof document === 'undefined') { - return loadable.resolve([]); - } else { - element = Drupal.settings.edit.context; - } - } else { - element = loadable.options.element; - } - - var entities = this.readEntities(element); - loadable.resolve(entities); - }, - - // The edit-id data attribute contains the full identifier of - // each entity element in the format - // `::::`. - _getID: function (element) { - var id = jQuery(element).attr('data-edit-id'); - if (!id) { - id = jQuery(element).closest('[data-edit-id]').attr('data-edit-id'); - } - return id; - }, - - // Returns the "URI" of an entity of an element in format - // `/`. - getElementSubject: function (element) { - return this._getID(element).split(':').slice(0, 2).join('/'); - }, - - // Returns the field name for an element in format - // `//`. - // (Slashes instead of colons because the field name is no namespace.) - getElementPredicate: function (element) { - if (!this._getID(element)) { - throw new Error('Could not find predicate for element'); - } - return this._getID(element).split(':').slice(2, 5).join('/'); - }, - - getElementType: function (element) { - return this._getID(element).split(':').slice(0, 1)[0]; - }, - - // Reads all editable entities (currently each Drupal field is considered an - // entity, in the future Drupal entities should be mapped to VIE entities) - // from DOM and returns the VIE enties it found. - // @todo: check the above. - readEntities: function (element) { - var service = this; - var entities = []; - var entityElements = jQuery(this.options.subjectSelector, element); - entityElements = entityElements.add(jQuery(element).filter(this.options.subjectSelector)); - entityElements.each(function () { - var entity = service._readEntity(jQuery(this)); - if (entity) { - entities.push(entity); - } - }); - return entities; - }, - - // Returns a filled VIE Entity instance for a DOM element. The Entity - // is also registered in the VIE entities collection. - _readEntity: function (element) { - var subject = this.getElementSubject(element); - var type = this.getElementType(element); - var entity = this._readEntityPredicates(subject, element, false); - if (jQuery.isEmptyObject(entity)) { - return null; - } - entity['@subject'] = subject; - if (type) { - entity['@type'] = this._registerType(type, element); - } - - // Register with VIE - return this._registerEntity(entity); - }, - - _registerEntity: function (entityData) { - var entityInstance = new this.vie.Entity(entityData); - return this.vie.entities.addOrUpdate(entityInstance, { - updateOptions: { - silent: true - } - }); - }, - - _registerType: function (typeId, element) { - typeId = ''; - var type = this.vie.types.get(typeId); - if (!type) { - this.vie.types.add(typeId, []); - type = this.vie.types.get(typeId); - } - - var predicate = this.getElementPredicate(element); - if (type.attributes.get(predicate)) { - return type; - } - - var label = element.data('edit-field-label'); - var range = 'Form'; - if (element.hasClass('edit-type-direct')) { - range = 'Direct'; - } - if (element.hasClass('edit-type-direct-with-wysiwyg')) { - range = 'Wysiwyg'; - } - type.attributes.add(predicate, [range], 0, 1, { - label: element.data('edit-field-label') - }); - - return type; - }, - - _readEntityPredicates: function (subject, element, emptyValues) { - var entityPredicates = {}; - var service = this; - this.findPredicateElements(subject, element, true).each(function () { - var predicateElement = jQuery(this); - var predicate = service.getElementPredicate(predicateElement); - if (!predicate) { - return; - } - var value = service._readElementValue(predicateElement); - if (value === null && !emptyValues) { - return; - } - - entityPredicates[predicate] = value; - }); - return entityPredicates; - }, - - _readElementValue: function (element) { - return jQuery.trim(element.html()); - }, - - // Subject elements are the DOM elements containing a single or multiple - // editable fields. In Spark Edit these elements are called _Fields_, - // and the actual DOM elements which are edited are called _Editables_. - findSubjectElements: function (element) { - if (!element) { - element = Drupal.settings.edit.context; - } - return jQuery(this.options.subjectSelector, element); - }, - - // Predicate Elements are the actual DOM elements that users will be able - // to edit. In regular Spark Edit they are called _Editables_. - // - // They are contained within Entity elements, which in Spark Edit are called - // _Fields_. - // @todo: clarify and document what the Best Way to do this is? Should VIE's - // entities map to Drupal's entities etc? Also see higher comments. - findPredicateElements: function (subject, element, allowNestedPredicates, stop) { - var predicates = jQuery(); - - // Form-type predicates - predicates = predicates.add(element.filter('.edit-type-form')); - - // Direct-type predicates - var direct = element.filter('.edit-type-direct'); - predicates = predicates.add(direct.find('.field-item')); - - if (!predicates.length && !stop) { - var parentElement = element.parent(this.options.subjectSelector); - if (parentElement.length) { - return this.findPredicateElements(subject, parentElement, allowNestedPredicates, true); - } - } - - return predicates; - } - }; - -})(jQuery, _, Drupal, VIE); diff --git a/js/views/fielddecorator-view.js b/js/views/fielddecorator-view.js deleted file mode 100644 index 01a4e83..0000000 --- a/js/views/fielddecorator-view.js +++ /dev/null @@ -1,324 +0,0 @@ -/** - * @file - * A Backbone View that decorates properties. - * - * It listens to state changes of the property editor. - * - * @todo rename to propertydecorator-view.js + PropertyDecorationView. - */ -(function($, Backbone, Drupal) { - -"use strict"; - -Drupal.edit = Drupal.edit || {}; -Drupal.edit.views = Drupal.edit.views || {}; -Drupal.edit.views.FieldDecorationView = Backbone.View.extend({ - - editor: null, - entity: null, - predicate : null, - editorName: null, - toolbarId: null, - - _widthAttributeIsEmpty: null, - - events: { - 'mouseenter.edit' : 'onMouseEnter', - 'mouseleave.edit' : 'onMouseLeave' - }, - - /** - * Implements Backbone Views' initialize() function. - * - * @param options - * An object with the following keys: - * - editor: the editor object with an 'options' object that has these keys: - * * entity: the VIE entity for the property. - * * property: the predicate of the property. - * * editorName: the editor name: 'form', 'direct' or - * 'direct-with-wysiwyg'. - * * widget: the parent EditableeEntity widget. - * - toolbarId: the ID attribute of the toolbar as rendered in the DOM. - */ - initialize: function(options) { - this.editor = options.editor; - this.toolbarId = options.toolbarId; - - this.entity = this.editor.options.entity; - this.predicate = this.editor.options.property; - this.editorName = this.editor.options.editorName; - - this.$el.css('background-color', this._getBgColor(this.$el)); - }, - - /** - * Listens to editor state changes. - */ - stateChange: function(from, to) { - switch (to) { - case 'inactive': - if (from !== null) { - this.undecorate(); - } - break; - case 'candidate': - this.decorate(); - if (from !== 'inactive') { - this.stopHighlight(); - if (from !== 'highlighted') { - this.stopEdit(this.editorName); - } - } - break; - case 'highlighted': - this.startHighlight(); - break; - case 'activating': - // NOTE: this step only exists for the 'form' editor! It is skipped by - // the 'direct' and 'direct-with-wysiwyg' editors, because no loading is - // necessary. - this.prepareEdit(this.editorName); - break; - case 'active': - if (this.editorName !== 'form') { - this.prepareEdit(this.editorName); - } - this.startEdit(this.editorName); - break; - case 'changed': - break; - case 'saving': - break; - case 'saved': - break; - case 'invalid': - break; - } - }, - - /** - * Starts hover: transition to 'highlight' state. - * - * @param event - */ - onMouseEnter: function(event) { - var that = this; - this._ignoreHoveringVia(event, '#' + this.toolbarId, function () { - var editableEntity = that.editor.options.widget; - editableEntity.setState('highlighted', that.predicate); - event.stopPropagation(); - }); - }, - - /** - * Stops hover: back to 'candidate' state. - * - * @param event - */ - onMouseLeave: function(event) { - var that = this; - this._ignoreHoveringVia(event, '#' + this.toolbarId, function () { - var editableEntity = that.editor.options.widget; - editableEntity.setState('candidate', that.predicate, { reason: 'mouseleave' }); - event.stopPropagation(); - }); - }, - - decorate: function () { - this.$el.addClass('edit-animate-fast edit-candidate edit-editable'); - }, - - undecorate: function () { - this.$el - .removeClass('edit-candidate edit-editable edit-highlighted edit-editing edit-belowoverlay'); - }, - - startHighlight: function () { - // Animations. - var that = this; - setTimeout(function() { - that.$el.addClass('edit-highlighted'); - }, 0); - }, - - stopHighlight: function() { - this.$el - .removeClass('edit-highlighted'); - }, - - prepareEdit: function(editorName) { - this.$el.addClass('edit-editing'); - - // While editing, don't show *any* other editors. - // @todo: revisit this once https://github.com/bergie/create/issues/133 is solved. - $('.edit-candidate').not('.edit-editing').removeClass('edit-editable'); - - if (editorName === 'form') { - this.$el.addClass('edit-belowoverlay'); - } - }, - - startEdit: function(editorName) { - if (editorName !== 'form') { - this._pad(); - } - }, - - stopEdit: function(editorName) { - this.$el.removeClass('edit-highlighted edit-editing'); - - // Make the other editors show up again. - // @todo: revisit this once https://github.com/bergie/create/issues/133 is solved. - $('.edit-candidate').addClass('edit-editable'); - - if (editorName === 'form') { - this.$el.removeClass('edit-belowoverlay'); - } - else { - this._unpad(); - } - }, - - _pad: function () { - var self = this; - - // Add 5px padding for readability. This means we'll freeze the current - // width and *then* add 5px padding, hence ensuring the padding is added "on - // the outside". - // 1) Freeze the width (if it's not already set); don't use animations. - if (this.$el[0].style.width === "") { - this._widthAttributeIsEmpty = true; - this.$el - .addClass('edit-animate-disable-width') - .css('width', this.$el.width()); - } - - // 2) Add padding; use animations. - var posProp = this._getPositionProperties(this.$el); - setTimeout(function() { - // Re-enable width animations (padding changes affect width too!). - self.$el.removeClass('edit-animate-disable-width'); - - // Pad the editable. - self.$el - .css({ - 'position': 'relative', - 'top': posProp.top - 5 + 'px', - 'left': posProp.left - 5 + 'px', - 'padding-top' : posProp['padding-top'] + 5 + 'px', - 'padding-left' : posProp['padding-left'] + 5 + 'px', - 'padding-right' : posProp['padding-right'] + 5 + 'px', - 'padding-bottom': posProp['padding-bottom'] + 5 + 'px', - 'margin-bottom': posProp['margin-bottom'] - 10 + 'px' - }); - }, 0); - }, - - _unpad: function () { - var self = this; - - // 1) Set the empty width again. - if (this._widthAttributeIsEmpty) { - this.$el - .addClass('edit-animate-disable-width') - .css('width', ''); - } - - // 2) Remove padding; use animations (these will run simultaneously with) - // the fading out of the toolbar as its gets removed). - var posProp = this._getPositionProperties(this.$el); - setTimeout(function() { - // Re-enable width animations (padding changes affect width too!). - self.$el.removeClass('edit-animate-disable-width'); - - // Unpad the editable. - self.$el - .css({ - 'position': 'relative', - 'top': posProp.top + 5 + 'px', - 'left': posProp.left + 5 + 'px', - 'padding-top' : posProp['padding-top'] - 5 + 'px', - 'padding-left' : posProp['padding-left'] - 5 + 'px', - 'padding-right' : posProp['padding-right'] - 5 + 'px', - 'padding-bottom': posProp['padding-bottom'] - 5 + 'px', - 'margin-bottom': posProp['margin-bottom'] + 10 + 'px' - }); - }, 0); - }, - - /** - * Gets the background color of an element (or the inherited one). - * - * @param $e - * A DOM element. - */ - _getBgColor: function($e) { - var c; - - if ($e === null || $e[0].nodeName === 'HTML') { - // Fallback to white. - return 'rgb(255, 255, 255)'; - } - c = $e.css('background-color'); - // TRICKY: edge case for Firefox' "transparent" here; this is a - // browser bug: https://bugzilla.mozilla.org/show_bug.cgi?id=635724 - if (c === 'rgba(0, 0, 0, 0)' || c === 'transparent') { - return this._getBgColor($e.parent()); - } - return c; - }, - - /** - * Gets the top and left properties of an element and convert extraneous - * values and information into numbers ready for subtraction. - * - * @param $e - * A DOM element. - */ - _getPositionProperties: function($e) { - var p, - r = {}, - props = [ - 'top', 'left', 'bottom', 'right', - 'padding-top', 'padding-left', 'padding-right', 'padding-bottom', - 'margin-bottom' - ]; - - var propCount = props.length; - for (var i = 0; i < propCount; i++) { - p = props[i]; - r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10); - } - return r; - }, - - /** - * Replaces blank or 'auto' CSS "position: " values with "0px". - * - * @param pos - * The value for a CSS position declaration. - */ - _replaceBlankPosition: function(pos) { - // @todo: this was pos == NaN (which always returns false, keeping this - // comment in case we find a regression. - if (pos === 'auto' || !pos) { - pos = '0px'; - } - return pos; - }, - - /** - * Ignores hovering to/from the given closest element, but as soon as a hover - * occurs to/from *another* element, then call the given callback. - */ - _ignoreHoveringVia: function(event, closest, callback) { - if ($(event.relatedTarget).closest(closest).length > 0) { - event.stopPropagation(); - } - else { - callback(); - } - } -}); - -})(jQuery, Backbone, Drupal); diff --git a/js/views/overlay-view.js b/js/views/overlay-view.js index d41f091..e545087 100644 --- a/js/views/overlay-view.js +++ b/js/views/overlay-view.js @@ -75,8 +75,6 @@ Drupal.edit.views.OverlayView = Backbone.View.extend({ .addClass('edit-animate-invisible') .bind(Drupal.edit.util.constants.transitionEnd, function (event) { that.$el.remove(); - // @todo - should the overlay really do this? - $('.edit-form-container, .edit-toolbar-container, #edit_modal, .edit-curtain, .edit-validation-errors').remove(); }); } }); diff --git a/js/views/propertyeditordecoration-view.js b/js/views/propertyeditordecoration-view.js new file mode 100644 index 0000000..7d8a27c --- /dev/null +++ b/js/views/propertyeditordecoration-view.js @@ -0,0 +1,322 @@ +/** + * @file + * A Backbone View that decorates a Property Editor widget. + * + * It listens to state changes of the property editor. + */ +(function($, Backbone, Drupal) { + +"use strict"; + +Drupal.edit = Drupal.edit || {}; +Drupal.edit.views = Drupal.edit.views || {}; +Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({ + + editor: null, + entity: null, + predicate : null, + editorName: null, + toolbarId: null, + + _widthAttributeIsEmpty: null, + + events: { + 'mouseenter.edit' : 'onMouseEnter', + 'mouseleave.edit' : 'onMouseLeave' + }, + + /** + * Implements Backbone Views' initialize() function. + * + * @param options + * An object with the following keys: + * - editor: the editor object with an 'options' object that has these keys: + * * entity: the VIE entity for the property. + * * property: the predicate of the property. + * * editorName: the editor name: 'form', 'direct' or + * 'direct-with-wysiwyg'. + * * widget: the parent EditableeEntity widget. + * - toolbarId: the ID attribute of the toolbar as rendered in the DOM. + */ + initialize: function(options) { + this.editor = options.editor; + this.toolbarId = options.toolbarId; + + this.entity = this.editor.options.entity; + this.predicate = this.editor.options.property; + this.editorName = this.editor.options.editorName; + + this.$el.css('background-color', this._getBgColor(this.$el)); + }, + + /** + * Listens to editor state changes. + */ + stateChange: function(from, to) { + switch (to) { + case 'inactive': + if (from !== null) { + this.undecorate(); + } + break; + case 'candidate': + this.decorate(); + if (from !== 'inactive') { + this.stopHighlight(); + if (from !== 'highlighted') { + this.stopEdit(this.editorName); + } + } + break; + case 'highlighted': + this.startHighlight(); + break; + case 'activating': + // NOTE: this step only exists for the 'form' editor! It is skipped by + // the 'direct' and 'direct-with-wysiwyg' editors, because no loading is + // necessary. + this.prepareEdit(this.editorName); + break; + case 'active': + if (this.editorName !== 'form') { + this.prepareEdit(this.editorName); + } + this.startEdit(this.editorName); + break; + case 'changed': + break; + case 'saving': + break; + case 'saved': + break; + case 'invalid': + break; + } + }, + + /** + * Starts hover: transition to 'highlight' state. + * + * @param event + */ + onMouseEnter: function(event) { + var that = this; + this._ignoreHoveringVia(event, '#' + this.toolbarId, function () { + var editableEntity = that.editor.options.widget; + editableEntity.setState('highlighted', that.predicate); + event.stopPropagation(); + }); + }, + + /** + * Stops hover: back to 'candidate' state. + * + * @param event + */ + onMouseLeave: function(event) { + var that = this; + this._ignoreHoveringVia(event, '#' + this.toolbarId, function () { + var editableEntity = that.editor.options.widget; + editableEntity.setState('candidate', that.predicate, { reason: 'mouseleave' }); + event.stopPropagation(); + }); + }, + + decorate: function () { + this.$el.addClass('edit-animate-fast edit-candidate edit-editable'); + }, + + undecorate: function () { + this.$el + .removeClass('edit-candidate edit-editable edit-highlighted edit-editing edit-belowoverlay'); + }, + + startHighlight: function () { + // Animations. + var that = this; + setTimeout(function() { + that.$el.addClass('edit-highlighted'); + }, 0); + }, + + stopHighlight: function() { + this.$el + .removeClass('edit-highlighted'); + }, + + prepareEdit: function(editorName) { + this.$el.addClass('edit-editing'); + + // While editing, don't show *any* other editors. + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) + // Revisit this. + $('.edit-candidate').not('.edit-editing').removeClass('edit-editable'); + + if (editorName === 'form') { + this.$el.addClass('edit-belowoverlay'); + } + }, + + startEdit: function(editorName) { + if (editorName !== 'form') { + this._pad(); + } + }, + + stopEdit: function(editorName) { + this.$el.removeClass('edit-highlighted edit-editing'); + + // Make the other editors show up again. + // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133) + // Revisit this. + $('.edit-candidate').addClass('edit-editable'); + + if (editorName === 'form') { + this.$el.removeClass('edit-belowoverlay'); + } + else { + this._unpad(); + } + }, + + _pad: function () { + var self = this; + + // Add 5px padding for readability. This means we'll freeze the current + // width and *then* add 5px padding, hence ensuring the padding is added "on + // the outside". + // 1) Freeze the width (if it's not already set); don't use animations. + if (this.$el[0].style.width === "") { + this._widthAttributeIsEmpty = true; + this.$el + .addClass('edit-animate-disable-width') + .css('width', this.$el.width()); + } + + // 2) Add padding; use animations. + var posProp = this._getPositionProperties(this.$el); + setTimeout(function() { + // Re-enable width animations (padding changes affect width too!). + self.$el.removeClass('edit-animate-disable-width'); + + // Pad the editable. + self.$el + .css({ + 'position': 'relative', + 'top': posProp.top - 5 + 'px', + 'left': posProp.left - 5 + 'px', + 'padding-top' : posProp['padding-top'] + 5 + 'px', + 'padding-left' : posProp['padding-left'] + 5 + 'px', + 'padding-right' : posProp['padding-right'] + 5 + 'px', + 'padding-bottom': posProp['padding-bottom'] + 5 + 'px', + 'margin-bottom': posProp['margin-bottom'] - 10 + 'px' + }); + }, 0); + }, + + _unpad: function () { + var self = this; + + // 1) Set the empty width again. + if (this._widthAttributeIsEmpty) { + this.$el + .addClass('edit-animate-disable-width') + .css('width', ''); + } + + // 2) Remove padding; use animations (these will run simultaneously with) + // the fading out of the toolbar as its gets removed). + var posProp = this._getPositionProperties(this.$el); + setTimeout(function() { + // Re-enable width animations (padding changes affect width too!). + self.$el.removeClass('edit-animate-disable-width'); + + // Unpad the editable. + self.$el + .css({ + 'position': 'relative', + 'top': posProp.top + 5 + 'px', + 'left': posProp.left + 5 + 'px', + 'padding-top' : posProp['padding-top'] - 5 + 'px', + 'padding-left' : posProp['padding-left'] - 5 + 'px', + 'padding-right' : posProp['padding-right'] - 5 + 'px', + 'padding-bottom': posProp['padding-bottom'] - 5 + 'px', + 'margin-bottom': posProp['margin-bottom'] + 10 + 'px' + }); + }, 0); + }, + + /** + * Gets the background color of an element (or the inherited one). + * + * @param $e + * A DOM element. + */ + _getBgColor: function($e) { + var c; + + if ($e === null || $e[0].nodeName === 'HTML') { + // Fallback to white. + return 'rgb(255, 255, 255)'; + } + c = $e.css('background-color'); + // TRICKY: edge case for Firefox' "transparent" here; this is a + // browser bug: https://bugzilla.mozilla.org/show_bug.cgi?id=635724 + if (c === 'rgba(0, 0, 0, 0)' || c === 'transparent') { + return this._getBgColor($e.parent()); + } + return c; + }, + + /** + * Gets the top and left properties of an element and convert extraneous + * values and information into numbers ready for subtraction. + * + * @param $e + * A DOM element. + */ + _getPositionProperties: function($e) { + var p, + r = {}, + props = [ + 'top', 'left', 'bottom', 'right', + 'padding-top', 'padding-left', 'padding-right', 'padding-bottom', + 'margin-bottom' + ]; + + var propCount = props.length; + for (var i = 0; i < propCount; i++) { + p = props[i]; + r[p] = parseInt(this._replaceBlankPosition($e.css(p)), 10); + } + return r; + }, + + /** + * Replaces blank or 'auto' CSS "position: " values with "0px". + * + * @param pos + * The value for a CSS position declaration. + */ + _replaceBlankPosition: function(pos) { + if (pos === 'auto' || !pos) { + pos = '0px'; + } + return pos; + }, + + /** + * Ignores hovering to/from the given closest element, but as soon as a hover + * occurs to/from *another* element, then call the given callback. + */ + _ignoreHoveringVia: function(event, closest, callback) { + if ($(event.relatedTarget).closest(closest).length > 0) { + event.stopPropagation(); + } + else { + callback(); + } + } +}); + +})(jQuery, Backbone, Drupal); diff --git a/js/views/toolbar-view.js b/js/views/toolbar-view.js index 55b5da3..c31791a 100644 --- a/js/views/toolbar-view.js +++ b/js/views/toolbar-view.js @@ -140,7 +140,9 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ var $inner = $(updatedField).html(); editor.element.html($inner); - // @todo: VIE doesn't seem to like this? :) It seems that if I delete/ + // @todo BLOCKED_ON(VIE.js, how to let VIE know that some content was removed and how to scan new content for VIE entities, to make them editable?) + // Also see Drupal.behaviors.editDiscoverEditables. + // VIE doesn't seem to like this? :) It seems that if I delete/ // overwrite an existing field, that VIE refuses to find the same // predicate again for the same entity? // self.$el.replaceWith(updatedField); @@ -285,7 +287,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ /** * Adjusts the toolbar to accomodate padding on the PropertyEditor widget. * - * @see FieldDecorationView._pad(). + * @see PropertyEditorDecorationView._pad(). */ _pad: function(editorName) { // The whole toolbar must move to the top when the property's DOM element @@ -307,7 +309,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ /** * Undoes the changes made by _pad(). * - * @see FieldDecorationView._unpad(). + * @see PropertyEditorDecorationView._unpad(). */ _unpad: function(editorName) { // Move the toolbar back to its original position. @@ -385,11 +387,6 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({ .bind(Drupal.edit.util.constants.transitionEnd, function (e) { $el.remove(); }); - // @todo: verify/confirm that this really necessary. Messing with this.$el - // is not recommended - maybe temporarily unbind/undelegate events? - // Immediately set to null, so that if the user hovers over the property - // before the removal been completed, a new toolbar can be created. - // this.$el = null; }, /** diff --git a/vie-and-create.patch b/vie-and-create.patch new file mode 100644 index 0000000..a050e04 --- /dev/null +++ b/vie-and-create.patch @@ -0,0 +1,5401 @@ + core/misc/create/create-editonly.js | 1643 ++++++++++++++++ + core/misc/vie/vie-core.js | 3682 +++++++++++++++++++++++++++++++++++ + core/modules/system/system.module | 33 +- + 3 files changed, 5356 insertions(+), 2 deletions(-) + +diff --git a/core/misc/create/create-editonly.js b/core/misc/create/create-editonly.js +new file mode 100644 +index 0000000..1d76bff +--- /dev/null ++++ b/core/misc/create/create-editonly.js +@@ -0,0 +1,1643 @@ ++// Create.js - On-site web editing interface ++// (c) 2011-2012 Henri Bergius, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false window:false console:false */ ++ 'use strict'; ++ ++ // # Widget for adding items to a collection ++ jQuery.widget('Midgard.midgardCollectionAdd', { ++ options: { ++ editingWidgets: null, ++ collection: null, ++ model: null, ++ definition: null, ++ view: null, ++ disabled: false, ++ vie: null, ++ editableOptions: null, ++ templates: { ++ button: '' ++ } ++ }, ++ ++ _create: function () { ++ this.addButtons = []; ++ var widget = this; ++ if (!widget.options.collection.localStorage) { ++ try { ++ widget.options.collection.url = widget.options.model.url(); ++ } catch (e) { ++ if (window.console) { ++ console.log(e); ++ } ++ } ++ } ++ ++ widget.options.collection.bind('add', function (model) { ++ model.primaryCollection = widget.options.collection; ++ widget.options.vie.entities.add(model); ++ model.collection = widget.options.collection; ++ }); ++ ++ // Re-check collection constraints ++ widget.options.collection.bind('add remove reset', widget.checkCollectionConstraints, widget); ++ ++ widget._bindCollectionView(widget.options.view); ++ }, ++ ++ _bindCollectionView: function (view) { ++ var widget = this; ++ view.bind('add', function (itemView) { ++ itemView.$el.effect('slide', function () { ++ widget._makeEditable(itemView); ++ }); ++ }); ++ }, ++ ++ _makeEditable: function (itemView) { ++ this.options.editableOptions.disabled = this.options.disabled; ++ this.options.editableOptions.model = itemView.model; ++ itemView.$el.midgardEditable(this.options.editableOptions); ++ }, ++ ++ _init: function () { ++ if (this.options.disabled) { ++ this.disable(); ++ return; ++ } ++ this.enable(); ++ }, ++ ++ hideButtons: function () { ++ _.each(this.addButtons, function (button) { ++ button.hide(); ++ }); ++ }, ++ ++ showButtons: function () { ++ _.each(this.addButtons, function (button) { ++ button.show(); ++ }); ++ }, ++ ++ checkCollectionConstraints: function () { ++ if (this.options.disabled) { ++ return; ++ } ++ ++ if (!this.options.view.canAdd()) { ++ this.hideButtons(); ++ return; ++ } ++ ++ if (!this.options.definition) { ++ // We have now information on the constraints applying to this collection ++ this.showButtons(); ++ return; ++ } ++ ++ if (!this.options.definition.max || this.options.definition.max === -1) { ++ // No maximum constraint ++ this.showButtons(); ++ return; ++ } ++ ++ if (this.options.collection.length < this.options.definition.max) { ++ this.showButtons(); ++ return; ++ } ++ // Collection is already full by its definition ++ this.hideButtons(); ++ }, ++ ++ enable: function () { ++ var widget = this; ++ ++ var addButton = jQuery(_.template(this.options.templates.button, { ++ icon: 'plus', ++ label: this.options.editableOptions.localize('Add', this.options.editableOptions.language) ++ })).button(); ++ addButton.addClass('midgard-create-add'); ++ addButton.click(function () { ++ widget.addItem(addButton); ++ }); ++ jQuery(widget.options.view.el).after(addButton); ++ ++ widget.addButtons.push(addButton); ++ widget.checkCollectionConstraints(); ++ }, ++ ++ disable: function () { ++ _.each(this.addButtons, function (button) { ++ button.remove(); ++ }); ++ this.addButtons = []; ++ }, ++ ++ _getTypeActions: function (options) { ++ var widget = this; ++ var actions = []; ++ _.each(this.options.definition.range, function (type) { ++ var nsType = widget.options.collection.vie.namespaces.uri(type); ++ if (!widget.options.view.canAdd(nsType)) { ++ return; ++ } ++ actions.push({ ++ name: type, ++ label: type, ++ cb: function () { ++ widget.options.collection.add({ ++ '@type': type ++ }, options); ++ }, ++ className: 'create-ui-btn' ++ }); ++ }); ++ return actions; ++ }, ++ ++ addItem: function (button, options) { ++ if (options === undefined) { ++ options = {}; ++ } ++ var addOptions = _.extend({}, options, { validate: false }); ++ ++ var itemData = {}; ++ if (this.options.definition && this.options.definition.range) { ++ if (this.options.definition.range.length === 1) { ++ // Items can be of single type, add that ++ itemData['@type'] = this.options.definition.range[0]; ++ } else { ++ // Ask user which type to add ++ jQuery('body').midgardNotifications('create', { ++ bindTo: button, ++ gravity: 'L', ++ body: this.options.editableOptions.localize('Choose type to add', this.options.editableOptions.language), ++ timeout: 0, ++ actions: this._getTypeActions(addOptions) ++ }); ++ return; ++ } ++ } else { ++ // Check the view templates for possible non-Thing type to use ++ var keys = _.keys(this.options.view.templates); ++ if (keys.length == 2) { ++ itemData['@type'] = keys[0]; ++ } ++ } ++ this.options.collection.add(itemData, addOptions); ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2011-2012 Henri Bergius, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false window:false console:false */ ++ 'use strict'; ++ ++ // # Widget for adding items anywhere inside a collection ++ jQuery.widget('Midgard.midgardCollectionAddBetween', jQuery.Midgard.midgardCollectionAdd, { ++ _bindCollectionView: function (view) { ++ var widget = this; ++ view.bind('add', function (itemView) { ++ //itemView.el.effect('slide'); ++ widget._makeEditable(itemView); ++ widget._refreshButtons(); ++ }); ++ view.bind('remove', function () { ++ widget._refreshButtons(); ++ }); ++ }, ++ ++ _refreshButtons: function () { ++ var widget = this; ++ window.setTimeout(function () { ++ widget.disable(); ++ widget.enable(); ++ }, 1); ++ }, ++ ++ prepareButton: function (index) { ++ var widget = this; ++ var addButton = jQuery(_.template(this.options.templates.button, { ++ icon: 'plus', ++ label: '' ++ })).button(); ++ addButton.addClass('midgard-create-add'); ++ addButton.click(function () { ++ widget.addItem(addButton, { ++ at: index ++ }); ++ }); ++ return addButton; ++ }, ++ ++ enable: function () { ++ var widget = this; ++ ++ var firstAddButton = widget.prepareButton(0); ++ jQuery(widget.options.view.el).prepend(firstAddButton); ++ widget.addButtons.push(firstAddButton); ++ jQuery.each(widget.options.view.entityViews, function (cid, view) { ++ var index = widget.options.collection.indexOf(view.model); ++ var addButton = widget.prepareButton(index + 1); ++ jQuery(view.el).append(addButton); ++ widget.addButtons.push(addButton); ++ }); ++ ++ this.checkCollectionConstraints(); ++ }, ++ ++ disable: function () { ++ var widget = this; ++ jQuery.each(widget.addButtons, function (idx, button) { ++ button.remove(); ++ }); ++ widget.addButtons = []; ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2011-2012 Henri Bergius, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false window:false VIE:false */ ++ 'use strict'; ++ ++ // Define Create's EditableEntity widget. ++ jQuery.widget('Midgard.midgardEditable', { ++ options: { ++ propertyEditors: {}, ++ collections: [], ++ model: null, ++ // the configuration (mapping and options) of property editor widgets ++ propertyEditorWidgetsConfiguration: { ++ hallo: { ++ widget: 'halloWidget', ++ options: {} ++ } ++ }, ++ // the available property editor widgets by data type ++ propertyEditorWidgets: { ++ 'default': 'hallo' ++ }, ++ collectionWidgets: { ++ 'default': 'midgardCollectionAdd' ++ }, ++ toolbarState: 'full', ++ vie: null, ++ domService: 'rdfa', ++ predicateSelector: '[property]', ++ disabled: false, ++ localize: function (id, language) { ++ return window.midgardCreate.localize(id, language); ++ }, ++ language: null, ++ // Current state of the Editable ++ state: null, ++ // Callback function for validating changes between states. Receives the previous state, new state, possibly property, and a callback ++ acceptStateChange: true, ++ // Callback function for listening (and reacting) to state changes. ++ stateChange: null, ++ // Callback function for decorating the full editable. Will be called on instantiation ++ decorateEditableEntity: null, ++ // Callback function for decorating a single property editor widget. Will ++ // be called on editing widget instantiation. ++ decoratePropertyEditor: null, ++ ++ // Deprecated. ++ editables: [], // Now `propertyEditors`. ++ editors: {}, // Now `propertyEditorWidgetsConfiguration`. ++ widgets: {} // Now `propertyEditorW ++ }, ++ ++ // Aids in consistently passing parameters to events and callbacks. ++ _params: function(predicate, extended) { ++ var entityParams = { ++ entity: this.options.model, ++ editableEntity: this, ++ entityElement: this.element, ++ ++ // Deprecated. ++ editable: this, ++ element: this.element, ++ instance: this.options.model ++ }; ++ ++ var propertyParams = (predicate) ? { ++ predicate: predicate, ++ propertyEditor: this.options.propertyEditors[predicate], ++ propertyElement: this.options.propertyEditors[predicate].element, ++ ++ // Deprecated. ++ property: predicate, ++ element: this.options.propertyEditors[predicate].element ++ } : {}; ++ ++ return _.extend(entityParams, propertyParams, extended); ++ }, ++ ++ _create: function () { ++ // Backwards compatibility: ++ // - this.options.propertyEditorWidgets used to be this.options.widgets ++ // - this.options.propertyEditorWidgetsConfiguration used to be ++ // this.options.editors ++ if (this.options.widgets) { ++ this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); ++ } ++ if (this.options.editors) { ++ this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); ++ } ++ ++ this.vie = this.options.vie; ++ this.domService = this.vie.service(this.options.domService); ++ if (!this.options.model) { ++ var widget = this; ++ this.vie.load({ ++ element: this.element ++ }).from(this.options.domService).execute().done(function (entities) { ++ widget.options.model = entities[0]; ++ }); ++ } ++ if (_.isFunction(this.options.decorateEditableEntity)) { ++ this.options.decorateEditableEntity(this._params()); ++ } ++ }, ++ ++ _init: function () { ++ // Backwards compatibility: ++ // - this.options.propertyEditorWidgets used to be this.options.widgets ++ // - this.options.propertyEditorWidgetsConfiguration used to be ++ // this.options.editors ++ if (this.options.widgets) { ++ this.options.propertyEditorWidgets = _.extend(this.options.propertyEditorWidgets, this.options.widgets); ++ } ++ if (this.options.editors) { ++ this.options.propertyEditorWidgetsConfiguration = _.extend(this.options.propertyEditorWidgetsConfiguration, this.options.editors); ++ } ++ ++ // Old way of setting the widget inactive ++ if (this.options.disabled === true) { ++ this.setState('inactive'); ++ return; ++ } ++ ++ if (this.options.disabled === false && this.options.state === 'inactive') { ++ this.setState('candidate'); ++ return; ++ } ++ this.options.disabled = false; ++ ++ if (this.options.state) { ++ this.setState(this.options.state); ++ return; ++ } ++ this.setState('candidate'); ++ }, ++ ++ // Method used for cycling between the different states of the Editable widget: ++ // ++ // * Inactive: editable is loaded but disabled ++ // * Candidate: editable is enabled but not activated ++ // * Highlight: user is hovering over the editable (not set by Editable widget directly) ++ // * Activating: an editor widget is being activated for user to edit with it (skipped for editors that activate instantly) ++ // * Active: user is actually editing something inside the editable ++ // * Changed: user has made changes to the editable ++ // * Invalid: the contents of the editable have validation errors ++ // ++ // In situations where state changes are triggered for a particular property editor, the `predicate` ++ // argument will provide the name of that property. ++ // ++ // State changes may carry optional context information in a JavaScript object. The payload of these context objects is not ++ // standardized, and is meant to be set and used by the application controller ++ // ++ // The callback parameter is optional and will be invoked after a state change has been accepted (after the 'statechange' ++ // event) or rejected. ++ setState: function (state, predicate, context, callback) { ++ var previous = this.options.state; ++ var current = state; ++ if (current === previous) { ++ return; ++ } ++ ++ if (this.options.acceptStateChange === undefined || !_.isFunction(this.options.acceptStateChange)) { ++ // Skip state transition validation ++ this._doSetState(previous, current, predicate, context); ++ if (_.isFunction(callback)) { ++ callback(true); ++ } ++ return; ++ } ++ ++ var widget = this; ++ this.options.acceptStateChange(previous, current, predicate, context, function (accepted) { ++ if (accepted) { ++ widget._doSetState(previous, current, predicate, context); ++ } ++ if (_.isFunction(callback)) { ++ callback(accepted); ++ } ++ return; ++ }); ++ }, ++ ++ getState: function () { ++ return this.options.state; ++ }, ++ ++ _doSetState: function (previous, current, predicate, context) { ++ this.options.state = current; ++ if (current === 'inactive') { ++ this.disable(); ++ } else if ((previous === null || previous === 'inactive') && current !== 'inactive') { ++ this.enable(); ++ } ++ ++ this._trigger('statechange', null, this._params(predicate, { ++ previous: previous, ++ current: current, ++ context: context ++ })); ++ }, ++ ++ findEditablePredicateElements: function (callback) { ++ this.domService.findPredicateElements(this.options.model.id, jQuery(this.options.predicateSelector, this.element), false).each(callback); ++ }, ++ ++ getElementPredicate: function (element) { ++ return this.domService.getElementPredicate(element); ++ }, ++ ++ enable: function () { ++ var editableEntity = this; ++ if (!this.options.model) { ++ return; ++ } ++ ++ this.findEditablePredicateElements(function () { ++ editableEntity._enablePropertyEditor(jQuery(this)); ++ }); ++ ++ this._trigger('enable', null, this._params()); ++ ++ _.each(this.domService.views, function (view) { ++ if (view instanceof this.vie.view.Collection && this.options.model === view.owner) { ++ var predicate = view.collection.predicate; ++ var editableOptions = _.clone(this.options); ++ editableOptions.state = null; ++ var collection = this.enableCollection({ ++ model: this.options.model, ++ collection: view.collection, ++ property: predicate, ++ definition: this.getAttributeDefinition(predicate), ++ view: view, ++ element: view.el, ++ vie: editableEntity.vie, ++ editableOptions: editableOptions ++ }); ++ editableEntity.options.collections.push(collection); ++ } ++ }, this); ++ }, ++ ++ disable: function () { ++ _.each(this.options.propertyEditors, function (editable) { ++ this.disableEditor({ ++ widget: this, ++ editable: editable, ++ entity: this.options.model, ++ element: jQuery(editable) ++ }); ++ }, this); ++ this.options.propertyEditors = {}; ++ ++ // Deprecated. ++ this.options.editables = []; ++ ++ _.each(this.options.collections, function (collectionWidget) { ++ var editableOptions = _.clone(this.options); ++ editableOptions.state = 'inactive'; ++ this.disableCollection({ ++ widget: this, ++ model: this.options.model, ++ element: collectionWidget, ++ vie: this.vie, ++ editableOptions: editableOptions ++ }); ++ }, this); ++ this.options.collections = []; ++ ++ this._trigger('disable', null, this._params()); ++ }, ++ ++ _enablePropertyEditor: function (element) { ++ var widget = this; ++ var predicate = this.getElementPredicate(element); ++ if (!predicate) { ++ return true; ++ } ++ if (this.options.model.get(predicate) instanceof Array) { ++ // For now we don't deal with multivalued properties in the editable ++ return true; ++ } ++ ++ var propertyElement = this.enablePropertyEditor({ ++ widget: this, ++ element: element, ++ entity: this.options.model, ++ property: predicate, ++ vie: this.vie, ++ decorate: this.options.decoratePropertyEditor, ++ decorateParams: _.bind(this._params, this), ++ changed: function (content) { ++ widget.setState('changed', predicate); ++ ++ var changedProperties = {}; ++ changedProperties[predicate] = content; ++ widget.options.model.set(changedProperties, { ++ silent: true ++ }); ++ ++ widget._trigger('changed', null, widget._params(predicate)); ++ }, ++ activating: function () { ++ widget.setState('activating', predicate); ++ }, ++ activated: function () { ++ widget.setState('active', predicate); ++ widget._trigger('activated', null, widget._params(predicate)); ++ }, ++ deactivated: function () { ++ widget.setState('candidate', predicate); ++ widget._trigger('deactivated', null, widget._params(predicate)); ++ } ++ }); ++ ++ if (!propertyElement) { ++ return; ++ } ++ var widgetType = propertyElement.data('createWidgetName'); ++ this.options.propertyEditors[predicate] = propertyElement.data(widgetType); ++ ++ // Deprecated. ++ this.options.editables.push(propertyElement); ++ ++ this._trigger('enableproperty', null, this._params(predicate)); ++ }, ++ ++ // returns the name of the property editor widget to use for the given property ++ _propertyEditorName: function (data) { ++ if (this.options.propertyEditorWidgets[data.property] !== undefined) { ++ // Property editor widget configuration set for specific RDF predicate ++ return this.options.propertyEditorWidgets[data.property]; ++ } ++ ++ // Load the property editor widget configuration for the data type ++ var propertyType = 'default'; ++ var attributeDefinition = this.getAttributeDefinition(data.property); ++ if (attributeDefinition) { ++ propertyType = attributeDefinition.range[0]; ++ } ++ if (this.options.propertyEditorWidgets[propertyType] !== undefined) { ++ return this.options.propertyEditorWidgets[propertyType]; ++ } ++ return this.options.propertyEditorWidgets['default']; ++ }, ++ ++ _propertyEditorWidget: function (editor) { ++ return this.options.propertyEditorWidgetsConfiguration[editor].widget; ++ }, ++ ++ _propertyEditorOptions: function (editor) { ++ return this.options.propertyEditorWidgetsConfiguration[editor].options; ++ }, ++ ++ getAttributeDefinition: function (property) { ++ var type = this.options.model.get('@type'); ++ if (!type) { ++ return; ++ } ++ if (!type.attributes) { ++ return; ++ } ++ return type.attributes.get(property); ++ }, ++ ++ // Deprecated. ++ enableEditor: function (data) { ++ return this.enablePropertyEditor(data); ++ }, ++ ++ enablePropertyEditor: function (data) { ++ var editorName = this._propertyEditorName(data); ++ if (editorName === null) { ++ return; ++ } ++ ++ var editorWidget = this._propertyEditorWidget(editorName); ++ ++ data.editorOptions = this._propertyEditorOptions(editorName); ++ data.toolbarState = this.options.toolbarState; ++ data.disabled = false; ++ // Pass metadata that could be useful for some implementations. ++ data.editorName = editorName; ++ data.editorWidget = editorWidget; ++ ++ if (typeof jQuery(data.element)[editorWidget] !== 'function') { ++ throw new Error(editorWidget + ' widget is not available'); ++ } ++ ++ jQuery(data.element)[editorWidget](data); ++ jQuery(data.element).data('createWidgetName', editorWidget); ++ return jQuery(data.element); ++ }, ++ ++ // Deprecated. ++ disableEditor: function (data) { ++ return this.disablePropertyEditor(data); ++ }, ++ ++ disablePropertyEditor: function (data) { ++ var widgetName = jQuery(data.element).data('createWidgetName'); ++ ++ data.disabled = true; ++ ++ if (widgetName) { ++ // only if there has been an editing widget registered ++ jQuery(data.element)[widgetName](data); ++ jQuery(data.element).removeClass('ui-state-disabled'); ++ ++ if (data.element.is(':focus')) { ++ data.element.blur(); ++ } ++ } ++ }, ++ ++ collectionWidgetName: function (data) { ++ if (this.options.collectionWidgets[data.property] !== undefined) { ++ // Widget configuration set for specific RDF predicate ++ return this.options.collectionWidgets[data.property]; ++ } ++ ++ var propertyType = 'default'; ++ var attributeDefinition = this.getAttributeDefinition(data.property); ++ if (attributeDefinition) { ++ propertyType = attributeDefinition.range[0]; ++ } ++ if (this.options.collectionWidgets[propertyType] !== undefined) { ++ return this.options.collectionWidgets[propertyType]; ++ } ++ return this.options.collectionWidgets['default']; ++ }, ++ ++ enableCollection: function (data) { ++ var widgetName = this.collectionWidgetName(data); ++ if (widgetName === null) { ++ return; ++ } ++ data.disabled = false; ++ if (typeof jQuery(data.element)[widgetName] !== 'function') { ++ throw new Error(widgetName + ' widget is not available'); ++ } ++ jQuery(data.element)[widgetName](data); ++ jQuery(data.element).data('createCollectionWidgetName', widgetName); ++ return jQuery(data.element); ++ }, ++ ++ disableCollection: function (data) { ++ var widgetName = jQuery(data.element).data('createCollectionWidgetName'); ++ if (widgetName === null) { ++ return; ++ } ++ data.disabled = true; ++ if (widgetName) { ++ // only if there has been an editing widget registered ++ jQuery(data.element)[widgetName](data); ++ jQuery(data.element).removeClass('ui-state-disabled'); ++ } ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2012 Tobias Herrmann, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false document:false */ ++ 'use strict'; ++ ++ // # Base property editor widget ++ // ++ // This property editor widget provides a very simplistic `contentEditable` ++ // property editor that can be used as standalone, but should more usually be ++ // used as the base class for other property editor widgets. ++ // This property editor widget is only useful for textual properties! ++ // ++ // Subclassing this base property editor widget is easy: ++ // ++ // jQuery.widget('Namespace.MyWidget', jQuery.Create.editWidget, { ++ // // override any properties ++ // }); ++ jQuery.widget('Create.editWidget', { ++ options: { ++ disabled: false, ++ vie: null ++ }, ++ // override to enable the widget ++ enable: function () { ++ this.element.attr('contenteditable', 'true'); ++ }, ++ // override to disable the widget ++ disable: function (disable) { ++ this.element.attr('contenteditable', 'false'); ++ }, ++ // called by the jQuery UI plugin factory when creating the property editor ++ // widget instance ++ _create: function () { ++ this._registerWidget(); ++ this._initialize(); ++ ++ if (_.isFunction(this.options.decorate) && _.isFunction(this.options.decorateParams)) { ++ // TRICKY: we can't use this.options.decorateParams()'s 'propertyName' ++ // parameter just yet, because it will only be available after this ++ // object has been created, but we're currently in the constructor! ++ // Hence we have to duplicate part of its logic here. ++ this.options.decorate(this.options.decorateParams(null, { ++ propertyName: this.options.property, ++ propertyEditor: this, ++ propertyElement: this.element, ++ // Deprecated. ++ editor: this, ++ predicate: this.options.property, ++ element: this.element ++ })); ++ } ++ }, ++ // called every time the property editor widget is called ++ _init: function () { ++ if (this.options.disabled) { ++ this.disable(); ++ return; ++ } ++ this.enable(); ++ }, ++ // override this function to initialize the property editor widget functions ++ _initialize: function () { ++ var self = this; ++ this.element.bind('focus', function () { ++ if (self.options.disabled) { ++ return; ++ } ++ self.options.activated(); ++ }); ++ this.element.bind('blur', function () { ++ if (self.options.disabled) { ++ return; ++ } ++ self.options.deactivated(); ++ }); ++ var before = this.element.html(); ++ this.element.bind('keyup paste', function (event) { ++ if (self.options.disabled) { ++ return; ++ } ++ var current = jQuery(this).html(); ++ if (before !== current) { ++ before = current; ++ self.options.changed(current); ++ } ++ }); ++ }, ++ // used to register the property editor widget name with the DOM element ++ _registerWidget: function () { ++ this.element.data("createWidgetName", this.widgetName); ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2012 Tobias Herrmann, IKS Consortium ++// (c) 2011 Rene Kapusta, Evo42 ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false document:false Aloha:false */ ++ 'use strict'; ++ ++ // # Aloha editing widget ++ // ++ // This widget allows editing textual contents using the ++ // [Aloha](http://aloha-editor.org) rich text editor. ++ // ++ // Due to licensing incompatibilities, Aloha Editor needs to be installed ++ // and configured separately. ++ jQuery.widget('Create.alohaWidget', jQuery.Create.editWidget, { ++ _initialize: function () {}, ++ enable: function () { ++ var options = this.options; ++ var editable; ++ var currentElement = Aloha.jQuery(options.element.get(0)).aloha(); ++ _.each(Aloha.editables, function (aloha) { ++ // Find the actual editable instance so we can hook to the events ++ // correctly ++ if (aloha.obj.get(0) === currentElement.get(0)) { ++ editable = aloha; ++ } ++ }); ++ if (!editable) { ++ return; ++ } ++ editable.vieEntity = options.entity; ++ ++ // Subscribe to activation and deactivation events ++ Aloha.bind('aloha-editable-activated', function (event, data) { ++ if (data.editable !== editable) { ++ return; ++ } ++ options.activated(); ++ }); ++ Aloha.bind('aloha-editable-deactivated', function (event, data) { ++ if (data.editable !== editable) { ++ return; ++ } ++ options.deactivated(); ++ }); ++ ++ Aloha.bind('aloha-smart-content-changed', function (event, data) { ++ if (data.editable !== editable) { ++ return; ++ } ++ if (!data.editable.isModified()) { ++ return true; ++ } ++ options.changed(data.editable.getContents()); ++ data.editable.setUnmodified(); ++ }); ++ this.options.disabled = false; ++ }, ++ disable: function () { ++ Aloha.jQuery(this.options.element.get(0)).mahalo(); ++ this.options.disabled = true; ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2012 Tobias Herrmann, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false document:false CKEDITOR:false */ ++ 'use strict'; ++ ++ // # CKEditor editing widget ++ // ++ // This widget allows editing textual content areas with the ++ // [CKEditor](http://ckeditor.com/) rich text editor. ++ jQuery.widget('Create.ckeditorWidget', jQuery.Create.editWidget, { ++ enable: function () { ++ this.element.attr('contentEditable', 'true'); ++ this.editor = CKEDITOR.inline(this.element.get(0)); ++ this.options.disabled = false; ++ ++ var widget = this; ++ this.editor.on('focus', function () { ++ widget.options.activated(); ++ }); ++ this.editor.on('blur', function () { ++ widget.options.activated(); ++ }); ++ this.editor.on('key', function () { ++ widget.options.changed(widget.editor.getData()); ++ }); ++ this.editor.on('paste', function () { ++ widget.options.changed(widget.editor.getData()); ++ }); ++ this.editor.on('afterCommandExec', function () { ++ widget.options.changed(widget.editor.getData()); ++ }); ++ }, ++ ++ disable: function () { ++ if (!this.editor) { ++ return; ++ } ++ this.element.attr('contentEditable', 'false'); ++ this.editor.destroy(); ++ this.editor = null; ++ }, ++ ++ _initialize: function () { ++ CKEDITOR.disableAutoInline = true; ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2012 Tobias Herrmann, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false document:false */ ++ 'use strict'; ++ ++ // # Hallo editing widget ++ // ++ // This widget allows editing textual content areas with the ++ // [Hallo](http://hallojs.org) rich text editor. ++ jQuery.widget('Create.halloWidget', jQuery.Create.editWidget, { ++ options: { ++ editorOptions: {}, ++ disabled: true, ++ toolbarState: 'full', ++ vie: null, ++ entity: null ++ }, ++ enable: function () { ++ jQuery(this.element).hallo({ ++ editable: true ++ }); ++ this.options.disabled = false; ++ }, ++ ++ disable: function () { ++ jQuery(this.element).hallo({ ++ editable: false ++ }); ++ this.options.disabled = true; ++ }, ++ ++ _initialize: function () { ++ jQuery(this.element).hallo(this.getHalloOptions()); ++ var self = this; ++ jQuery(this.element).bind('halloactivated', function (event, data) { ++ self.options.activated(); ++ }); ++ jQuery(this.element).bind('hallodeactivated', function (event, data) { ++ self.options.deactivated(); ++ }); ++ jQuery(this.element).bind('hallomodified', function (event, data) { ++ self.options.changed(data.content); ++ data.editable.setUnmodified(); ++ }); ++ ++ jQuery(document).bind('midgardtoolbarstatechange', function(event, data) { ++ // Switch between Hallo configurations when toolbar state changes ++ if (data.display === self.options.toolbarState) { ++ return; ++ } ++ self.options.toolbarState = data.display; ++ var newOptions = self.getHalloOptions(); ++ self.element.hallo('changeToolbar', newOptions.parentElement, newOptions.toolbar, true); ++ }); ++ }, ++ ++ getHalloOptions: function() { ++ var defaults = { ++ plugins: { ++ halloformat: {}, ++ halloblock: {}, ++ hallolists: {}, ++ hallolink: {}, ++ halloimage: { ++ entity: this.options.entity ++ } ++ }, ++ buttonCssClass: 'create-ui-btn-small', ++ placeholder: '[' + this.options.property + ']' ++ }; ++ ++ if (typeof this.element.annotate === 'function' && this.options.vie.services.stanbol) { ++ // Enable Hallo Annotate plugin by default if user has annotate.js ++ // loaded and VIE has Stanbol enabled ++ defaults.plugins.halloannotate = { ++ vie: this.options.vie ++ }; ++ } ++ ++ if (this.options.toolbarState === 'full') { ++ // Use fixed toolbar in the Create tools area ++ defaults.parentElement = jQuery('.create-ui-toolbar-dynamictoolarea .create-ui-tool-freearea'); ++ defaults.toolbar = 'halloToolbarFixed'; ++ } else { ++ // Tools area minimized, use floating toolbar ++ defaults.parentElement = 'body'; ++ defaults.toolbar = 'halloToolbarContextual'; ++ } ++ return _.extend(defaults, this.options.editorOptions); ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2012 Henri Bergius, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false document:false */ ++ 'use strict'; ++ ++ // # Redactor editing widget ++ // ++ // This widget allows editing textual content areas with the ++ // [Redactor](http://redactorjs.com/) rich text editor. ++ jQuery.widget('Create.redactorWidget', jQuery.Create.editWidget, { ++ editor: null, ++ ++ options: { ++ editorOptions: {}, ++ disabled: true ++ }, ++ ++ enable: function () { ++ jQuery(this.element).redactor(this.getRedactorOptions()); ++ this.options.disabled = false; ++ }, ++ ++ disable: function () { ++ jQuery(this.element).destroyEditor(); ++ this.options.disabled = true; ++ }, ++ ++ _initialize: function () { ++ var self = this; ++ jQuery(this.element).bind('focus', function (event) { ++ self.options.activated(); ++ }); ++ /* ++ jQuery(this.element).bind('blur', function (event) { ++ self.options.deactivated(); ++ }); ++ */ ++ }, ++ ++ getRedactorOptions: function () { ++ var self = this; ++ var overrides = { ++ keyupCallback: function (obj, event) { ++ self.options.changed(jQuery(self.element).getCode()); ++ }, ++ execCommandCallback: function (obj, command) { ++ self.options.changed(jQuery(self.element).getCode()); ++ } ++ }; ++ ++ return _.extend(self.options.editorOptions, overrides); ++ } ++ }); ++})(jQuery); ++// Create.js - On-site web editing interface ++// (c) 2011-2012 Henri Bergius, IKS Consortium ++// Create may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://createjs.org/ ++(function (jQuery, undefined) { ++ // Run JavaScript in strict mode ++ /*global jQuery:false _:false window:false */ ++ 'use strict'; ++ ++ jQuery.widget('Midgard.midgardStorage', { ++ saveEnabled: true, ++ options: { ++ // Whether to use localstorage ++ localStorage: false, ++ removeLocalstorageOnIgnore: true, ++ // VIE instance to use for storage handling ++ vie: null, ++ // URL callback for Backbone.sync ++ url: '', ++ // Whether to enable automatic saving ++ autoSave: false, ++ // How often to autosave in milliseconds ++ autoSaveInterval: 5000, ++ // Whether to save entities that are referenced by entities ++ // we're saving to the server. ++ saveReferencedNew: false, ++ saveReferencedChanged: false, ++ // Namespace used for events from midgardEditable-derived widget ++ editableNs: 'midgardeditable', ++ // CSS selector for the Edit button, leave to null to not bind ++ // notifications to any element ++ editSelector: '#midgardcreate-edit a', ++ localize: function (id, language) { ++ return window.midgardCreate.localize(id, language); ++ }, ++ language: null ++ }, ++ ++ _create: function () { ++ var widget = this; ++ this.changedModels = []; ++ ++ if (window.localStorage) { ++ this.options.localStorage = true; ++ } ++ ++ this.vie = this.options.vie; ++ ++ this.vie.entities.bind('add', function (model) { ++ // Add the back-end URL used by Backbone.sync ++ model.url = widget.options.url; ++ model.toJSON = model.toJSONLD; ++ }); ++ ++ widget._bindEditables(); ++ if (widget.options.autoSave) { ++ widget._autoSave(); ++ } ++ }, ++ ++ _autoSave: function () { ++ var widget = this; ++ widget.saveEnabled = true; ++ ++ var doAutoSave = function () { ++ if (!widget.saveEnabled) { ++ return; ++ } ++ ++ if (widget.changedModels.length === 0) { ++ return; ++ } ++ ++ widget.saveRemoteAll({ ++ // We make autosaves silent so that potential changes from server ++ // don't disrupt user while writing. ++ silent: true ++ }); ++ }; ++ ++ var timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); ++ ++ this.element.bind('startPreventSave', function () { ++ if (timeout) { ++ window.clearInterval(timeout); ++ timeout = null; ++ } ++ widget.disableAutoSave(); ++ }); ++ this.element.bind('stopPreventSave', function () { ++ if (!timeout) { ++ timeout = window.setInterval(doAutoSave, widget.options.autoSaveInterval); ++ } ++ widget.enableAutoSave(); ++ }); ++ ++ }, ++ ++ enableAutoSave: function () { ++ this.saveEnabled = true; ++ }, ++ ++ disableAutoSave: function () { ++ this.saveEnabled = false; ++ }, ++ ++ _bindEditables: function () { ++ var widget = this; ++ this.restorables = []; ++ var restorer; ++ ++ widget.element.bind(widget.options.editableNs + 'changed', function (event, options) { ++ if (_.indexOf(widget.changedModels, options.instance) === -1) { ++ widget.changedModels.push(options.instance); ++ } ++ widget._saveLocal(options.instance); ++ }); ++ ++ widget.element.bind(widget.options.editableNs + 'disable', function (event, options) { ++ widget._restoreLocal(options.instance); ++ }); ++ ++ widget.element.bind(widget.options.editableNs + 'enable', function (event, options) { ++ if (!options.instance._originalAttributes) { ++ options.instance._originalAttributes = _.clone(options.instance.attributes); ++ } ++ ++ if (!options.instance.isNew() && widget._checkLocal(options.instance)) { ++ // We have locally-stored modifications, user needs to be asked ++ widget.restorables.push(options.instance); ++ } ++ ++ /*_.each(options.instance.attributes, function (attributeValue, property) { ++ if (attributeValue instanceof widget.vie.Collection) { ++ widget._readLocalReferences(options.instance, property, attributeValue); ++ } ++ });*/ ++ }); ++ ++ widget.element.bind('midgardcreatestatechange', function (event, options) { ++ if (options.state === 'browse' || widget.restorables.length === 0) { ++ widget.restorables = []; ++ if (restorer) { ++ restorer.close(); ++ } ++ return; ++ } ++ restorer = widget.checkRestore(); ++ }); ++ ++ widget.element.bind('midgardstorageloaded', function (event, options) { ++ if (_.indexOf(widget.changedModels, options.instance) === -1) { ++ widget.changedModels.push(options.instance); ++ } ++ }); ++ }, ++ ++ checkRestore: function () { ++ var widget = this; ++ if (widget.restorables.length === 0) { ++ return; ++ } ++ ++ var message; ++ var restorer; ++ if (widget.restorables.length === 1) { ++ message = _.template(widget.options.localize('localModification', widget.options.language), { ++ label: widget.restorables[0].getSubjectUri() ++ }); ++ } else { ++ message = _.template(widget.options.localize('localModifications', widget.options.language), { ++ number: widget.restorables.length ++ }); ++ } ++ ++ var doRestore = function (event, notification) { ++ widget.restoreLocal(); ++ restorer.close(); ++ }; ++ ++ var doIgnore = function (event, notification) { ++ widget.ignoreLocal(); ++ restorer.close(); ++ }; ++ ++ restorer = jQuery('body').midgardNotifications('create', { ++ bindTo: widget.options.editSelector, ++ gravity: 'TR', ++ body: message, ++ timeout: 0, ++ actions: [ ++ { ++ name: 'restore', ++ label: widget.options.localize('Restore', widget.options.language), ++ cb: doRestore, ++ className: 'create-ui-btn' ++ }, ++ { ++ name: 'ignore', ++ label: widget.options.localize('Ignore', widget.options.language), ++ cb: doIgnore, ++ className: 'create-ui-btn' ++ } ++ ], ++ callbacks: { ++ beforeShow: function () { ++ if (!window.Mousetrap) { ++ return; ++ } ++ window.Mousetrap.bind(['command+shift+r', 'ctrl+shift+r'], function (event) { ++ event.preventDefault(); ++ doRestore(); ++ }); ++ window.Mousetrap.bind(['command+shift+i', 'ctrl+shift+i'], function (event) { ++ event.preventDefault(); ++ doIgnore(); ++ }); ++ }, ++ afterClose: function () { ++ if (!window.Mousetrap) { ++ return; ++ } ++ window.Mousetrap.unbind(['command+shift+r', 'ctrl+shift+r']); ++ window.Mousetrap.unbind(['command+shift+i', 'ctrl+shift+i']); ++ } ++ } ++ }); ++ return restorer; ++ }, ++ ++ restoreLocal: function () { ++ _.each(this.restorables, function (instance) { ++ this.readLocal(instance); ++ }, this); ++ this.restorables = []; ++ }, ++ ++ ignoreLocal: function () { ++ if (this.options.removeLocalstorageOnIgnore) { ++ _.each(this.restorables, function (instance) { ++ this._removeLocal(instance); ++ }, this); ++ } ++ this.restorables = []; ++ }, ++ ++ saveReferences: function (model) { ++ _.each(model.attributes, function (value, property) { ++ if (!value || !value.isCollection) { ++ return; ++ } ++ ++ value.each(function (referencedModel) { ++ if (this.changedModels.indexOf(referencedModel) !== -1) { ++ // The referenced model is already in the save queue ++ return; ++ } ++ ++ if (referencedModel.isNew() && this.options.saveReferencedNew) { ++ return referencedModel.save(); ++ } ++ ++ if (referencedModel.hasChanged() && this.options.saveReferencedChanged) { ++ return referencedModel.save(); ++ } ++ }, this); ++ }, this); ++ }, ++ ++ saveRemote: function (model, options) { ++ // Optionally handle entities referenced in this model first ++ this.saveReferences(model); ++ ++ this._trigger('saveentity', null, { ++ entity: model, ++ options: options ++ }); ++ ++ var widget = this; ++ model.save(null, _.extend({}, options, { ++ success: function (m, response) { ++ // From now on we're going with the values we have on server ++ model._originalAttributes = _.clone(model.attributes); ++ widget._removeLocal(model); ++ window.setTimeout(function () { ++ // Remove the model from the list of changed models after saving ++ widget.changedModels.splice(widget.changedModels.indexOf(model), 1); ++ }, 0); ++ if (_.isFunction(options.success)) { ++ options.success(m, response); ++ } ++ widget._trigger('savedentity', null, { ++ entity: model, ++ options: options ++ }); ++ }, ++ error: function (m, response) { ++ if (_.isFunction(options.error)) { ++ options.error(m, response); ++ } ++ } ++ })); ++ }, ++ ++ saveRemoteAll: function (options) { ++ var widget = this; ++ if (widget.changedModels.length === 0) { ++ return; ++ } ++ ++ widget._trigger('save', null, { ++ entities: widget.changedModels, ++ options: options, ++ // Deprecated ++ models: widget.changedModels ++ }); ++ ++ var notification_msg; ++ var needed = widget.changedModels.length; ++ if (needed > 1) { ++ notification_msg = _.template(widget.options.localize('saveSuccessMultiple', widget.options.language), { ++ number: needed ++ }); ++ } else { ++ notification_msg = _.template(widget.options.localize('saveSuccess', widget.options.language), { ++ label: widget.changedModels[0].getSubjectUri() ++ }); ++ } ++ ++ widget.disableAutoSave(); ++ _.each(widget.changedModels, function (model) { ++ this.saveRemote(model, { ++ success: function (m, response) { ++ needed--; ++ if (needed <= 0) { ++ // All models were happily saved ++ widget._trigger('saved', null, { ++ options: options ++ }); ++ if (options && _.isFunction(options.success)) { ++ options.success(m, response); ++ } ++ jQuery('body').midgardNotifications('create', { ++ body: notification_msg ++ }); ++ widget.enableAutoSave(); ++ } ++ }, ++ error: function (m, err) { ++ if (options && _.isFunction(options.error)) { ++ options.error(m, err); ++ } ++ jQuery('body').midgardNotifications('create', { ++ body: _.template(widget.options.localize('saveError', widget.options.language), { ++ error: err.responseText || '' ++ }), ++ timeout: 0 ++ }); ++ ++ widget._trigger('error', null, { ++ instance: model ++ }); ++ } ++ }); ++ }, this); ++ }, ++ ++ _saveLocal: function (model) { ++ if (!this.options.localStorage) { ++ return; ++ } ++ ++ if (model.isNew()) { ++ // Anonymous object, save as refs instead ++ if (!model.primaryCollection) { ++ return; ++ } ++ return this._saveLocalReferences(model.primaryCollection.subject, model.primaryCollection.predicate, model); ++ } ++ window.localStorage.setItem(model.getSubjectUri(), JSON.stringify(model.toJSONLD())); ++ }, ++ ++ _getReferenceId: function (model, property) { ++ return model.id + ':' + property; ++ }, ++ ++ _saveLocalReferences: function (subject, predicate, model) { ++ if (!this.options.localStorage) { ++ return; ++ } ++ ++ if (!subject || !predicate) { ++ return; ++ } ++ ++ var widget = this; ++ var identifier = subject + ':' + predicate; ++ var json = model.toJSONLD(); ++ if (window.localStorage.getItem(identifier)) { ++ var referenceList = JSON.parse(window.localStorage.getItem(identifier)); ++ var index = _.pluck(referenceList, '@').indexOf(json['@']); ++ if (index !== -1) { ++ referenceList[index] = json; ++ } else { ++ referenceList.push(json); ++ } ++ window.localStorage.setItem(identifier, JSON.stringify(referenceList)); ++ return; ++ } ++ window.localStorage.setItem(identifier, JSON.stringify([json])); ++ }, ++ ++ _checkLocal: function (model) { ++ if (!this.options.localStorage) { ++ return false; ++ } ++ ++ var local = window.localStorage.getItem(model.getSubjectUri()); ++ if (!local) { ++ return false; ++ } ++ ++ return true; ++ }, ++ ++ hasLocal: function (model) { ++ if (!this.options.localStorage) { ++ return false; ++ } ++ ++ if (!window.localStorage.getItem(model.getSubjectUri())) { ++ return false; ++ } ++ return true; ++ }, ++ ++ readLocal: function (model) { ++ if (!this.options.localStorage) { ++ return; ++ } ++ ++ var local = window.localStorage.getItem(model.getSubjectUri()); ++ if (!local) { ++ return; ++ } ++ if (!model._originalAttributes) { ++ model._originalAttributes = _.clone(model.attributes); ++ } ++ var parsed = JSON.parse(local); ++ var entity = this.vie.entities.addOrUpdate(parsed, { ++ overrideAttributes: true ++ }); ++ ++ this._trigger('loaded', null, { ++ instance: entity ++ }); ++ }, ++ ++ _readLocalReferences: function (model, property, collection) { ++ if (!this.options.localStorage) { ++ return; ++ } ++ ++ var identifier = this._getReferenceId(model, property); ++ var local = window.localStorage.getItem(identifier); ++ if (!local) { ++ return; ++ } ++ collection.add(JSON.parse(local)); ++ }, ++ ++ _restoreLocal: function (model) { ++ var widget = this; ++ ++ // Remove unsaved collection members ++ if (!model) { return; } ++ _.each(model.attributes, function (attributeValue, property) { ++ if (attributeValue instanceof widget.vie.Collection) { ++ var removables = []; ++ attributeValue.forEach(function (model) { ++ if (model.isNew()) { ++ removables.push(model); ++ } ++ }); ++ attributeValue.remove(removables); ++ } ++ }); ++ ++ // Restore original object properties ++ if (!model.changedAttributes()) { ++ if (model._originalAttributes) { ++ model.set(model._originalAttributes); ++ } ++ return; ++ } ++ ++ model.set(model.previousAttributes()); ++ }, ++ ++ _removeLocal: function (model) { ++ if (!this.options.localStorage) { ++ return; ++ } ++ ++ window.localStorage.removeItem(model.getSubjectUri()); ++ } ++ }); ++})(jQuery); ++if (window.midgardCreate === undefined) { ++ window.midgardCreate = {}; ++} ++if (window.midgardCreate.locale === undefined) { ++ window.midgardCreate.locale = {}; ++} ++ ++window.midgardCreate.locale.en = { ++ // Session-state buttons for the main toolbar ++ 'Save': 'Save', ++ 'Saving': 'Saving', ++ 'Cancel': 'Cancel', ++ 'Edit': 'Edit', ++ // Storage status messages ++ 'localModification': 'Item "<%= label %>" has local modifications', ++ 'localModifications': '<%= number %> items on this page have local modifications', ++ 'Restore': 'Restore', ++ 'Ignore': 'Ignore', ++ 'saveSuccess': 'Item "<%= label %>" saved successfully', ++ 'saveSuccessMultiple': '<%= number %> items saved successfully', ++ 'saveError': 'Error occurred while saving
<%= error %>', ++ // Tagging ++ 'Item tags': 'Item tags', ++ 'Suggested tags': 'Suggested tags', ++ 'Tags': 'Tags', ++ 'add a tag': 'add a tag', ++ // Collection widgets ++ 'Add': 'Add', ++ 'Choose type to add': 'Choose type to add' ++}; +diff --git a/core/misc/vie/vie-core.js b/core/misc/vie/vie-core.js +new file mode 100644 +index 0000000..ca5c079 +--- /dev/null ++++ b/core/misc/vie/vie-core.js +@@ -0,0 +1,3682 @@ ++/*Copyright (c) 2011 Henri Bergius, IKS Consortium ++Copyright (c) 2011 Sebastian Germesin, IKS Consortium ++ ++Permission is hereby granted, free of charge, to any person ++obtaining a copy of this software and associated documentation ++files (the "Software"), to deal in the Software without ++restriction, including without limitation the rights to use, ++copy, modify, merge, publish, distribute, sublicense, and/or sell ++copies of the Software, and to permit persons to whom the ++Software is furnished to do so, subject to the following ++conditions: ++ ++The above copyright notice and this permission notice shall be ++included in all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES ++OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT ++HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, ++WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++OTHER DEALINGS IN THE SOFTWARE. ++*/(function(){// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++ ++/*global console:false exports:false require:false */ ++ ++var root = this, ++ jQuery = root.jQuery, ++ Backbone = root.Backbone, ++ _ = root._; ++ ++ ++// ## VIE constructor ++// ++// The VIE constructor is the way to initialize VIE for your ++// application. The instance of VIE handles all management of ++// semantic interaction, including keeping track of entities, ++// changes to them, the possible RDFa views on the page where ++// the entities are displayed, and connections to external ++// services like Stanbol and DBPedia. ++// ++// To get a VIE instance, simply run: ++// ++// var vie = new VIE(); ++// ++// You can also pass configurations to the VIE instance through ++// the constructor. For example, to set a different default ++// namespace to be used for names that don't have a namespace ++// specified, do: ++// ++// var vie = new VIE({ ++// baseNamespace: 'http://example.net' ++// }); ++// ++// ### Differences with VIE 1.x ++// ++// VIE 1.x used singletons for managing entities and views loaded ++// from a page. This has been changed with VIE 2.x, and now all ++// data managed by VIE is tied to the instance of VIE being used. ++// ++// This means that VIE needs to be instantiated before using. So, ++// when previously you could get entities from page with: ++// ++// VIE.RDFaEntities.getInstances(); ++// ++// Now you need to instantiate VIE first. This example uses the ++// Classic API compatibility layer instead of the `load` method: ++// ++// var vie = new VIE(); ++// vie.RDFaEntities.getInstances(); ++// ++// Currently the Classic API is enabled by default, but it is ++// recommended to ensure it is enabled before using it. So: ++// ++// var vie = new VIE({classic: true}); ++// vie.RDFaEntities.getInstances(); ++var VIE = root.VIE = function(config) { ++ this.config = (config) ? config : {}; ++ this.services = {}; ++ this.jQuery = jQuery; ++ this.entities = new this.Collection([], { ++ vie: this ++ }); ++ ++ this.Entity.prototype.entities = this.entities; ++ this.Entity.prototype.entityCollection = this.Collection; ++ this.Entity.prototype.vie = this; ++ ++ this.Namespaces.prototype.vie = this; ++// ### Namespaces in VIE ++// VIE supports different ontologies and an easy use of them. ++// Namespace prefixes reduce the amount of code you have to ++// write. In VIE, it does not matter if you access an entitie's ++// property with ++// `entity.get('')` or ++// `entity.get('dbprop:capitalOf')` or even ++// `entity.get('capitalOf')` once the corresponding namespace ++// is registered as *baseNamespace*. ++// By default `"http://viejs.org/ns/"`is set as base namespace. ++// For more information about how to set, get and list all ++// registered namespaces, refer to the ++// Namespaces documentation. ++ this.namespaces = new this.Namespaces( ++ (this.config.baseNamespace) ? this.config.baseNamespace : "http://viejs.org/ns/", ++ ++// By default, VIE is shipped with common namespace prefixes: ++ ++// + owl : "http://www.w3.org/2002/07/owl#" ++// + rdfs : "http://www.w3.org/2000/01/rdf-schema#" ++// + rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" ++// + schema : 'http://schema.org/' ++// + foaf : 'http://xmlns.com/foaf/0.1/' ++// + geo : 'http://www.w3.org/2003/01/geo/wgs84_pos#' ++// + dbpedia: "http://dbpedia.org/ontology/" ++// + dbprop : "http://dbpedia.org/property/" ++// + skos : "http://www.w3.org/2004/02/skos/core#" ++// + xsd : "http://www.w3.org/2001/XMLSchema#" ++// + sioc : "http://rdfs.org/sioc/ns#" ++// + dcterms: "http://purl.org/dc/terms/" ++ { ++ owl : "http://www.w3.org/2002/07/owl#", ++ rdfs : "http://www.w3.org/2000/01/rdf-schema#", ++ rdf : "http://www.w3.org/1999/02/22-rdf-syntax-ns#", ++ schema : 'http://schema.org/', ++ foaf : 'http://xmlns.com/foaf/0.1/', ++ geo : 'http://www.w3.org/2003/01/geo/wgs84_pos#', ++ dbpedia: "http://dbpedia.org/ontology/", ++ dbprop : "http://dbpedia.org/property/", ++ skos : "http://www.w3.org/2004/02/skos/core#", ++ xsd : "http://www.w3.org/2001/XMLSchema#", ++ sioc : "http://rdfs.org/sioc/ns#", ++ dcterms: "http://purl.org/dc/terms/" ++ } ++ ); ++ ++ ++ this.Type.prototype.vie = this; ++ this.Types.prototype.vie = this; ++ this.Attribute.prototype.vie = this; ++ this.Attributes.prototype.vie = this; ++// ### Type hierarchy in VIE ++// VIE takes care about type hierarchy of entities ++// (aka. *schema* or *ontology*). ++// Once a type hierarchy is known to VIE, we can leverage ++// this information, to easily ask, whether an entity ++// is of type, e.g., *foaf:Person* or *schema:Place*. ++// For more information about how to generate such a type ++// hierarchy, refer to the ++// Types documentation. ++ this.types = new this.Types(); ++// By default, there is a parent type in VIE, called ++// *owl:Thing*. All types automatically inherit from this ++// type and all registered entities, are of this type. ++ this.types.add("owl:Thing"); ++ ++// As described above, the Classic API of VIE 1.x is loaded ++// by default. As this might change in the future, it is ++// recommended to ensure it is enabled before using it. So: ++// ++// var vie = new VIE({classic: true}); ++// vie.RDFaEntities.getInstances(); ++ if (this.config.classic === true) { ++ /* Load Classic API as well */ ++ this.RDFa = new this.ClassicRDFa(this); ++ this.RDFaEntities = new this.ClassicRDFaEntities(this); ++ this.EntityManager = new this.ClassicEntityManager(this); ++ ++ this.cleanup = function() { ++ this.entities.reset(); ++ }; ++ } ++}; ++ ++// ### use(service, name) ++// This method registers services within VIE. ++// **Parameters**: ++// *{string|object}* **service** The service to be registered. ++// *{string}* **name** An optional name to register the service with. If this ++// is not set, the default name that comes with the service is taken. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE}* : The current VIE instance. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// var conf1 = {...}; ++// var conf2 = {...}; ++// vie.use(new vie.StanbolService()); ++// vie.use(new vie.StanbolService(conf1), "stanbol_1"); ++// vie.use(new vie.StanbolService(conf2), "stanbol_2"); ++// // <-- this means that there are now 3 services registered! ++VIE.prototype.use = function(service, name) { ++ if (!name && !service.name) { ++ throw new Error("Please provide a name for the service!"); ++ } ++ service.vie = this; ++ service.name = (name)? name : service.name; ++ if (service.init) { ++ service.init(); ++ } ++ this.services[service.name] = service; ++ ++ return this; ++}; ++ ++// ### service(name) ++// This method returns the service object that is ++// registered under the given name. ++// **Parameters**: ++// *{string}* **name** ... ++// **Throws**: ++// *{Error}* if no service could be found. ++// **Returns**: ++// *{object}* : The service to be queried. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var service = vie.service("stanbol"); ++VIE.prototype.service = function(name) { ++ if (!this.hasService(name)) { ++ throw "Undefined service " + name; ++ } ++ return this.services[name]; ++}; ++ ++// ### hasService(name) ++// This method returns a boolean telling whether VIE has a particular ++// service loaded. ++// **Parameters**: ++// *{string}* **name** ++// **Returns**: ++// *{boolean}* whether service is available ++VIE.prototype.hasService = function(name) { ++ if (!this.services[name]) { ++ return false; ++ } ++ return true; ++}; ++ ++// ### getServicesArray() ++// This method returns an array of all registered services. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{array}* : An array of service instances. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var services = vie.getServicesArray(); ++// services.length; // <-- 1 ++VIE.prototype.getServicesArray = function() { ++ return _.map(this.services, function (v) {return v;}); ++}; ++ ++// ### load(options) ++// This method instantiates a new VIE.Loadable in order to ++// perform queries on the services. ++// **Parameters**: ++// *{object}* **options** Options to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Loadable}* : A new instance of VIE.Loadable. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var loader = vie.load({...}); ++VIE.prototype.load = function(options) { ++ if (!options) { options = {}; } ++ options.vie = this; ++ return new this.Loadable(options); ++}; ++ ++// ### save(options) ++// This method instantiates a new VIE.Savable in order to ++// perform queries on the services. ++// **Parameters**: ++// *{object}* **options** Options to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Savable}* : A new instance of VIE.Savable. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var saver = vie.save({...}); ++VIE.prototype.save = function(options) { ++ if (!options) { options = {}; } ++ options.vie = this; ++ return new this.Savable(options); ++}; ++ ++// ### remove(options) ++// This method instantiates a new VIE.Removable in order to ++// perform queries on the services. ++// **Parameters**: ++// *{object}* **options** Options to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Removable}* : A new instance of VIE.Removable. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var remover = vie.remove({...}); ++VIE.prototype.remove = function(options) { ++ if (!options) { options = {}; } ++ options.vie = this; ++ return new this.Removable(options); ++}; ++ ++// ### analyze(options) ++// This method instantiates a new VIE.Analyzable in order to ++// perform queries on the services. ++// **Parameters**: ++// *{object}* **options** Options to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Analyzable}* : A new instance of VIE.Analyzable. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var analyzer = vie.analyze({...}); ++VIE.prototype.analyze = function(options) { ++ if (!options) { options = {}; } ++ options.vie = this; ++ return new this.Analyzable(options); ++}; ++ ++// ### find(options) ++// This method instantiates a new VIE.Findable in order to ++// perform queries on the services. ++// **Parameters**: ++// *{object}* **options** Options to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Findable}* : A new instance of VIE.Findable. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.use(new vie.StanbolService(), "stanbol"); ++// var finder = vie.find({...}); ++VIE.prototype.find = function(options) { ++ if (!options) { options = {}; } ++ options.vie = this; ++ return new this.Findable(options); ++}; ++ ++// ### loadSchema(url, options) ++// VIE only knows the *owl:Thing* type by default. ++// You can use this method to import another ++// schema (ontology) from an external resource. ++// (Currently, this supports only the JSON format!!) ++// As this method works asynchronously, you might want ++// to register `success` and `error` callbacks via the ++// options. ++// **Parameters**: ++// *{string}* **url** The url, pointing to the schema to import. ++// *{object}* **options** Options to be set. ++// (Set ```success``` and ```error``` as callbacks.). ++// **Throws**: ++// *{Error}* if the url is not set. ++// **Returns**: ++// *{VIE}* : The VIE instance itself. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.loadSchema("http://schema.rdfs.org/all.json", ++// { ++// baseNS : "http://schema.org/", ++// success : function () {console.log("success");}, ++// error : function (msg) {console.warn(msg);} ++// }); ++VIE.prototype.loadSchema = function(url, options) { ++ options = (!options)? {} : options; ++ ++ if (!url) { ++ throw new Error("Please provide a proper URL"); ++ } ++ else { ++ var vie = this; ++ jQuery.getJSON(url) ++ .success(function(data) { ++ try { ++ VIE.Util.loadSchemaOrg(vie, data, options.baseNS); ++ if (options.success) { ++ options.success.call(vie); ++ } ++ } catch (e) { ++ options.error.call(vie, e); ++ return; ++ } ++ }) ++ .error(function(data, textStatus, jqXHR) { ++ if (options.error) { ++ console.warn(data, textStatus, jqXHR); ++ options.error.call(vie, "Could not load schema from URL (" + url + ")"); ++ } ++ }); ++ } ++ ++ return this; ++}; ++ ++// ### getTypedEntityClass(type) ++// This method generates a special type of `Entity` based on the given type. ++// **Parameters**: ++// *{string}* **type** The type. ++// **Throws**: ++// *{Error}* if the type is unknown to VIE. ++// **Returns**: ++// *{VIE.Entity}* : A subclass of `VIE.Entity`. ++// **Example usage**: ++// ++// var vie = new VIE(); ++// vie.types.add("Person"); ++// var PersonClass = vie.getTypedEntityClass("Person"); ++// var Person = new PersonClass({"name", "Sebastian"}); ++VIE.prototype.getTypedEntityClass = function (type) { ++ var typeType = this.types.get(type); ++ if (!typeType) { ++ throw new Error("Unknown type " + type); ++ } ++ var TypedEntityClass = function (attrs, opts) { ++ if (!attrs) { ++ attrs = {}; ++ } ++ attrs["@type"] = type; ++ this.set(attrs, opts); ++ }; ++ TypedEntityClass.prototype = new this.Entity(); ++ TypedEntityClass.prototype.schema = function () { ++ return VIE.Util.getFormSchemaForType(typeType); ++ }; ++ return TypedEntityClass; ++}; ++ ++// ## Running VIE on Node.js ++// ++// When VIE is running under Node.js we can use the CommonJS ++// require interface to load our dependencies automatically. ++// ++// This means Node.js users don't need to care about dependencies ++// and can just run VIE with: ++// ++// var VIE = require('vie'); ++// ++// In browser environments the dependencies have to be included ++// before including VIE itself. ++if (typeof exports === 'object') { ++ exports.VIE = VIE; ++ ++ if (!jQuery) { ++ jQuery = require('jquery'); ++ } ++ if (!Backbone) { ++ Backbone = require('backbone'); ++ Backbone.setDomLibrary(jQuery); ++ } ++ if (!_) { ++ _ = require('underscore')._; ++ } ++} ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++ ++// ## VIE.Able ++// VIE implements asynchronius service methods through ++// [jQuery.Deferred](http://api.jquery.com/category/deferred-object/) objects. ++// Loadable, Analysable, Savable, etc. are part of the VIE service API and ++// are implemented with the generic VIE.Able class. ++// Example: ++// ++// VIE.prototype.Loadable = function (options) { ++// this.init(options,"load"); ++// }; ++// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); ++// ++// This defines ++// ++// someVIEService.load(options) ++// .using(...) ++// .execute() ++// .success(...) ++// .fail(...) ++// which will run the asynchronius `load` function of the service with the created Loadable ++// object. ++ ++// ### VIE.Able() ++// This is the constructor of a VIE.Able. This should not be called ++// globally but using the inherited classes below. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Able}* : A **new** VIE.Able object. ++// Example: ++// ++// VIE.prototype.Loadable = function (options) { ++// this.init(options,"load"); ++// }; ++// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); ++VIE.prototype.Able = function(){ ++ ++// ### init(options, methodName) ++// Internal method, called during initialization. ++// **Parameters**: ++// *{object}* **options** the *able* options coming from the API call ++// *{string}* **methodName** the service method called on `.execute`. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Able}* : The current instance. ++// **Example usage**: ++// ++// VIE.prototype.Loadable = function (options) { ++// this.init(options,"load"); ++// }; ++// VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); ++ this.init = function(options, methodName) { ++ this.options = options; ++ this.services = options.from || options.using || options.to || []; ++ this.vie = options.vie; ++ ++ this.methodName = methodName; ++ ++ // Instantiate the deferred object ++ this.deferred = jQuery.Deferred(); ++ ++// In order to get more information and documentation about the passed-through ++// deferred methods and their synonyms, please see the documentation of ++// the [jQuery.Deferred object](http://api.jquery.com/category/deferred-object/) ++ /* Public deferred-methods */ ++ this.resolve = this.deferred.resolve; ++ this.resolveWith = this.deferred.resolveWith; ++ this.reject = this.deferred.reject; ++ this.rejectWith = this.deferred.rejectWith; ++ this.success = this.done = this.deferred.done; ++ this.fail = this.deferred.fail; ++ this.then = this.deferred.then; ++ this.always = this.deferred.always; ++ this.from = this.using; ++ this.to = this.using; ++ ++ return this; ++ }; ++ ++ ++// ### using(services) ++// This method registers services with the current able instance. ++// **Parameters**: ++// *{string|array}* **services** An id of a service or an array of strings. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Able}* : The current instance. ++// **Example usage**: ++// ++// var loadable = vie.load({id: "http://example.com/entity/1234"}); ++// able.using("myService"); ++ this.using = function(services) { ++ var self = this; ++ services = (_.isArray(services))? services : [ services ]; ++ _.each (services, function (s) { ++ var obj = (typeof s === "string")? self.vie.service(s) : s; ++ self.services.push(obj); ++ }); ++ return this; ++ }; ++ ++// ### execute() ++// This method runs the actual method on all registered services. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ... ++// **Returns**: ++// *{VIE.Able}* : The current instance. ++// **Example usage**: ++// ++// var able = new vie.Able().init(); ++// able.using("stanbol") ++// .done(function () {alert("finished");}) ++// .execute(); ++ this.execute = function() { ++ /* call service[methodName] */ ++ var able = this; ++ _(this.services).each(function(service){ ++ service[able.methodName](able); ++ }); ++ return this; ++ }; ++}; ++ ++// ## VIE.Loadable ++// A ```VIE.Loadable``` is a wrapper around the deferred object ++// to **load** semantic data from a semantic web service. ++VIE.prototype.Loadable = function (options) { ++ this.init(options,"load"); ++}; ++VIE.prototype.Loadable.prototype = new VIE.prototype.Able(); ++ ++// ## VIE.Savable ++// A ```VIE.Savable``` is a wrapper around the deferred object ++// to **save** entities by a VIE service. The RDFaService would write the data ++// in the HTML as RDFa, the StanbolService stores the data in its Entityhub, etc. ++VIE.prototype.Savable = function(options){ ++ this.init(options, "save"); ++}; ++VIE.prototype.Savable.prototype = new VIE.prototype.Able(); ++ ++// ## VIE.Removable ++// A ```VIE.Removable``` is a wrapper around the deferred object ++// to **remove** semantic data from a semantic web service. ++VIE.prototype.Removable = function(options){ ++ this.init(options, "remove"); ++}; ++VIE.prototype.Removable.prototype = new VIE.prototype.Able(); ++ ++// ## VIE.Analyzable ++// A ```VIE.Analyzable``` is a wrapper around the deferred object ++// to **analyze** data and extract semantic information with the ++// help of a semantic web service. ++VIE.prototype.Analyzable = function (options) { ++ this.init(options, "analyze"); ++}; ++VIE.prototype.Analyzable.prototype = new VIE.prototype.Able(); ++ ++// ## VIE.Findable ++// A ```VIE.Findable``` is a wrapper around the deferred object ++// to **find** semantic data on a semantic storage. ++VIE.prototype.Findable = function (options) { ++ this.init(options, "find"); ++}; ++VIE.prototype.Findable.prototype = new VIE.prototype.Able(); ++ ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++ ++// ## VIE Utils ++// ++// The here-listed methods are utility methods for the day-to-day ++// VIE.js usage. All methods are within the static namespace ```VIE.Util```. ++VIE.Util = { ++ ++// ### VIE.Util.toCurie(uri, safe, namespaces) ++// This method converts a given ++// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object. ++// If the given uri is already a URI, it is left untouched and directly returned. ++// If no prefix could be found, an ```Error``` is thrown. ++// **Parameters**: ++// *{string}* **uri** The URI to be transformed. ++// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs. ++// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes. ++// **Throws**: ++// *{Error}* If no prefix could be found in the passed namespaces. ++// **Returns**: ++// *{string}* The CURIE or SCURIE. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var uri = ""; ++// VIE.Util.toCurie(uri, false, ns); // --> dbp:Person ++// VIE.Util.toCurie(uri, true, ns); // --> [dbp:Person] ++ toCurie : function (uri, safe, namespaces) { ++ if (VIE.Util.isCurie(uri, namespaces)) { ++ return uri; ++ } ++ var delim = ":"; ++ for (var k in namespaces.toObj()) { ++ if (uri.indexOf(namespaces.get(k)) === 1) { ++ var pattern = new RegExp("^" + "$/, '') + ++ ((safe)? "]" : ""); ++ } ++ } ++ throw new Error("No prefix found for URI '" + uri + "'!"); ++ }, ++ ++// ### VIE.Util.isCurie(curie, namespaces) ++// This method checks, whether ++// the given string is a CURIE and returns ```true``` if so and ```false```otherwise. ++// **Parameters**: ++// *{string}* **curie** The CURIE (or SCURIE) to be checked. ++// *{VIE.Namespaces}* **namespaces** The namespaces to be used for the prefixes. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var uri = ""; ++// var curie = "dbp:Person"; ++// var scurie = "[dbp:Person]"; ++// var text = "This is some text."; ++// VIE.Util.isCurie(uri, ns); // --> false ++// VIE.Util.isCurie(curie, ns); // --> true ++// VIE.Util.isCurie(scurie, ns); // --> true ++// VIE.Util.isCurie(text, ns); // --> false ++ isCurie : function (curie, namespaces) { ++ if (VIE.Util.isUri(curie)) { ++ return false; ++ } else { ++ try { ++ VIE.Util.toUri(curie, namespaces); ++ return true; ++ } catch (e) { ++ return false; ++ } ++ } ++ }, ++ ++// ### VIE.Util.toUri(curie, namespaces) ++// This method converts a ++// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object. ++// **Parameters**: ++// *{string}* **curie** The CURIE to be transformed. ++// *{VIE.Namespaces}* **namespaces** The namespaces object ++// **Throws**: ++// *{Error}* If no URI could be assembled. ++// **Returns**: ++// *{string}* : A string, representing the URI. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var curie = "dbp:Person"; ++// var scurie = "[dbp:Person]"; ++// VIE.Util.toUri(curie, ns); ++// --> ++// VIE.Util.toUri(scurie, ns); ++// --> ++ toUri : function (curie, namespaces) { ++ if (VIE.Util.isUri(curie)) { ++ return curie; ++ } ++ var delim = ":"; ++ for (var prefix in namespaces.toObj()) { ++ if (prefix !== "" && (curie.indexOf(prefix + ":") === 0 || curie.indexOf("[" + prefix + ":") === 0)) { ++ var pattern = new RegExp("^" + "\\[{0,1}" + prefix + delim); ++ return "<" + curie.replace(pattern, namespaces.get(prefix)).replace(/\]{0,1}$/, '') + ">"; ++ } ++ } ++ /* check for the default namespace */ ++ if (curie.indexOf(delim) === -1) { ++ return "<" + namespaces.base() + curie + ">"; ++ } ++ throw new Error("No prefix found for CURIE '" + curie + "'!"); ++ }, ++ ++// ### VIE.Util.isUri(something) ++// This method checks, whether the given string is a URI. ++// **Parameters**: ++// *{string}* **something** : The string to be checked. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise. ++// **Example usage**: ++// ++// var uri = ""; ++// var curie = "dbp:Person"; ++// VIE.Util.isUri(uri); // --> true ++// VIE.Util.isUri(curie); // --> false ++ isUri : function (something) { ++ return (typeof something === "string" && something.search(/^<.+>$/) === 0); ++ }, ++ ++// ### VIE.Util.mapAttributeNS(attr, ns) ++// This method maps an attribute of an entity into namespaces if they have CURIEs. ++// **Parameters**: ++// *{string}* **attr** : The attribute to be transformed. ++// *{VIE.Namespaces}* **ns** : The namespaces. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{string}* : The transformed attribute's name. ++// **Example usage**: ++// ++// var attr = "name"; ++// var ns = myVIE.namespaces; ++// VIE.Util.mapAttributeNS(attr, ns); // '<' + ns.base() + attr + '>'; ++ mapAttributeNS : function (attr, ns) { ++ var a = attr; ++ if (ns.isUri (attr) || attr.indexOf('@') === 0) { ++ //ignore ++ } else if (ns.isCurie(attr)) { ++ a = ns.uri(attr); ++ } else if (!ns.isUri(attr)) { ++ if (attr.indexOf(":") === -1) { ++ a = '<' + ns.base() + attr + '>'; ++ } else { ++ a = '<' + attr + '>'; ++ } ++ } ++ return a; ++ }, ++ ++// ### VIE.Util.rdf2Entities(service, results) ++// This method converts *rdf/json* data from an external service ++// into VIE.Entities. ++// **Parameters**: ++// *{object}* **service** The service that retrieved the data. ++// *{object}* **results** The data to be transformed. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data. ++ rdf2Entities: function (service, results) { ++ if (typeof jQuery.rdf !== 'function') { ++ /* fallback if no rdfQuery has been loaded */ ++ return VIE.Util._rdf2EntitiesNoRdfQuery(service, results); ++ } ++ try { ++ var rdf = (results instanceof jQuery.rdf)? ++ results.base(service.vie.namespaces.base()) : ++ jQuery.rdf().base(service.vie.namespaces.base()).load(results, {}); ++ ++ /* if the service contains rules to apply special transformation, they are executed here.*/ ++ if (service.rules) { ++ var rules = jQuery.rdf.ruleset(); ++ for (var prefix in service.vie.namespaces.toObj()) { ++ if (prefix !== "") { ++ rules.prefix(prefix, service.vie.namespaces.get(prefix)); ++ } ++ } ++ for (var i = 0; i < service.rules.length; i++)if(service.rules.hasOwnProperty(i)) { ++ var rule = service.rules[i]; ++ rules.add(rule.left, rule.right); ++ } ++ rdf = rdf.reason(rules, 10); /* execute the rules only 10 times to avoid looping */ ++ } ++ var entities = {}; ++ rdf.where('?subject ?property ?object').each(function() { ++ var subject = this.subject.toString(); ++ if (!entities[subject]) { ++ entities[subject] = { ++ '@subject': subject, ++ '@context': service.vie.namespaces.toObj(true), ++ '@type': [] ++ }; ++ } ++ var propertyUri = this.property.toString(); ++ var propertyCurie; ++ ++ try { ++ propertyCurie = service.vie.namespaces.curie(propertyUri); ++ //jQuery.createCurie(propertyUri, {namespaces: service.vie.namespaces.toObj(true)}); ++ } catch (e) { ++ propertyCurie = propertyUri; ++ // console.warn(propertyUri + " doesn't have a namespace definition in '", service.vie.namespaces.toObj()); ++ } ++ entities[subject][propertyCurie] = entities[subject][propertyCurie] || []; ++ ++ function getValue(rdfQueryLiteral){ ++ if(typeof rdfQueryLiteral.value === "string"){ ++ if (rdfQueryLiteral.lang){ ++ var literal = { ++ toString: function(){ ++ return this["@value"]; ++ }, ++ "@value": rdfQueryLiteral.value.replace(/^"|"$/g, ''), ++ "@language": rdfQueryLiteral.lang ++ }; ++ return literal; ++ } ++ else ++ return rdfQueryLiteral.value; ++ return rdfQueryLiteral.value.toString(); ++ } else if (rdfQueryLiteral.type === "uri"){ ++ return rdfQueryLiteral.toString(); ++ } else { ++ return rdfQueryLiteral.value; ++ } ++ } ++ entities[subject][propertyCurie].push(getValue(this.object)); ++ }); ++ ++ _(entities).each(function(ent){ ++ ent["@type"] = ent["@type"].concat(ent["rdf:type"]); ++ delete ent["rdf:type"]; ++ _(ent).each(function(value, property){ ++ if(value.length === 1){ ++ ent[property] = value[0]; ++ } ++ }); ++ }); ++ ++ var vieEntities = []; ++ jQuery.each(entities, function() { ++ var entityInstance = new service.vie.Entity(this); ++ entityInstance = service.vie.entities.addOrUpdate(entityInstance); ++ vieEntities.push(entityInstance); ++ }); ++ return vieEntities; ++ } catch (e) { ++ console.warn("Something went wrong while parsing the returned results!", e); ++ return []; ++ } ++ }, ++ ++ /* ++ VIE.Util.getPreferredLangForPreferredProperty(entity, preferredFields, preferredLanguages) ++ looks for specific ranking fields and languages. It calculates all possibilities and gives them ++ a score. It returns the value with the best score. ++ */ ++ getPreferredLangForPreferredProperty: function(entity, preferredFields, preferredLanguages) { ++ var l, labelArr, lang, p, property, resArr, valueArr, _len, _len2, ++ _this = this; ++ resArr = []; ++ /* Try to find a label in the preferred language ++ */ ++ _.each(preferredLanguages, function (lang) { ++ _.each(preferredFields, function (property) { ++ labelArr = null; ++ /* property can be a string e.g. "skos:prefLabel" ++ */ ++ if (typeof property === "string" && entity.get(property)) { ++ labelArr = _.flatten([entity.get(property)]); ++ _(labelArr).each(function(label) { ++ /* ++ The score is a natural number with 0 for the ++ best candidate with the first preferred language ++ and first preferred property ++ */ ++ var labelLang, score, value; ++ score = p; ++ labelLang = label["@language"]; ++ /* ++ legacy code for compatibility with uotdated stanbol, ++ to be removed after may 2012 ++ */ ++ if (typeof label === "string" && (label.indexOf("@") === label.length - 3 || label.indexOf("@") === label.length - 5)) { ++ labelLang = label.replace(/(^\"*|\"*@)..(..)?$/g, ""); ++ } ++ /* end of legacy code ++ */ ++ if (labelLang) { ++ if (labelLang === lang) { ++ score += l; ++ } else { ++ score += 20; ++ } ++ } else { ++ score += 10; ++ } ++ value = label.toString(); ++ /* legacy code for compatibility with uotdated stanbol, to be removed after may 2012 ++ */ ++ value = value.replace(/(^\"*|\"*@..$)/g, ""); ++ /* end of legacy code ++ */ ++ return resArr.push({ ++ score: score, ++ value: value ++ }); ++ }); ++ /* ++ property can be an object like ++ { ++ property: "skos:broader", ++ makeLabel: function(propertyValueArr) { return "..."; } ++ } ++ */ ++ } else if (typeof property === "object" && entity.get(property.property)) { ++ valueArr = _.flatten([entity.get(property.property)]); ++ valueArr = _(valueArr).map(function(termUri) { ++ if (termUri.isEntity) { ++ return termUri.getSubject(); ++ } else { ++ return termUri; ++ } ++ }); ++ resArr.push({ ++ score: p, ++ value: property.makeLabel(valueArr) ++ }); ++ } ++ }); ++ }); ++ /* ++ take the result with the best score ++ */ ++ resArr = _(resArr).sortBy(function(a) { ++ return a.score; ++ }); ++ if(resArr.length) { ++ return resArr[0].value; ++ } else { ++ return "n/a"; ++ } ++ }, ++ ++ ++// ### VIE.Util._rdf2EntitiesNoRdfQuery(service, results) ++// This is a **private** method which should ++// only be accessed through ```VIE.Util._rdf2Entities()``` and is a helper method in case there is no ++// rdfQuery loaded (*not recommended*). ++// **Parameters**: ++// *{object}* **service** The service that retrieved the data. ++// *{object}* **results** The data to be transformed. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{[VIE.Entity]}* : An array, containing VIE.Entity instances which have been transformed from the given data. ++ _rdf2EntitiesNoRdfQuery: function (service, results) { ++ var jsonLD = []; ++ _.forEach(results, function(value, key) { ++ var entity = {}; ++ entity['@subject'] = '<' + key + '>'; ++ _.forEach(value, function(triples, predicate) { ++ predicate = '<' + predicate + '>'; ++ _.forEach(triples, function(triple) { ++ if (triple.type === 'uri') { ++ triple.value = '<' + triple.value + '>'; ++ } ++ ++ if (entity[predicate] && !_.isArray(entity[predicate])) { ++ entity[predicate] = [entity[predicate]]; ++ } ++ ++ if (_.isArray(entity[predicate])) { ++ entity[predicate].push(triple.value); ++ return; ++ } ++ entity[predicate] = triple.value; ++ }); ++ }); ++ jsonLD.push(entity); ++ }); ++ return jsonLD; ++ }, ++ ++// ### VIE.Util.loadSchemaOrg(vie, SchemaOrg, baseNS) ++// This method is a wrapper around ++// the schema.org ontology. It adds all the ++// given types and properties as ```VIE.Type``` instances to the given VIE instance. ++// If the paramenter **baseNS** is set, the method automatically sets the namespace ++// to the provided one. If it is not set, it will keep the base namespace of VIE untouched. ++// **Parameters**: ++// *{VIE}* **vie** The instance of ```VIE```. ++// *{object}* **SchemaOrg** The data imported from schema.org. ++// *{string|undefined}* **baseNS** If set, this will become the new baseNamespace within the given ```VIE``` instance. ++// **Throws**: ++// *{Error}* If the parameter was not given. ++// **Returns**: ++// *nothing* ++ loadSchemaOrg : function (vie, SchemaOrg, baseNS) { ++ ++ if (!SchemaOrg) { ++ throw new Error("Please load the schema.json file."); ++ } ++ vie.types.remove(""); ++ ++ var baseNSBefore = (baseNS)? baseNS : vie.namespaces.base(); ++ vie.namespaces.base(baseNS); ++ ++ var datatypeMapping = { ++ 'DataType': 'xsd:anyType', ++ 'Boolean' : 'xsd:boolean', ++ 'Date' : 'xsd:date', ++ 'DateTime': 'xsd:dateTime', ++ 'Time' : 'xsd:time', ++ 'Float' : 'xsd:float', ++ 'Integer' : 'xsd:integer', ++ 'Number' : 'xsd:anySimpleType', ++ 'Text' : 'xsd:string', ++ 'URL' : 'xsd:anyURI' ++ }; ++ ++ var dataTypeHelper = function (ancestors, id) { ++ var type = vie.types.add(id, [{'id' : 'value', 'range' : datatypeMapping[id]}]); ++ ++ for (var i = 0; i < ancestors.length; i++) { ++ var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) : ++ dataTypeHelper.call(vie, SchemaOrg.datatypes[ancestors[i]].supertypes, ancestors[i]); ++ type.inherit(supertype); ++ } ++ return type; ++ }; ++ ++ for (var dt in SchemaOrg.datatypes) { ++ if (!vie.types.get(dt)) { ++ var ancestors = SchemaOrg.datatypes[dt].supertypes; ++ dataTypeHelper.call(vie, ancestors, dt); ++ } ++ } ++ ++ var metadataHelper = function (definition) { ++ var metadata = {}; ++ ++ if (definition.label) { ++ metadata.label = definition.label; ++ } ++ ++ if (definition.url) { ++ metadata.url = definition.url; ++ } ++ ++ if (definition.comment) { ++ metadata.comment = definition.comment; ++ } ++ ++ if (definition.metadata) { ++ metadata = _.extend(metadata, definition.metadata); ++ } ++ return metadata; ++ }; ++ ++ var typeProps = function (id) { ++ var props = []; ++ _.each(SchemaOrg.types[id].specific_properties, function (pId) { ++ var property = SchemaOrg.properties[pId]; ++ props.push({ ++ 'id' : property.id, ++ 'range' : property.ranges, ++ 'min' : property.min, ++ 'max' : property.max, ++ 'metadata': metadataHelper(property) ++ }); ++ }); ++ return props; ++ }; ++ ++ var typeHelper = function (ancestors, id, props, metadata) { ++ var type = vie.types.add(id, props, metadata); ++ ++ for (var i = 0; i < ancestors.length; i++) { ++ var supertype = (vie.types.get(ancestors[i]))? vie.types.get(ancestors[i]) : ++ typeHelper.call(vie, SchemaOrg.types[ancestors[i]].supertypes, ancestors[i], typeProps.call(vie, ancestors[i])); ++ type.inherit(supertype); ++ } ++ if (id === "Thing" && !type.isof("owl:Thing")) { ++ type.inherit("owl:Thing"); ++ } ++ return type; ++ }; ++ ++ _.each(SchemaOrg.types, function (typeDef) { ++ if (vie.types.get(typeDef.id)) { ++ return; ++ } ++ var ancestors = typeDef.supertypes; ++ var metadata = metadataHelper(typeDef); ++ typeHelper.call(vie, ancestors, typeDef.id, typeProps.call(vie, typeDef.id), metadata); ++ }); ++ ++ /* set the namespace to either the old value or the provided baseNS value */ ++ vie.namespaces.base(baseNSBefore); ++ }, ++ ++// ### VIE.Util.getEntityTypeUnion(entity) ++// This generates a entity-specific VIE type that is a subtype of all the ++// types of the entity. This makes it easier to deal with attribute definitions ++// specific to an entity because they're merged to a single list. This custom ++// type is transient, meaning that it won't be automatilly added to the entity ++// or the VIE type registry. ++ getEntityTypeUnion : function(entity) { ++ var vie = entity.vie; ++ return new vie.Type('Union').inherit(entity.get('@type')); ++ }, ++ ++// ### VIE.Util.getFormSchemaForType(type) ++// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms) ++// -compatible form schema for any VIE Type. ++ getFormSchemaForType : function(type, allowNested) { ++ var schema = {}; ++ ++ // Generate a schema ++ _.each(type.attributes.toArray(), function (attribute) { ++ var key = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces); ++ schema[key] = VIE.Util.getFormSchemaForAttribute(attribute); ++ }); ++ ++ // Clean up unknown attribute types ++ _.each(schema, function (field, id) { ++ if (!field.type) { ++ delete schema[id]; ++ } ++ ++ if (field.type === 'URL') { ++ field.type = 'Text'; ++ field.dataType = 'url'; ++ } ++ ++ if (field.type === 'List' && !field.listType) { ++ delete schema[id]; ++ } ++ ++ if (!allowNested) { ++ if (field.type === 'NestedModel' || field.listType === 'NestedModel') { ++ delete schema[id]; ++ } ++ } ++ }); ++ ++ return schema; ++ }, ++ ++/// ### VIE.Util.getFormSchemaForAttribute(attribute) ++ getFormSchemaForAttribute : function(attribute) { ++ var primaryType = attribute.range[0]; ++ var schema = {}; ++ ++ var getWidgetForType = function (type) { ++ switch (type) { ++ case 'xsd:anySimpleType': ++ case 'xsd:float': ++ case 'xsd:integer': ++ return 'Number'; ++ case 'xsd:string': ++ return 'Text'; ++ case 'xsd:date': ++ return 'Date'; ++ case 'xsd:dateTime': ++ return 'DateTime'; ++ case 'xsd:boolean': ++ return 'Checkbox'; ++ case 'xsd:anyURI': ++ return 'URL'; ++ default: ++ var typeType = attribute.vie.types.get(type); ++ if (!typeType) { ++ return null; ++ } ++ if (typeType.attributes.get('value')) { ++ // Convert to proper xsd type ++ return getWidgetForType(typeType.attributes.get('value').range[0]); ++ } ++ return 'NestedModel'; ++ } ++ }; ++ ++ // TODO: Generate a nicer label ++ schema.title = VIE.Util.toCurie(attribute.id, false, attribute.vie.namespaces); ++ ++ // TODO: Handle attributes linking to other VIE entities ++ ++ if (attribute.min > 0) { ++ schema.validators = ['required']; ++ } ++ ++ if (attribute.max > 1) { ++ schema.type = 'List'; ++ schema.listType = getWidgetForType(primaryType); ++ if (schema.listType === 'NestedModel') { ++ schema.nestedModelType = primaryType; ++ } ++ return schema; ++ } ++ ++ schema.type = getWidgetForType(primaryType); ++ if (schema.type === 'NestedModel') { ++ schema.nestedModelType = primaryType; ++ } ++ return schema; ++ }, ++ ++// ### VIE.Util.getFormSchema(entity) ++// This creates a [Backbone Forms](https://github.com/powmedia/backbone-forms) ++// -compatible form schema for any VIE Entity. The form schema creation ++// utilizes type information attached to the entity. ++// **Parameters**: ++// *{```Entity```}* **entity** An instance of VIE ```Entity```. ++// **Throws**: ++// *nothing*.. ++// **Returns**: ++// *{object}* a JavaScript object representation of the form schema ++ getFormSchema : function(entity) { ++ if (!entity || !entity.isEntity) { ++ return {}; ++ } ++ ++ var unionType = VIE.Util.getEntityTypeUnion(entity); ++ var schema = VIE.Util.getFormSchemaForType(unionType, true); ++ ++ // Handle nested models ++ _.each(schema, function (property, id) { ++ if (property.type !== 'NestedModel' && property.listType !== 'NestedModel') { ++ return; ++ } ++ schema[id].model = entity.vie.getTypedEntityClass(property.nestedModelType); ++ }); ++ ++ return schema; ++ }, ++ ++// ### VIE.Util.xsdDateTime(date) ++// This transforms a ```Date``` instance into an xsd:DateTime format. ++// **Parameters**: ++// *{```Date```}* **date** An instance of a javascript ```Date```. ++// **Throws**: ++// *nothing*.. ++// **Returns**: ++// *{string}* A string representation of the dateTime in the xsd:dateTime format. ++ xsdDateTime : function(date) { ++ function pad(n) { ++ var s = n.toString(); ++ return s.length < 2 ? '0'+s : s; ++ } ++ ++ var yyyy = date.getFullYear(); ++ var mm1 = pad(date.getMonth()+1); ++ var dd = pad(date.getDate()); ++ var hh = pad(date.getHours()); ++ var mm2 = pad(date.getMinutes()); ++ var ss = pad(date.getSeconds()); ++ ++ return yyyy +'-' +mm1 +'-' +dd +'T' +hh +':' +mm2 +':' +ss; ++ }, ++ ++// ### VIE.Util.extractLanguageString(entity, attrs, langs) ++// This method extracts a literal string from an entity, searching through the given attributes and languages. ++// **Parameters**: ++// *{```VIE.Entity```}* **entity** An instance of a VIE.Entity. ++// *{```array|string```}* **attrs** Either a string or an array of possible attributes. ++// *{```array|string```}* **langs** Either a string or an array of possible languages. ++// **Throws**: ++// *nothing*.. ++// **Returns**: ++// *{string|undefined}* The string that was found at the attribute with the wanted language, undefined if nothing could be found. ++// **Example usage**: ++// ++// var attrs = ["name", "rdfs:label"]; ++// var langs = ["en", "de"]; ++// VIE.Util.extractLanguageString(someEntity, attrs, langs); // "Barack Obama"; ++ extractLanguageString : function(entity, attrs, langs) { ++ var p, attr, name, i, n; ++ if (entity && typeof entity !== "string") { ++ attrs = (_.isArray(attrs))? attrs : [ attrs ]; ++ langs = (_.isArray(langs))? langs : [ langs ]; ++ for (p = 0; p < attrs.length; p++) { ++ for (var l = 0; l < langs.length; l++) { ++ var lang = langs[l]; ++ attr = attrs[p]; ++ if (entity.has(attr)) { ++ name = entity.get(attr); ++ name = (_.isArray(name))? name : [ name ]; ++ for (i = 0; i < name.length; i++) { ++ n = name[i]; ++ if (n.isEntity) { ++ n = VIE.Util.extractLanguageString(n, attrs, lang); ++ } else if (typeof n === "string") { ++ n = n; ++ } else { ++ n = ""; ++ } ++ if (n && n.indexOf('@' + lang) > -1) { ++ return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim(); ++ } ++ } ++ } ++ } ++ } ++ /* let's do this again in case we haven't found a name but are dealing with ++ broken data where no language is given */ ++ for (p = 0; p < attrs.length; p++) { ++ attr = attrs[p]; ++ if (entity.has(attr)) { ++ name = entity.get(attr); ++ name = (_.isArray(name))? name : [ name ]; ++ for (i = 0; i < name.length; i++) { ++ n = name[i]; ++ if (n.isEntity) { ++ n = VIE.Util.extractLanguageString(n, attrs, []); ++ } ++ if (n && (typeof n === "string") && n.indexOf('@') === -1) { ++ return n.replace(/"/g, "").replace(/@[a-z]+/, '').trim(); ++ } ++ } ++ } ++ } ++ } ++ return undefined; ++ }, ++ ++// ### VIE.Util.transformationRules(service) ++// This returns a default set of rdfQuery rules that transform semantic data into the ++// VIE entity types. ++// **Parameters**: ++// *{object}* **service** An instance of a vie.service. ++// **Throws**: ++// *nothing*.. ++// **Returns**: ++// *{array}* An array of rules with 'left' and 'right' side. ++ transformationRules : function (service) { ++ var res = [ ++ // rule(s) to transform a dbpedia:Person into a VIE:Person ++ { ++ 'left' : [ ++ '?subject a dbpedia:Person', ++ '?subject rdfs:label ?label' ++ ], ++ 'right': function(ns){ ++ return function(){ ++ return [ ++ jQuery.rdf.triple(this.subject.toString(), ++ 'a', ++ '<' + ns.base() + 'Person>', { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'name>', ++ this.label, { ++ namespaces: ns.toObj() ++ }) ++ ]; ++ }; ++ }(service.vie.namespaces) ++ }, ++ // rule(s) to transform a foaf:Person into a VIE:Person ++ { ++ 'left' : [ ++ '?subject a foaf:Person', ++ '?subject rdfs:label ?label' ++ ], ++ 'right': function(ns){ ++ return function(){ ++ return [ ++ jQuery.rdf.triple(this.subject.toString(), ++ 'a', ++ '<' + ns.base() + 'Person>', { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'name>', ++ this.label, { ++ namespaces: ns.toObj() ++ }) ++ ]; ++ }; ++ }(service.vie.namespaces) ++ }, ++ // rule(s) to transform a dbpedia:Place into a VIE:Place ++ { ++ 'left' : [ ++ '?subject a dbpedia:Place', ++ '?subject rdfs:label ?label' ++ ], ++ 'right': function(ns) { ++ return function() { ++ return [ ++ jQuery.rdf.triple(this.subject.toString(), ++ 'a', ++ '<' + ns.base() + 'Place>', { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'name>', ++ this.label.toString(), { ++ namespaces: ns.toObj() ++ }) ++ ]; ++ }; ++ }(service.vie.namespaces) ++ }, ++ // rule(s) to transform a dbpedia:City into a VIE:City ++ { ++ 'left' : [ ++ '?subject a dbpedia:City', ++ '?subject rdfs:label ?label', ++ '?subject dbpedia:abstract ?abs', ++ '?subject dbpedia:country ?country' ++ ], ++ 'right': function(ns) { ++ return function() { ++ return [ ++ jQuery.rdf.triple(this.subject.toString(), ++ 'a', ++ '<' + ns.base() + 'City>', { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'name>', ++ this.label.toString(), { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'description>', ++ this.abs.toString(), { ++ namespaces: ns.toObj() ++ }), ++ jQuery.rdf.triple(this.subject.toString(), ++ '<' + ns.base() + 'containedIn>', ++ this.country.toString(), { ++ namespaces: ns.toObj() ++ }) ++ ]; ++ }; ++ }(service.vie.namespaces) ++ } ++ ]; ++ return res; ++ }, ++ ++ getAdditionalRules : function (service) { ++ ++ var mapping = { ++ Work : "CreativeWork", ++ Film : "Movie", ++ TelevisionEpisode : "TVEpisode", ++ TelevisionShow : "TVSeries", // not listed as equivalent class on dbpedia.org ++ Website : "WebPage", ++ Painting : "Painting", ++ Sculpture : "Sculpture", ++ ++ Event : "Event", ++ SportsEvent : "SportsEvent", ++ MusicFestival : "Festival", ++ FilmFestival : "Festival", ++ ++ Place : "Place", ++ Continent : "Continent", ++ Country : "Country", ++ City : "City", ++ Airport : "Airport", ++ Station : "TrainStation", // not listed as equivalent class on dbpedia.org ++ Hospital : "GovernmentBuilding", ++ Mountain : "Mountain", ++ BodyOfWater : "BodyOfWater", ++ ++ Company : "Organization", ++ Person : "Person" ++ }; ++ ++ var additionalRules = []; ++ _.each(mapping, function (map, key) { ++ var tripple = { ++ 'left' : [ '?subject a dbpedia:' + key, '?subject rdfs:label ?label' ], ++ 'right' : function(ns) { ++ return function() { ++ return [ jQuery.rdf.triple(this.subject.toString(), 'a', '<' + ns.base() + map + '>', { ++ namespaces : ns.toObj() ++ }), jQuery.rdf.triple(this.subject.toString(), '<' + ns.base() + 'name>', this.label.toString(), { ++ namespaces : ns.toObj() ++ }) ]; ++ }; ++ }(service.vie.namespaces) ++ }; ++ additionalRules.push(tripple); ++ }); ++ return additionalRules; ++ } ++}; ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++ ++// ## VIE Entities ++// ++// In VIE there are two low-level model types for storing data. ++// **Collections** and **Entities**. Considering `var v = new VIE();` a VIE instance, ++// `v.entities` is a Collection with `VIE Entity` objects in it. ++// VIE internally uses JSON-LD to store entities. ++// ++// Each Entity has a few special attributes starting with an `@`. VIE has an API ++// for correctly using these attributes, so in order to stay compatible with later ++// versions of the library, possibly using a later version of JSON-LD, use the API ++// to interact with your entities. ++// ++// * `@subject` stands for the identifier of the entity. Use `e.getSubject()` ++// * `@type` stores the explicit entity types. VIE internally handles Type hierarchy, ++// which basically enables to define subtypes and supertypes. Every entity has ++// the type 'owl:Thing'. Read more about Types in VIE.Type. ++// * `@context` stores namespace definitions used in the entity. Read more about ++// Namespaces in VIE Namespaces. ++VIE.prototype.Entity = function(attrs, opts) { ++ ++ attrs = (attrs)? attrs : {}; ++ opts = (opts)? opts : {}; ++ ++ var self = this; ++ ++ if (attrs['@type'] !== undefined) { ++ attrs['@type'] = (_.isArray(attrs['@type']))? attrs['@type'] : [ attrs['@type'] ]; ++ attrs['@type'] = _.map(attrs['@type'], function(val){ ++ if (!self.vie.types.get(val)) { ++ //if there is no such type -> add it and let it inherit from "owl:Thing" ++ self.vie.types.add(val).inherit("owl:Thing"); ++ } ++ return self.vie.types.get(val).id; ++ }); ++ attrs['@type'] = (attrs['@type'].length === 1)? attrs['@type'][0] : attrs['@type']; ++ } else { ++ // provide "owl:Thing" as the default type if none was given ++ attrs['@type'] = self.vie.types.get("owl:Thing").id; ++ } ++ ++ //the following provides full seamless namespace support ++ //for attributes. It should not matter, if you ++ //query for `model.get('name')` or `model.get('foaf:name')` ++ //or even `model.get('http://xmlns.com/foaf/0.1/name');` ++ //However, if we just overwrite `set()` and `get()`, this ++ //raises a lot of side effects, so we need to expand ++ //the attributes before we create the model. ++ _.each (attrs, function (value, key) { ++ var newKey = VIE.Util.mapAttributeNS(key, this.namespaces); ++ if (key !== newKey) { ++ delete attrs[key]; ++ attrs[newKey] = value; ++ } ++ }, self.vie); ++ ++ var Model = Backbone.Model.extend({ ++ idAttribute: '@subject', ++ ++ initialize: function(attributes, options) { ++ if (attributes['@subject']) { ++ this.id = this['@subject'] = this.toReference(attributes['@subject']); ++ } else { ++ this.id = this['@subject'] = attributes['@subject'] = this.cid.replace('c', '_:bnode'); ++ } ++ return this; ++ }, ++ ++ schema: function() { ++ return VIE.Util.getFormSchema(this); ++ }, ++ ++ // ### Getter, Has, Setter ++ // #### `.get(attr)` ++ // To be able to communicate to a VIE Entity you can use a simple get(property) ++ // command as in `entity.get('rdfs:label')` which will give you one or more literals. ++ // If the property points to a collection, its entities can be browsed further. ++ get: function (attr) { ++ attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); ++ var value = Backbone.Model.prototype.get.call(this, attr); ++ value = (_.isArray(value))? value : [ value ]; ++ ++ value = _.map(value, function(v) { ++ if (v !== undefined && attr === '@type' && self.vie.types.get(v)) { ++ return self.vie.types.get(v); ++ } else if (v !== undefined && self.vie.entities.get(v)) { ++ return self.vie.entities.get(v); ++ } else { ++ return v; ++ } ++ }, this); ++ if(value.length === 0) { ++ return undefined; ++ } ++ // if there is only one element, just return that one ++ value = (value.length === 1)? value[0] : value; ++ return value; ++ }, ++ ++ // #### `.has(attr)` ++ // Sometimes you'd like to determine if a specific attribute is set ++ // in an entity. For this reason you can call for example `person.has('friend')` ++ // to determine if a person entity has friends. ++ has: function(attr) { ++ attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); ++ return Backbone.Model.prototype.has.call(this, attr); ++ }, ++ ++ // #### `.set(attrName, value, opts)`, ++ // The `options` parameter always refers to a `Backbone.Model.set` `options` object. ++ // ++ // **`.set(attributes, options)`** is the most universal way of calling the ++ // `.set` method. In this case the `attributes` object is a map of all ++ // attributes to be changed. ++ set : function(attrs, options, opts) { ++ if (!attrs) { ++ return this; ++ } ++ ++ if (attrs['@subject']) { ++ attrs['@subject'] = this.toReference(attrs['@subject']); ++ } ++ ++ // Use **`.set(attrName, value, options)`** for setting or changing exactly one ++ // entity attribute. ++ if (typeof attrs === "string") { ++ var obj = {}; ++ obj[attrs] = options; ++ return this.set(obj, opts); ++ } ++ // **`.set(entity)`**: In case you'd pass a VIE entity, ++ // the passed entities attributes are being set for the entity. ++ if (attrs.attributes) { ++ attrs = attrs.attributes; ++ } ++ var self = this; ++ var coll; ++ // resolve shortened URIs like rdfs:label.. ++ _.each (attrs, function (value, key) { ++ var newKey = VIE.Util.mapAttributeNS(key, self.vie.namespaces); ++ if (key !== newKey) { ++ delete attrs[key]; ++ attrs[newKey] = value; ++ } ++ }, this); ++ // Finally iterate through the *attributes* to be set and prepare ++ // them for the Backbone.Model.set method. ++ _.each (attrs, function (value, key) { ++ if (!value) { return; } ++ if (key.indexOf('@') === -1) { ++ if (value.isCollection) { ++ // ignore ++ value.each(function (child) { ++ self.vie.entities.addOrUpdate(child); ++ }); ++ } else if (value.isEntity) { ++ self.vie.entities.addOrUpdate(value); ++ coll = new self.vie.Collection(value, { ++ vie: self.vie, ++ predicate: key ++ }); ++ attrs[key] = coll; ++ } else if (_.isArray(value)) { ++ if (this.attributes[key] && this.attributes[key].isCollection) { ++ var newEntities = this.attributes[key].addOrUpdate(value); ++ attrs[key] = this.attributes[key]; ++ attrs[key].reset(newEntities); ++ } ++ } else if (value["@value"]) { ++ // The value is a literal object, ignore ++ } else if (_.isObject(value) && !_.isDate(value)) { ++ // The value is another VIE Entity ++ var child = new self.vie.Entity(value, options); ++ // which is being stored in `v.entities` ++ self.vie.entities.addOrUpdate(child); ++ // and set as VIE Collection attribute on the original entity ++ coll = new self.vie.Collection(value, { ++ vie: self.vie, ++ predicate: key ++ }); ++ attrs[key] = coll; ++ } else { ++ // ignore ++ } ++ } ++ }, this); ++ return Backbone.Model.prototype.set.call(this, attrs, options); ++ }, ++ ++ // **`.unset(attr, opts)` ** removes an attribute from the entity. ++ unset: function (attr, opts) { ++ attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); ++ return Backbone.Model.prototype.unset.call(this, attr, opts); ++ }, ++ ++ // Validation based on type rules. ++ // ++ // There are two ways to skip validation for entity operations: ++ // ++ // * `options.silent = true` ++ // * `options.validate = false` ++ validate: function (attrs, opts) { ++ if (opts && opts.validate === false) { ++ return; ++ } ++ var types = this.get('@type'); ++ if (_.isArray(types)) { ++ var results = []; ++ _.each(types, function (type) { ++ var res = this.validateByType(type, attrs, opts); ++ if (res) { ++ results.push(res); ++ } ++ }, this); ++ if (_.isEmpty(results)) { ++ return; ++ } ++ return _.flatten(results); ++ } ++ ++ return this.validateByType(types, attrs, opts); ++ }, ++ ++ validateByType: function (type, attrs, opts) { ++ var messages = { ++ max: '<%= property %> cannot contain more than <%= num %> items', ++ min: '<%= property %> must contain at least <%= num %> items', ++ required: '<%= property %> is required' ++ }; ++ ++ if (!type.attributes) { ++ return; ++ } ++ ++ var toError = function (definition, constraint, messageValues) { ++ return { ++ property: definition.id, ++ constraint: constraint, ++ message: _.template(messages[constraint], _.extend({ ++ property: definition.id ++ }, messageValues)) ++ }; ++ }; ++ ++ var checkMin = function (definition, attrs) { ++ if (!attrs[definition.id] || _.isEmpty(attrs[definition.id])) { ++ return toError(definition, 'required', {}); ++ } ++ }; ++ ++ // Check the number of items in attr against max ++ var checkMax = function (definition, attrs) { ++ if (!attrs[definition.id]) { ++ return; ++ } ++ ++ if (!attrs[definition.id].isCollection && !_.isArray(attrs[definition.id])) { ++ return; ++ } ++ ++ if (attrs[definition.id].length > definition.max) { ++ return toError(definition, 'max', { ++ num: definition.max ++ }); ++ } ++ }; ++ ++ var results = []; ++ _.each(type.attributes.list(), function (definition) { ++ var res; ++ if (definition.max && definition.max != -1) { ++ res = checkMax(definition, attrs); ++ if (res) { ++ results.push(res); ++ } ++ } ++ ++ if (definition.min && definition.min > 0) { ++ res = checkMin(definition, attrs); ++ if (res) { ++ results.push(res); ++ } ++ } ++ }); ++ ++ if (_.isEmpty(results)) { ++ return; ++ } ++ return results; ++ }, ++ ++ isNew: function() { ++ if (this.getSubjectUri().substr(0, 7) === '_:bnode') { ++ return true; ++ } ++ return false; ++ }, ++ ++ hasChanged: function(attr) { ++ if (this.markedChanged) { ++ return true; ++ } ++ ++ return Backbone.Model.prototype.hasChanged.call(this, attr); ++ }, ++ ++ // Force hasChanged to return true ++ forceChanged: function(changed) { ++ this.markedChanged = changed ? true : false; ++ }, ++ ++ // **`getSubject()`** is the getter for the entity identifier. ++ getSubject: function(){ ++ if (typeof this.id === "undefined") { ++ this.id = this.attributes[this.idAttribute]; ++ } ++ if (typeof this.id === 'string') { ++ if (this.id.substr(0, 7) === 'http://' || this.id.substr(0, 4) === 'urn:') { ++ return this.toReference(this.id); ++ } ++ return this.id; ++ } ++ return this.cid.replace('c', '_:bnode'); ++ }, ++ ++ // TODO describe ++ getSubjectUri: function(){ ++ return this.fromReference(this.getSubject()); ++ }, ++ ++ isReference: function(uri){ ++ var matcher = new RegExp("^\\<([^\\>]*)\\>$"); ++ if (matcher.exec(uri)) { ++ return true; ++ } ++ return false; ++ }, ++ ++ toReference: function(uri){ ++ if (_.isArray(uri)) { ++ var self = this; ++ return _.map(uri, function(part) { ++ return self.toReference(part); ++ }); ++ } ++ var ns = this.vie.namespaces; ++ var ret = uri; ++ if (uri.substring(0, 2) === "_:") { ++ ret = uri; ++ } ++ else if (ns.isCurie(uri)) { ++ ret = ns.uri(uri); ++ if (ret === "<" + ns.base() + uri + ">") { ++ /* no base namespace extension with IDs */ ++ ret = '<' + uri + '>'; ++ } ++ } else if (!ns.isUri(uri)) { ++ ret = '<' + uri + '>'; ++ } ++ return ret; ++ }, ++ ++ fromReference: function(uri){ ++ var ns = this.vie.namespaces; ++ if (!ns.isUri(uri)) { ++ return uri; ++ } ++ return uri.substring(1, uri.length - 1); ++ }, ++ ++ as: function(encoding){ ++ if (encoding === "JSON") { ++ return this.toJSON(); ++ } ++ if (encoding === "JSONLD") { ++ return this.toJSONLD(); ++ } ++ throw new Error("Unknown encoding " + encoding); ++ }, ++ ++ toJSONLD: function(){ ++ var instanceLD = {}; ++ var instance = this; ++ _.each(instance.attributes, function(value, name){ ++ var entityValue = value; //instance.get(name); ++ ++ if (value instanceof instance.vie.Collection) { ++ entityValue = value.map(function(instance) { ++ return instance.getSubject(); ++ }); ++ } ++ ++ // TODO: Handle collections separately ++ instanceLD[name] = entityValue; ++ }); ++ ++ instanceLD['@subject'] = instance.getSubject(); ++ ++ return instanceLD; ++ }, ++ ++ // **`.setOrAdd(arg1, arg2)`** similar to `.set(..)`, `.setOrAdd(..)` can ++ // be used for setting one or more attributes of an entity, but in ++ // this case it's a collection of values, not just one. That means, if the ++ // entity already has the attribute set, make the value to a VIE Collection ++ // and use the collection as value. The collection can contain entities ++ // or literals, but not both at the same time. ++ setOrAdd: function (arg1, arg2, option) { ++ var entity = this; ++ if (typeof arg1 === "string" && arg2) { ++ // calling entity.setOrAdd("rdfs:type", "example:Musician") ++ entity._setOrAddOne(arg1, arg2, option); ++ } ++ else ++ if (typeof arg1 === "object") { ++ // calling entity.setOrAdd({"rdfs:type": "example:Musician", ...}) ++ _(arg1).each(function(val, key){ ++ entity._setOrAddOne(key, val, arg2); ++ }); ++ } ++ return this; ++ }, ++ ++ ++ /* attr is always of type string */ ++ /* value can be of type: string,int,double,object,VIE.Entity,VIE.Collection */ ++ /* val can be of type: undefined,string,int,double,array,VIE.Collection */ ++ ++ /* depending on the type of value and the type of val, different actions need to be made */ ++ _setOrAddOne: function (attr, value, options) { ++ if (!attr || !value) ++ return; ++ options = (options)? options : {}; ++ var v; ++ ++ attr = VIE.Util.mapAttributeNS(attr, self.vie.namespaces); ++ ++ if (_.isArray(value)) { ++ for (v = 0; v < value.length; v++) { ++ this._setOrAddOne(attr, value[v], options); ++ } ++ return; ++ } ++ ++ if (attr === "@type" && value instanceof self.vie.Type) { ++ value = value.id; ++ } ++ ++ var obj = {}; ++ var existing = Backbone.Model.prototype.get.call(this, attr); ++ ++ if (!existing) { ++ obj[attr] = value; ++ this.set(obj, options); ++ } else if (existing.isCollection) { ++ if (value.isCollection) { ++ value.each(function (model) { ++ existing.add(model); ++ }); ++ } else if (value.isEntity) { ++ existing.add(value); ++ } else if (typeof value === "object") { ++ value = new this.vie.Entity(value); ++ existing.add(value); ++ } else { ++ throw new Error("you cannot add a literal to a collection of entities!"); ++ } ++ this.trigger('change:' + attr, this, value, {}); ++ this.change({}); ++ } else if (_.isArray(existing)) { ++ if (value.isCollection) { ++ for (v = 0; v < value.size(); v++) { ++ this._setOrAddOne(attr, value.at(v).getSubject(), options); ++ } ++ } else if (value.isEntity) { ++ this._setOrAddOne(attr, value.getSubject(), options); ++ } else if (typeof value === "object") { ++ value = new this.vie.Entity(value); ++ this._setOrAddOne(attr, value, options); ++ } else { ++ /* yes, we (have to) allow multiple equal values */ ++ existing.push(value); ++ obj[attr] = existing; ++ this.set(obj); ++ } ++ } else { ++ var arr = [ existing ]; ++ arr.push(value); ++ obj[attr] = arr; ++ return this.set(obj, options); ++ } ++ }, ++ ++ // **`.hasType(type)`** determines if the entity has the explicit `type` set. ++ hasType: function(type){ ++ type = self.vie.types.get(type); ++ return this.hasPropertyValue("@type", type); ++ }, ++ ++ // TODO describe ++ hasPropertyValue: function(property, value) { ++ var t = this.get(property); ++ if (!(value instanceof Object)) { ++ value = self.vie.entities.get(value); ++ } ++ if (t instanceof Array) { ++ return t.indexOf(value) !== -1; ++ } ++ else { ++ return t === value; ++ } ++ }, ++ ++ // **`.isof(type)`** determines if the entity is of `type` by explicit or implicit ++ // declaration. E.g. if Employee is a subtype of Person and e Entity has ++ // explicitly set type Employee, e.isof(Person) will evaluate to true. ++ isof: function (type) { ++ var types = this.get('@type'); ++ ++ if (types === undefined) { ++ return false; ++ } ++ types = (_.isArray(types))? types : [ types ]; ++ ++ type = (self.vie.types.get(type))? self.vie.types.get(type) : new self.vie.Type(type); ++ for (var t = 0; t < types.length; t++) { ++ if (self.vie.types.get(types[t])) { ++ if (self.vie.types.get(types[t]).isof(type)) { ++ return true; ++ } ++ } else { ++ var typeTmp = new self.vie.Type(types[t]); ++ if (typeTmp.id === type.id) { ++ return true; ++ } ++ } ++ } ++ return false; ++ }, ++ // TODO describe ++ addTo : function (collection, update) { ++ var self = this; ++ if (collection instanceof self.vie.Collection) { ++ if (update) { ++ collection.addOrUpdate(self); ++ } else { ++ collection.add(self); ++ } ++ return this; ++ } ++ throw new Error("Please provide a proper collection of type VIE.Collection as argument!"); ++ }, ++ ++ isEntity: true, ++ ++ vie: self.vie ++ }); ++ ++ return new Model(attrs, opts); ++}; ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++VIE.prototype.Collection = Backbone.Collection.extend({ ++ model: VIE.prototype.Entity, ++ ++ initialize: function (models, options) { ++ if (!options || !options.vie) { ++ throw new Error('Each collection needs a VIE reference'); ++ } ++ this.vie = options.vie; ++ this.predicate = options.predicate; ++ }, ++ ++ canAdd: function (type) { ++ return true; ++ }, ++ ++ get: function(id) { ++ if (id === null) { ++ return null; ++ } ++ ++ id = (id.getSubject)? id.getSubject() : id; ++ if (typeof id === "string" && id.indexOf("_:") === 0) { ++ if (id.indexOf("bnode") === 2) { ++ //bnode! ++ id = id.replace("_:bnode", 'c'); ++ return this._byCid[id]; ++ } else { ++ return this._byId["<" + id + ">"]; ++ } ++ } else { ++ id = this.toReference(id); ++ return this._byId[id]; ++ } ++ }, ++ ++ addOrUpdate: function(model, options) { ++ options = options || {}; ++ ++ var collection = this; ++ var existing; ++ if (_.isArray(model)) { ++ var entities = []; ++ _.each(model, function(item) { ++ entities.push(collection.addOrUpdate(item, options)); ++ }); ++ return entities; ++ } ++ ++ if (model === undefined) { ++ throw new Error("No model given"); ++ } ++ ++ if (_.isString(model)) { ++ model = { ++ '@subject': model, ++ id: model ++ }; ++ } ++ ++ if (!model.isEntity) { ++ model = new this.model(model); ++ } ++ ++ if (model.id && this.get(model.id)) { ++ existing = this.get(model.id); ++ } ++ if (this.getByCid(model.cid)) { ++ existing = this.getByCid(model.cid); ++ } ++ if (existing) { ++ var newAttribs = {}; ++ _.each(model.attributes, function(value, attribute) { ++ if (!existing.has(attribute)) { ++ newAttribs[attribute] = value; ++ return true; ++ } ++ ++ if (attribute === '@subject') { ++ if (model.isNew() && !existing.isNew()) { ++ // Save order issue, skip ++ return true; ++ } ++ } ++ ++ if (existing.get(attribute) === value) { ++ return true; ++ } ++ //merge existing attribute values with new ones! ++ //not just overwrite 'em!! ++ var oldVals = existing.attributes[attribute]; ++ var newVals = value; ++ if (oldVals instanceof collection.vie.Collection) { ++ // TODO: Merge collections ++ return true; ++ } ++ if (options.overrideAttributes) { ++ newAttribs[attribute] = value; ++ return true; ++ } ++ if (attribute === '@context') { ++ newAttribs[attribute] = jQuery.extend(true, {}, oldVals, newVals); ++ } else { ++ oldVals = (jQuery.isArray(oldVals))? oldVals : [ oldVals ]; ++ newVals = (jQuery.isArray(newVals))? newVals : [ newVals ]; ++ newAttribs[attribute] = _.uniq(oldVals.concat(newVals)); ++ newAttribs[attribute] = (newAttribs[attribute].length === 1)? newAttribs[attribute][0] : newAttribs[attribute]; ++ } ++ }); ++ ++ if (!_.isEmpty(newAttribs)) { ++ existing.set(newAttribs, options.updateOptions); ++ } ++ return existing; ++ } ++ this.add(model, options.addOptions); ++ return model; ++ }, ++ ++ isReference: function(uri){ ++ var matcher = new RegExp("^\\<([^\\>]*)\\>$"); ++ if (matcher.exec(uri)) { ++ return true; ++ } ++ return false; ++ }, ++ ++ toReference: function(uri){ ++ if (this.isReference(uri)) { ++ return uri; ++ } ++ return '<' + uri + '>'; ++ }, ++ ++ fromReference: function(uri){ ++ if (!this.isReference(uri)) { ++ return uri; ++ } ++ return uri.substring(1, uri.length - 1); ++ }, ++ ++ isCollection: true ++}); ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++// ++ ++// ## VIE.Types ++// Within VIE, we provide special capabilities of handling types of entites. This helps ++// for example to query easily for certain entities (e.g., you only need to query for *Person*s ++// and not for all subtypes). ++if (VIE.prototype.Type) { ++ throw new Error("ERROR: VIE.Type is already defined. Please check your installation!"); ++} ++if (VIE.prototype.Types) { ++ throw new Error("ERROR: VIE.Types is already defined. Please check your installation!"); ++} ++ ++// ### VIE.Type(id, attrs, metadata) ++// This is the constructor of a VIE.Type. ++// **Parameters**: ++// *{string}* **id** The id of the type. ++// *{string|array|VIE.Attribute}* **attrs** A string, proper ```VIE.Attribute``` or an array of these which ++// *{object}* **metadata** Possible metadata about the type ++// are the possible attributes of the type ++// **Throws**: ++// *{Error}* if one of the given paramenters is missing. ++// **Returns**: ++// *{VIE.Type}* : A **new** VIE.Type object. ++// **Example usage**: ++// ++// var person = new vie.Type("Person", ["name", "knows"]); ++VIE.prototype.Type = function (id, attrs, metadata) { ++ if (id === undefined || typeof id !== 'string') { ++ throw "The type constructor needs an 'id' of type string! E.g., 'Person'"; ++ } ++ ++// ### id ++// This field stores the id of the type's instance. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{string}* : The id of the type as a URI. ++// **Example usage**: ++// ++// console.log(person.id); ++// // --> "" ++ this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); ++ ++ /* checks whether such a type is already defined. */ ++ if (this.vie.types.get(this.id)) { ++ throw new Error("The type " + this.id + " is already defined!"); ++ } ++ ++// ### supertypes ++// This field stores all parent types of the type's instance. This ++// is set if the current type inherits from another type. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{VIE.Types}* : The supertypes (parents) of the type. ++// **Example usage**: ++// ++// console.log(person.supertypes); ++ this.supertypes = new this.vie.Types(); ++ ++// ### subtypes ++// This field stores all children types of the type's instance. This ++// will be set if another type inherits from the current type. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{VIE.Types}* : The subtypes (parents) of the type. ++// **Example usage**: ++// ++// console.log(person.subtypes); ++ this.subtypes = new this.vie.Types(); ++ ++// ### attributes ++// This field stores all attributes of the type's instance as ++// a proper ```VIE.Attributes``` class. (see also VIE.Attributes) ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{VIE.Attributes}* : The attributes of the type. ++// **Example usage**: ++// ++// console.log(person.attributes); ++ this.attributes = new this.vie.Attributes(this, (attrs)? attrs : []); ++ ++// ### metadata ++// This field stores possible additional information about the type, like ++// a human-readable label. ++ this.metadata = metadata ? metadata : {}; ++ ++// ### isof(type) ++// This method checks whether the current type is a child of the given type. ++// **Parameters**: ++// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked. ++// **Throws**: ++// *{Error}* If the type is not valid. ++// **Returns**: ++// *{boolean}* : ```true``` if the current type inherits from the type, ```false``` otherwise. ++// **Example usage**: ++// ++// console.log(person.isof("owl:Thing")); ++// // <-- true ++ this.isof = function (type) { ++ type = this.vie.types.get(type); ++ if (type) { ++ return type.subsumes(this.id); ++ } else { ++ throw new Error("No valid type given"); ++ } ++ }; ++ ++// ### subsumes(type) ++// This method checks whether the current type is a parent of the given type. ++// **Parameters**: ++// *{string|VIE.Type}* **type** The type (or the id of that type) to be checked. ++// **Throws**: ++// *{Error}* If the type is not valid. ++// **Returns**: ++// *{boolean}* : ```true``` if the current type is a parent of the type, ```false``` otherwise. ++// **Example usage**: ++// ++// var x = new vie.Type(...); ++// var y = new vie.Type(...).inherit(x); ++// y.isof(x) === x.subsumes(y); ++ this.subsumes = function (type) { ++ type = this.vie.types.get(type); ++ if (type) { ++ if (this.id === type.id) { ++ return true; ++ } ++ var subtypes = this.subtypes.list(); ++ for (var c = 0; c < subtypes.length; c++) { ++ var childObj = subtypes[c]; ++ if (childObj) { ++ if (childObj.id === type.id || childObj.subsumes(type)) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } else { ++ throw new Error("No valid type given"); ++ } ++ }; ++ ++// ### inherit(supertype) ++// This method invokes inheritance throught the types. This adds the current type to the ++// subtypes of the supertype and vice versa. ++// **Parameters**: ++// *{string|VIE.Type|array}* **supertype** The type to be inherited from. If this is an array ++// the inherit method is called sequentially on all types. ++// **Throws**: ++// *{Error}* If the type is not valid. ++// **Returns**: ++// *{VIE.Type}* : The instance itself. ++// **Example usage**: ++// ++// var x = new vie.Type(...); ++// var y = new vie.Type(...).inherit(x); ++// y.isof(x) // <-- true ++ this.inherit = function (supertype) { ++ if (typeof supertype === "string") { ++ this.inherit(this.vie.types.get(supertype)); ++ } ++ else if (supertype instanceof this.vie.Type) { ++ supertype.subtypes.addOrOverwrite(this); ++ this.supertypes.addOrOverwrite(supertype); ++ try { ++ /* only for validation of attribute-inheritance! ++ if this throws an error (inheriting two attributes ++ that cannot be combined) we reverse all changes. */ ++ this.attributes.list(); ++ } catch (e) { ++ supertype.subtypes.remove(this); ++ this.supertypes.remove(supertype); ++ throw e; ++ } ++ } else if (jQuery.isArray(supertype)) { ++ for (var i = 0, slen = supertype.length; i < slen; i++) { ++ this.inherit(supertype[i]); ++ } ++ } else { ++ throw new Error("Wrong argument in VIE.Type.inherit()"); ++ } ++ return this; ++ }; ++ ++// ### hierarchy() ++// This method serializes the hierarchy of child types into an object. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{object}* : The hierachy of child types as an object. ++// **Example usage**: ++// ++// var x = new vie.Type(...); ++// var y = new vie.Type(...).inherit(x); ++// x.hierarchy(); ++ this.hierarchy = function () { ++ var obj = {id : this.id, subtypes: []}; ++ var list = this.subtypes.list(); ++ for (var c = 0, llen = list.length; c < llen; c++) { ++ var childObj = this.vie.types.get(list[c]); ++ obj.subtypes.push(childObj.hierarchy()); ++ } ++ return obj; ++ }; ++ ++// ### instance() ++// This method creates a ```VIE.Entity``` instance from this type. ++// **Parameters**: ++// *{object}* **attrs** see constructor of VIE.Entity ++// *{object}* **opts** see constructor of VIE.Entity ++// **Throws**: ++// *{Error}* if the instance could not be built ++// **Returns**: ++// *{VIE.Entity}* : A **new** instance of a ```VIE.Entity``` with the current type. ++// **Example usage**: ++// ++// var person = new vie.Type("person"); ++// var sebastian = person.instance( ++// {"@subject" : "#me", ++// "name" : "Sebastian"}); ++// console.log(sebastian.get("name")); // <-- "Sebastian" ++ this.instance = function (attrs, opts) { ++ attrs = (attrs)? attrs : {}; ++ opts = (opts)? opts : {}; ++ ++ /* turn type/attribute checking on by default! */ ++ if (opts.typeChecking !== false) { ++ for (var a in attrs) { ++ if (a.indexOf('@') !== 0 && !this.attributes.get(a)) { ++ throw new Error("Cannot create an instance of " + this.id + " as the type does not allow an attribute '" + a + "'!"); ++ } ++ } ++ } ++ ++ if (attrs['@type']) { ++ attrs['@type'].push(this.id); ++ } else { ++ attrs['@type'] = this.id; ++ } ++ ++ return new this.vie.Entity(attrs, opts); ++ }; ++ ++// ### toString() ++// This method returns the id of the type. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{string}* : The id of the type. ++// **Example usage**: ++// ++// var x = new vie.Type(...); ++// x.toString() === x.id; ++ this.toString = function () { ++ return this.id; ++ }; ++}; ++ ++// ### VIE.Types() ++// This is the constructor of a VIE.Types. This is a convenience class ++// to store ```VIE.Type``` instances properly. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Types}* : A **new** VIE.Types object. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++VIE.prototype.Types = function () { ++ ++ this._types = {}; ++ ++// ### add(id, attrs, metadata) ++// This method adds a `VIE.Type` to the types. ++// **Parameters**: ++// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added. ++// *{string|object}* **attrs** Only used if ```id``` is a string. ++// *{object}* **metadata** potential additional metadata about the type. ++// **Throws**: ++// *{Error}* if a type with the given id already exists a ```VIE.Entity``` instance from this type. ++// **Returns**: ++// *{VIE.Types}* : The instance itself. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.add("Person", ["name", "knows"]); ++ this.add = function (id, attrs, metadata) { ++ if (_.isArray(id)) { ++ _.each(id, function (type) { ++ this.add(type); ++ }, this); ++ return this; ++ } ++ ++ if (this.get(id)) { ++ throw new Error("Type '" + id + "' already registered."); ++ } else { ++ if (typeof id === "string") { ++ var t = new this.vie.Type(id, attrs, metadata); ++ this._types[t.id] = t; ++ return t; ++ } else if (id instanceof this.vie.Type) { ++ this._types[id.id] = id; ++ return id; ++ } else { ++ throw new Error("Wrong argument to VIE.Types.add()!"); ++ } ++ } ++ return this; ++ }; ++ ++// ### addOrOverwrite(id, attrs) ++// This method adds or overwrites a `VIE.Type` to the types. This is the same as ++// ``this.remove(id); this.add(id, attrs);`` ++// **Parameters**: ++// *{string|VIE.Type}* **id** If this is a string, the type is created and directly added. ++// *{string|object}* **attrs** Only used if ```id``` is a string. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Types}* : The instance itself. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.addOrOverwrite("Person", ["name", "knows"]); ++ this.addOrOverwrite = function(id, attrs){ ++ if (this.get(id)) { ++ this.remove(id); ++ } ++ return this.add(id, attrs); ++ }; ++ ++// ### get(id) ++// This method retrieves a `VIE.Type` from the types by it's id. ++// **Parameters**: ++// *{string|VIE.Type}* **id** The id or the type itself. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Type}* : The instance of the type or ```undefined```. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.addOrOverwrite("Person", ["name", "knows"]); ++// types.get("Person"); ++ this.get = function (id) { ++ if (!id) { ++ return undefined; ++ } ++ if (typeof id === 'string') { ++ var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); ++ return this._types[lid]; ++ } else if (id instanceof this.vie.Type) { ++ return this.get(id.id); ++ } ++ return undefined; ++ }; ++ ++// ### remove(id) ++// This method removes a type of given id from the type. This also ++// removes all children if their only parent were this ++// type. Furthermore, this removes the link from the ++// super- and subtypes. ++// **Parameters**: ++// *{string|VIE.Type}* **id** The id or the type itself. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Type}* : The removed type. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.addOrOverwrite("Person", ["name", "knows"]); ++// types.remove("Person"); ++ this.remove = function (id) { ++ var t = this.get(id); ++ /* test whether the type actually exists in VIE ++ * and prevents removing *owl:Thing*. ++ */ ++ if (!t) { ++ return this; ++ } ++ if (!t || t.subsumes("owl:Thing")) { ++ console.warn("You are not allowed to remove 'owl:Thing'."); ++ return this; ++ } ++ delete this._types[t.id]; ++ ++ var subtypes = t.subtypes.list(); ++ for (var c = 0; c < subtypes.length; c++) { ++ var childObj = subtypes[c]; ++ if (childObj.supertypes.list().length === 1) { ++ /* recursively remove all children ++ that inherit only from this type */ ++ this.remove(childObj); ++ } else { ++ childObj.supertypes.remove(t.id); ++ } ++ } ++ return t; ++ }; ++ ++// ### toArray() === list() ++// This method returns an array of all types. ++// **Parameters**: ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{array}* : An array of ```VIE.Type``` instances. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.addOrOverwrite("Person", ["name", "knows"]); ++// types.list(); ++ this.toArray = this.list = function () { ++ var ret = []; ++ for (var i in this._types) { ++ ret.push(this._types[i]); ++ } ++ return ret; ++ }; ++ ++// ### sort(types, desc) ++// This method sorts an array of types in their order, given by the ++// inheritance. This returns a copy and leaves the original array untouched. ++// **Parameters**: ++// *{array|VIE.Type}* **types** The array of ```VIE.Type``` instances or ids of types to be sorted. ++// *{boolean}* **desc** If 'desc' is given and 'true', the array will be sorted ++// in descendant order. ++// *nothing* ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{array}* : A sorted copy of the array. ++// **Example usage**: ++// ++// var types = new vie.Types(); ++// types.addOrOverwrite("Person", ["name", "knows"]); ++// types.sort(types.list(), true); ++ this.sort = function (types, desc) { ++ var self = this; ++ types = (jQuery.isArray(types))? types : [ types ]; ++ desc = (desc)? true : false; ++ ++ if (types.length === 0) return []; ++ var copy = [ types[0] ]; ++ var x, tlen; ++ for (x = 1, tlen = types.length; x < tlen; x++) { ++ var insert = types[x]; ++ var insType = self.get(insert); ++ if (insType) { ++ for (var y = 0; y < copy.length; y++) { ++ if (insType.subsumes(copy[y])) { ++ copy.splice(y,0,insert); ++ break; ++ } else if (y === copy.length - 1) { ++ copy.push(insert); ++ } ++ } ++ } ++ } ++ ++ //unduplicate ++ for (x = 0; x < copy.length; x++) { ++ if (copy.lastIndexOf(copy[x]) !== x) { ++ copy.splice(x, 1); ++ x--; ++ } ++ } ++ ++ if (!desc) { ++ copy.reverse(); ++ } ++ return copy; ++ }; ++}; ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++// ++ ++// ## VIE.Attributes ++// Within VIE, we provide special capabilities of handling attributes of types of entites. This ++// helps first of all to list all attributes of an entity type, but furthermore fully supports ++// inheritance of attributes from the type-class to inherit from. ++if (VIE.prototype.Attribute) { ++ throw new Error("ERROR: VIE.Attribute is already defined. Please check your VIE installation!"); ++} ++if (VIE.prototype.Attributes) { ++ throw new Error("ERROR: VIE.Attributes is already defined. Please check your VIE installation!"); ++} ++ ++// ### VIE.Attribute(id, range, domain, minCount, maxCount, metadata) ++// This is the constructor of a VIE.Attribute. ++// **Parameters**: ++// *{string}* **id** The id of the attribute. ++// *{string|array}* **range** A string or an array of strings of the target range of ++// the attribute. ++// *{string}* **domain** The domain of the attribute. ++// *{number}* **minCount** The minimal number this attribute can occur. (needs to be >= 0) ++// *{number}* **maxCount** The maximal number this attribute can occur. (needs to be >= minCount, use `-1` for unlimited) ++// *{object}* **metadata** Possible metadata about the attribute ++// **Throws**: ++// *{Error}* if one of the given paramenters is missing. ++// **Returns**: ++// *{VIE.Attribute}* : A **new** VIE.Attribute object. ++// **Example usage**: ++// ++// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person", 0, 10); ++// // Creates an attribute to describe a *knows*-relationship ++// // between persons. Each person can only have ++VIE.prototype.Attribute = function (id, range, domain, minCount, maxCount, metadata) { ++ if (id === undefined || typeof id !== 'string') { ++ throw new Error("The attribute constructor needs an 'id' of type string! E.g., 'Person'"); ++ } ++ if (range === undefined) { ++ throw new Error("The attribute constructor of " + id + " needs 'range'."); ++ } ++ if (domain === undefined) { ++ throw new Error("The attribute constructor of " + id + " needs a 'domain'."); ++ } ++ ++ this._domain = domain; ++ ++// ### id ++// This field stores the id of the attribute's instance. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{string}* : A URI, representing the id of the attribute. ++// **Example usage**: ++// ++// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); ++// console.log(knowsAttr.id); ++// // --> ++ this.id = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); ++ ++// ### range ++// This field stores the ranges of the attribute's instance. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{array}* : An array of strings which represent the types. ++// **Example usage**: ++// ++// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); ++// console.log(knowsAttr.range); ++// // --> ["Person"] ++ this.range = (_.isArray(range))? range : [ range ]; ++ ++// ### min ++// This field stores the minimal amount this attribute can occur in the type's instance. The number ++// needs to be greater or equal to zero. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{int}* : The minimal amount this attribute can occur. ++// **Example usage**: ++// ++// console.log(person.min); ++// // --> 0 ++ minCount = minCount ? minCount : 0; ++ this.min = (minCount > 0) ? minCount : 0; ++ ++// ### max ++// This field stores the maximal amount this attribute can occur in the type's instance. ++// This number cannot be smaller than min ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{int}* : The maximal amount this attribute can occur. ++// **Example usage**: ++// ++// console.log(person.max); ++// // --> 1.7976931348623157e+308 ++ maxCount = maxCount ? maxCount : 1; ++ if (maxCount === -1) { ++ maxCount = Number.MAX_VALUE; ++ } ++ this.max = (maxCount >= this.min)? maxCount : this.min; ++ ++// ### metadata ++// This field holds potential metadata about the attribute. ++ this.metadata = metadata ? metadata : {}; ++ ++// ### applies(range) ++// This method checks, whether the current attribute applies in the given range. ++// If ```range``` is a string and cannot be transformed into a ```VIE.Type```, ++// this performs only string comparison, if it is a VIE.Type ++// or an ID of a VIE.Type, then inheritance is checked as well. ++// **Parameters**: ++// *{string|VIE.Type}* **range** The ```VIE.Type``` (or it's string representation) to be checked. ++// **Throws**: ++// nothing ++// **Returns**: ++// *{boolean}* : ```true``` if the given type applies to this attribute and ```false``` otherwise. ++// **Example usage**: ++// ++// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); ++// console.log(knowsAttr.applies("Person")); // --> true ++// console.log(knowsAttr.applies("Place")); // --> false ++ this.applies = function (range) { ++ if (this.vie.types.get(range)) { ++ range = this.vie.types.get(range); ++ } ++ for (var r = 0, len = this.range.length; r < len; r++) { ++ var x = this.vie.types.get(this.range[r]); ++ if (x === undefined && typeof range === "string") { ++ if (range === this.range[r]) { ++ return true; ++ } ++ } ++ else { ++ if (range.isof(this.range[r])) { ++ return true; ++ } ++ } ++ } ++ return false; ++ }; ++ ++}; ++ ++// ## VIE.Attributes(domain, attrs) ++// This is the constructor of a VIE.Attributes. Basically a convenience class ++// that represents a list of ```VIE.Attribute```. As attributes are part of a ++// certain ```VIE.Type```, it needs to be passed for inheritance checks. ++// **Parameters**: ++// *{string}* **domain** The domain of the attributes (the type they will be part of). ++// *{string|VIE.Attribute|array}* **attrs** Either a string representation of an attribute, ++// a proper instance of ```VIE.Attribute``` or an array of both. ++// *{string}* **domain** The domain of the attribute. ++// **Throws**: ++// *{Error}* if one of the given paramenters is missing. ++// **Returns**: ++// *{VIE.Attribute}* : A **new** VIE.Attribute instance. ++// **Example usage**: ++// ++// var knowsAttr = new vie.Attribute("knows", ["Person"], "Person"); ++// var personAttrs = new vie.Attributes("Person", knowsAttr); ++VIE.prototype.Attributes = function (domain, attrs) { ++ ++ this._local = {}; ++ this._attributes = {}; ++ ++// ### domain ++// This field stores the domain of the attributes' instance. ++// **Parameters**: ++// nothing ++// **Throws**: ++// nothing ++// **Returns**: ++// *{string}* : The string representation of the domain. ++// **Example usage**: ++// ++// console.log(personAttrs.domain); ++// // --> ["Person"] ++ this.domain = domain; ++ ++// ### add(id, range, min, max, metadata) ++// This method adds a ```VIE.Attribute``` to the attributes instance. ++// **Parameters**: ++// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper ++// instance of a ```VIE.Attribute```. ++// *{string|array}* **range** An array representing the target range of the attribute. ++// *{number}* **min** The minimal amount this attribute can appear. ++// instance of a ```VIE.Attribute```. ++// *{number}* **max** The maximal amount this attribute can appear. ++// *{object}* **metadata** Additional metadata for the attribute. ++// **Throws**: ++// *{Error}* If an atribute with the given id is already registered. ++// *{Error}* If the ```id``` parameter is not a string, nor a ```VIE.Type``` instance. ++// **Returns**: ++// *{VIE.Attribute}* : The generated or passed attribute. ++// **Example usage**: ++// ++// personAttrs.add("name", "Text", 0, 1); ++ this.add = function (id, range, min, max, metadata) { ++ if (_.isArray(id)) { ++ _.each(id, function (attribute) { ++ this.add(attribute); ++ }, this); ++ return this; ++ } ++ ++ if (this.get(id)) { ++ throw new Error("Attribute '" + id + "' already registered for domain " + this.domain.id + "!"); ++ } else { ++ if (typeof id === "string") { ++ var a = new this.vie.Attribute(id, range, this.domain, min, max, metadata); ++ this._local[a.id] = a; ++ return a; ++ } else if (id instanceof this.vie.Attribute) { ++ id.domain = this.domain; ++ id.vie = this.vie; ++ this._local[id.id] = id; ++ return id; ++ } else { ++ throw new Error("Wrong argument to VIE.Types.add()!"); ++ } ++ } ++ }; ++ ++// ### remove(id) ++// This method removes a ```VIE.Attribute``` from the attributes instance. ++// **Parameters**: ++// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper ++// instance of a ```VIE.Attribute```. ++// **Throws**: ++// *{Error}* When the attribute is inherited from a parent ```VIE.Type``` and thus cannot be removed. ++// **Returns**: ++// *{VIE.Attribute}* : The removed attribute. ++// **Example usage**: ++// ++// personAttrs.remove("knows"); ++ this.remove = function (id) { ++ var a = this.get(id); ++ if (a.id in this._local) { ++ delete this._local[a.id]; ++ return a; ++ } ++ throw new Error("The attribute " + id + " is inherited and cannot be removed from the domain " + this.domain.id + "!"); ++ }; ++ ++// ### get(id) ++// This method returns a ```VIE.Attribute``` from the attributes instance by it's id. ++// **Parameters**: ++// *{string|VIE.Attribute}* **id** The string representation of an attribute, or a proper ++// instance of a ```VIE.Attribute```. ++// **Throws**: ++// *{Error}* When the method is called with an unknown datatype. ++// **Returns**: ++// *{VIE.Attribute}* : The attribute. ++// **Example usage**: ++// ++// personAttrs.get("knows"); ++ this.get = function (id) { ++ if (typeof id === 'string') { ++ var lid = this.vie.namespaces.isUri(id) ? id : this.vie.namespaces.uri(id); ++ return this._inherit()._attributes[lid]; ++ } else if (id instanceof this.vie.Attribute) { ++ return this.get(id.id); ++ } else { ++ throw new Error("Wrong argument in VIE.Attributes.get()"); ++ } ++ }; ++ ++// ### _inherit() ++// The private method ```_inherit``` creates a full list of all attributes. This includes ++// local attributes as well as inherited attributes from the parents. The ranges of attributes ++// with the same id will be merged. This method is called everytime an attribute is requested or ++// the list of all attributes. Usually this method should not be invoked outside of the class. ++// **Parameters**: ++// *nothing* ++// instance of a ```VIE.Attribute```. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *nothing* ++// **Example usage**: ++// ++// personAttrs._inherit(); ++ this._inherit = function () { ++ var a, x, id; ++ var attributes = jQuery.extend(true, {}, this._local); ++ ++ var inherited = _.map(this.domain.supertypes.list(), ++ function (x) { ++ return x.attributes; ++ } ++ ); ++ ++ var add = {}; ++ var merge = {}; ++ var ilen, alen; ++ for (a = 0, ilen = inherited.length; a < ilen; a++) { ++ var attrs = inherited[a].list(); ++ for (x = 0, alen = attrs.length; x < alen; x++) { ++ id = attrs[x].id; ++ if (!(id in attributes)) { ++ if (!(id in add) && !(id in merge)) { ++ add[id] = attrs[x]; ++ } ++ else { ++ if (!merge[id]) { ++ merge[id] = {range : [], mins : [], maxs: [], metadatas: []}; ++ } ++ if (id in add) { ++ merge[id].range = jQuery.merge(merge[id].range, add[id].range); ++ merge[id].mins = jQuery.merge(merge[id].mins, [ add[id].min ]); ++ merge[id].maxs = jQuery.merge(merge[id].maxs, [ add[id].max ]); ++ merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ add[id].metadata ]); ++ delete add[id]; ++ } ++ merge[id].range = jQuery.merge(merge[id].range, attrs[x].range); ++ merge[id].mins = jQuery.merge(merge[id].mins, [ attrs[x].min ]); ++ merge[id].maxs = jQuery.merge(merge[id].maxs, [ attrs[x].max ]); ++ merge[id].metadatas = jQuery.merge(merge[id].metadatas, [ attrs[x].metadata ]); ++ merge[id].range = _.uniq(merge[id].range); ++ merge[id].mins = _.uniq(merge[id].mins); ++ merge[id].maxs = _.uniq(merge[id].maxs); ++ merge[id].metadatas = _.uniq(merge[id].metadatas); ++ } ++ } ++ } ++ } ++ ++ /* adds inherited attributes that do not need to be merged */ ++ jQuery.extend(attributes, add); ++ ++ /* merges inherited attributes */ ++ for (id in merge) { ++ var mranges = merge[id].range; ++ var mins = merge[id].mins; ++ var maxs = merge[id].maxs; ++ var metadatas = merge[id].metadatas; ++ var ranges = []; ++ //merging ranges ++ for (var r = 0, mlen = mranges.length; r < mlen; r++) { ++ var p = this.vie.types.get(mranges[r]); ++ var isAncestorOf = false; ++ if (p) { ++ for (x = 0; x < mlen; x++) { ++ if (x === r) { ++ continue; ++ } ++ var c = this.vie.types.get(mranges[x]); ++ if (c && c.isof(p)) { ++ isAncestorOf = true; ++ break; ++ } ++ } ++ } ++ if (!isAncestorOf) { ++ ranges.push(mranges[r]); ++ } ++ } ++ ++ var maxMin = _.max(mins); ++ var minMax = _.min(maxs); ++ if (maxMin <= minMax && minMax >= 0 && maxMin >= 0) { ++ attributes[id] = new this.vie.Attribute(id, ranges, this, maxMin, minMax, metadatas[0]); ++ } else { ++ throw new Error("This inheritance is not allowed because of an invalid minCount/maxCount pair!"); ++ } ++ } ++ ++ this._attributes = attributes; ++ return this; ++ }; ++ ++// ### toArray() === list() ++// This method return an array of ```VIE.Attribute```s from the attributes instance. ++// **Parameters**: ++// *nothing. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{array}* : An array of ```VIE.Attribute```. ++// **Example usage**: ++// ++// personAttrs.list(); ++ this.toArray = this.list = function (range) { ++ var ret = []; ++ var attributes = this._inherit()._attributes; ++ for (var a in attributes) { ++ if (!range || attributes[a].applies(range)) { ++ ret.push(attributes[a]); ++ } ++ } ++ return ret; ++ }; ++ ++ attrs = _.isArray(attrs) ? attrs : [ attrs ]; ++ _.each(attrs, function (attr) { ++ this.add(attr.id, attr.range, attr.min, attr.max, attr.metadata); ++ }, this); ++}; ++// VIE - Vienna IKS Editables ++// (c) 2011 Henri Bergius, IKS Consortium ++// (c) 2011 Sebastian Germesin, IKS Consortium ++// (c) 2011 Szaby Grünwald, IKS Consortium ++// VIE may be freely distributed under the MIT license. ++// For all details and documentation: ++// http://viejs.org/ ++if (VIE.prototype.Namespaces) { ++ throw new Error("ERROR: VIE.Namespaces is already defined. " + ++ "Please check your VIE installation!"); ++} ++ ++// ## VIE Namespaces ++// ++// In general, a namespace is a container that provides context for the identifiers. ++// Within VIE, namespaces are used to distinguish different ontolgies or vocabularies ++// of identifiers, types and attributes. However, because of their verbosity, namespaces ++// tend to make their usage pretty circuitous. The ``VIE.Namespaces(...)`` class provides VIE ++// with methods to maintain abbreviations (akak **prefixes**) for namespaces in order to ++// alleviate their usage. By default, every VIE instance is equipped with a main instance ++// of the namespaces in ``myVIE.namespaces``. Furthermore, VIE uses a **base namespace**, ++// which is used if no prefix is given (has an empty prefix). ++// In the upcoming sections, we will explain the ++// methods to add, access and remove prefixes. ++ ++ ++ ++// ## VIE.Namespaces(base, namespaces) ++// This is the constructor of a VIE.Namespaces. The constructor initially ++// needs a *base namespace* and can optionally be initialised with an ++// associative array of prefixes and namespaces. The base namespace is used in a way ++// that every non-prefixed, non-expanded attribute or type is assumed to be of that ++// namespace. This helps, e.g., in an environment where only one namespace is given. ++// **Parameters**: ++// *{string}* **base** The base namespace. ++// *{object}* **namespaces** Initial namespaces to bootstrap the namespaces. (optional) ++// **Throws**: ++// *{Error}* if the base namespace is missing. ++// **Returns**: ++// *{VIE.Attribute}* : A **new** VIE.Attribute object. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces("http://viejs.org/ns/", ++// { ++// "foaf": "http://xmlns.com/foaf/0.1/" ++// }); ++VIE.prototype.Namespaces = function (base, namespaces) { ++ ++ if (!base) { ++ throw new Error("Please provide a base namespace!"); ++ } ++ this._base = base; ++ ++ this._namespaces = (namespaces)? namespaces : {}; ++ if (typeof this._namespaces !== "object" || _.isArray(this._namespaces)) { ++ throw new Error("If you want to initialise VIE namespace prefixes, " + ++ "please provide a proper object!"); ++ } ++}; ++ ++ ++// ### base(ns) ++// This is a **getter** and **setter** for the base ++// namespace. If called like ``base();`` it ++// returns the actual base namespace as a string. If provided ++// with a string, e.g., ``base("http://viejs.org/ns/");`` ++// it sets the current base namespace and retuns the namespace object ++// for the purpose of chaining. If provided with anything except a string, ++// it throws an Error. ++// **Parameters**: ++// *{string}* **ns** The namespace to be set. (optional) ++// **Throws**: ++// *{Error}* if the namespace is not of type string. ++// **Returns**: ++// *{string}* : The current base namespace. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// console.log(namespaces.base()); // <-- "http://base.ns/" ++// namespaces.base("http://viejs.org/ns/"); ++// console.log(namespaces.base()); // <-- "http://viejs.org/ns/" ++VIE.prototype.Namespaces.prototype.base = function (ns) { ++ if (!ns) { ++ return this._base; ++ } ++ else if (typeof ns === "string") { ++ /* remove another mapping */ ++ this.removeNamespace(ns); ++ this._base = ns; ++ return this._base; ++ } else { ++ throw new Error("Please provide a valid namespace!"); ++ } ++}; ++ ++// ### add(prefix, namespace) ++// This method adds new prefix mappings to the ++// current instance. If a prefix or a namespace is already ++// present (in order to avoid ambiguities), an Error is thrown. ++// ``prefix`` can also be an object in which case, the method ++// is called sequentially on all elements. ++// **Parameters**: ++// *{string|object}* **prefix** The prefix to be set. If it is an object, the ++// method will be applied to all key,value pairs sequentially. ++// *{string}* **namespace** The namespace to be set. ++// **Throws**: ++// *{Error}* If a prefix or a namespace is already ++// present (in order to avoid ambiguities). ++// **Returns**: ++// *{VIE.Namespaces}* : The current namespaces instance. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.add("", "http://..."); ++// // is always equal to ++// namespaces.base("http://..."); // <-- setter of base namespace ++VIE.prototype.Namespaces.prototype.add = function (prefix, namespace) { ++ if (typeof prefix === "object") { ++ for (var k1 in prefix) { ++ this.add(k1, prefix[k1]); ++ } ++ return this; ++ } ++ if (prefix === "") { ++ this.base(namespace); ++ return this; ++ } ++ /* checking if we overwrite existing mappings */ ++ else if (this.contains(prefix) && namespace !== this._namespaces[prefix]) { ++ throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" + ++ "There is already a mapping existing: '(" + prefix + "," + this.get(prefix) + ")'!"); ++ } else { ++ jQuery.each(this._namespaces, function (k1,v1) { ++ if (v1 === namespace && k1 !== prefix) { ++ throw new Error("ERROR: Trying to register namespace prefix mapping (" + prefix + "," + namespace + ")!" + ++ "There is already a mapping existing: '(" + k1 + "," + namespace + ")'!"); ++ } ++ }); ++ } ++ /* if not, just add them */ ++ this._namespaces[prefix] = namespace; ++ return this; ++}; ++ ++// ### addOrReplace(prefix, namespace) ++// This method adds new prefix mappings to the ++// current instance. This will overwrite existing mappings. ++// **Parameters**: ++// *{string|object}* **prefix** The prefix to be set. If it is an object, the ++// method will be applied to all key,value pairs sequentially. ++// *{string}* **namespace** The namespace to be set. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Namespaces}* : The current namespaces instance. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("", "http://..."); ++// // is always equal to ++// namespaces.base("http://..."); // <-- setter of base namespace ++VIE.prototype.Namespaces.prototype.addOrReplace = function (prefix, namespace) { ++ if (typeof prefix === "object") { ++ for (var k1 in prefix) { ++ this.addOrReplace(k1, prefix[k1]); ++ } ++ return this; ++ } ++ this.remove(prefix); ++ this.removeNamespace(namespace); ++ return this.add(prefix, namespace); ++}; ++ ++// ### get(prefix) ++// This method retrieves a namespaces, given a prefix. If the ++// prefix is the empty string, the base namespace is returned. ++// **Parameters**: ++// *{string}* **prefix** The prefix to be retrieved. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{string|undefined}* : The namespace or ```undefined``` if no namespace could be found. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("test", "http://test.ns"); ++// console.log(namespaces.get("test")); // <-- "http://test.ns" ++VIE.prototype.Namespaces.prototype.get = function (prefix) { ++ if (prefix === "") { ++ return this.base(); ++ } ++ return this._namespaces[prefix]; ++}; ++ ++// ### getPrefix(namespace) ++// This method retrieves a prefix, given a namespace. ++// **Parameters**: ++// *{string}* **namespace** The namespace to be retrieved. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{string|undefined}* : The prefix or ```undefined``` if no prefix could be found. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("test", "http://test.ns"); ++// console.log(namespaces.getPrefix("http://test.ns")); // <-- "test" ++VIE.prototype.Namespaces.prototype.getPrefix = function (namespace) { ++ var prefix; ++ if (namespace.indexOf('<') === 0) { ++ namespace = namespace.substring(1, namespace.length - 1); ++ } ++ jQuery.each(this._namespaces, function (k1,v1) { ++ if (namespace.indexOf(v1) === 0) { ++ prefix = k1; ++ } ++ ++ if (namespace.indexOf(k1 + ':') === 0) { ++ prefix = k1; ++ } ++ }); ++ return prefix; ++}; ++ ++// ### contains(prefix) ++// This method checks, whether a prefix is stored in the instance. ++// **Parameters**: ++// *{string}* **prefix** The prefix to be checked. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* : ```true``` if the prefix could be found, ```false``` otherwise. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("test", "http://test.ns"); ++// console.log(namespaces.contains("test")); // <-- true ++VIE.prototype.Namespaces.prototype.contains = function (prefix) { ++ return (prefix in this._namespaces); ++}; ++ ++// ### containsNamespace(namespace) ++// This method checks, whether a namespace is stored in the instance. ++// **Parameters**: ++// *{string}* **namespace** The namespace to be checked. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* : ```true``` if the namespace could be found, ```false``` otherwise. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("test", "http://test.ns"); ++// console.log(namespaces.containsNamespace("http://test.ns")); // <-- true ++VIE.prototype.Namespaces.prototype.containsNamespace = function (namespace) { ++ return this.getPrefix(namespace) !== undefined; ++}; ++ ++// ### update(prefix, namespace) ++// This method overwrites the namespace that is stored under the ++// prefix ``prefix`` with the new namespace ``namespace``. ++// If a namespace is already bound to another prefix, an Error is thrown. ++// **Parameters**: ++// *{string}* **prefix** The prefix. ++// *{string}* **namespace** The namespace. ++// **Throws**: ++// *{Error}* If a namespace is already bound to another prefix. ++// **Returns**: ++// *{VIE.Namespaces}* : The namespace instance. ++// **Example usage**: ++// ++// ... ++VIE.prototype.Namespaces.prototype.update = function (prefix, namespace) { ++ this.remove(prefix); ++ return this.add(prefix, namespace); ++}; ++ ++// ### updateNamespace(prefix, namespace) ++// This method overwrites the prefix that is bound to the ++// namespace ``namespace`` with the new prefix ``prefix``. If another namespace is ++// already registered with the given ``prefix``, an Error is thrown. ++// **Parameters**: ++// *{string}* **prefix** The prefix. ++// *{string}* **namespace** The namespace. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Namespaces}* : The namespace instance. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.add("test", "http://test.ns"); ++// namespaces.updateNamespace("test2", "http://test.ns"); ++// namespaces.get("test2"); // <-- "http://test.ns" ++VIE.prototype.Namespaces.prototype.updateNamespace = function (prefix, namespace) { ++ this.removeNamespace(prefix); ++ return this.add(prefix, namespace); ++}; ++ ++// ### remove(prefix) ++// This method removes the namespace that is stored under the prefix ``prefix``. ++// **Parameters**: ++// *{string}* **prefix** The prefix to be removed. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Namespaces}* : The namespace instance. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.add("test", "http://test.ns"); ++// namespaces.get("test"); // <-- "http://test.ns" ++// namespaces.remove("test"); ++// namespaces.get("test"); // <-- undefined ++VIE.prototype.Namespaces.prototype.remove = function (prefix) { ++ if (prefix) { ++ delete this._namespaces[prefix]; ++ } ++ return this; ++}; ++ ++// ### removeNamespace(namespace) ++// This method removes removes the namespace ``namespace`` from the instance. ++// **Parameters**: ++// *{string}* **namespace** The namespace to be removed. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{VIE.Namespaces}* : The namespace instance. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.add("test", "http://test.ns"); ++// namespaces.get("test"); // <-- "http://test.ns" ++// namespaces.removeNamespace("http://test.ns"); ++// namespaces.get("test"); // <-- undefined ++VIE.prototype.Namespaces.prototype.removeNamespace = function (namespace) { ++ var prefix = this.getPrefix(namespace); ++ if (prefix) { ++ delete this._namespaces[prefix]; ++ } ++ return this; ++}; ++ ++// ### toObj() ++// This method serializes the namespace instance into an associative ++// array representation. The base namespace is given an empty ++// string as key. ++// **Parameters**: ++// *{boolean}* **omitBase** If set to ```true``` this omits the baseNamespace. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{object}* : A serialization of the namespaces as an object. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.add("test", "http://test.ns"); ++// console.log(namespaces.toObj()); ++// // <-- {"" : "http://base.ns/", ++// "test": "http://test.ns"} ++// console.log(namespaces.toObj(true)); ++// // <-- {"test": "http://test.ns"} ++VIE.prototype.Namespaces.prototype.toObj = function (omitBase) { ++ if (omitBase) { ++ return jQuery.extend({}, this._namespaces); ++ } ++ return jQuery.extend({'' : this._base}, this._namespaces); ++}; ++ ++// ### curie(uri, safe) ++// This method converts a given ++// URI into a CURIE (or SCURIE), based on the given ```VIE.Namespaces``` object. ++// If the given uri is already a URI, it is left untouched and directly returned. ++// If no prefix could be found, an ```Error``` is thrown. ++// **Parameters**: ++// *{string}* **uri** The URI to be transformed. ++// *{boolean}* **safe** A flag whether to generate CURIEs or SCURIEs. ++// **Throws**: ++// *{Error}* If no prefix could be found in the passed namespaces. ++// **Returns**: ++// *{string}* The CURIE or SCURIE. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var uri = ""; ++// ns.curie(uri, false); // --> dbp:Person ++// ns.curie(uri, true); // --> [dbp:Person] ++VIE.prototype.Namespaces.prototype.curie = function(uri, safe){ ++ return VIE.Util.toCurie(uri, safe, this); ++}; ++ ++// ### isCurie(curie) ++// This method checks, whether ++// the given string is a CURIE and returns ```true``` if so and ```false```otherwise. ++// **Parameters**: ++// *{string}* **curie** The CURIE (or SCURIE) to be checked. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* ```true``` if the given curie is a CURIE or SCURIE and ```false``` otherwise. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var uri = ""; ++// var curie = "dbp:Person"; ++// var scurie = "[dbp:Person]"; ++// var text = "This is some text."; ++// ns.isCurie(uri); // --> false ++// ns.isCurie(curie); // --> true ++// ns.isCurie(scurie); // --> true ++// ns.isCurie(text); // --> false ++VIE.prototype.Namespaces.prototype.isCurie = function (something) { ++ return VIE.Util.isCurie(something, this); ++}; ++ ++// ### uri(curie) ++// This method converts a ++// given CURIE (or save CURIE) into a URI, based on the given ```VIE.Namespaces``` object. ++// **Parameters**: ++// *{string}* **curie** The CURIE to be transformed. ++// **Throws**: ++// *{Error}* If no URI could be assembled. ++// **Returns**: ++// *{string}* : A string, representing the URI. ++// **Example usage**: ++// ++// var ns = new myVIE.Namespaces( ++// "http://viejs.org/ns/", ++// { "dbp": "http://dbpedia.org/ontology/" } ++// ); ++// var curie = "dbp:Person"; ++// var scurie = "[dbp:Person]"; ++// ns.uri(curie); ++// --> ++// ns.uri(scurie); ++// --> ++VIE.prototype.Namespaces.prototype.uri = function (curie) { ++ return VIE.Util.toUri(curie, this); ++}; ++ ++// ### isUri(something) ++// This method checks, whether the given string is a URI. ++// **Parameters**: ++// *{string}* **something** : The string to be checked. ++// **Throws**: ++// *nothing* ++// **Returns**: ++// *{boolean}* : ```true``` if the string is a URI, ```false``` otherwise. ++// **Example usage**: ++// ++// var namespaces = new vie.Namespaces("http://base.ns/"); ++// namespaces.addOrReplace("test", "http://test.ns"); ++// var uri = ""; ++// var curie = "test:Person"; ++// namespaces.isUri(uri); // --> true ++// namespaces.isUri(curie); // --> false ++VIE.prototype.Namespaces.prototype.isUri = VIE.Util.isUri; ++})(); +\ No newline at end of file +diff --git a/core/modules/system/system.module b/core/modules/system/system.module +index 3b2d131..f8c67e8 100644 +--- a/core/modules/system/system.module ++++ b/core/modules/system/system.module +@@ -1839,7 +1839,7 @@ function system_library_info() { + ), + ); + +- // Underscore ++ // Underscore. + $libraries['underscore'] = array( + 'title' => 'Underscore.js', + 'website' => 'http://underscorejs.org/', +@@ -1849,7 +1849,7 @@ function system_library_info() { + ), + ); + +- // Backbone ++ // Backbone. + $libraries['backbone'] = array( + 'title' => 'Backbone.js', + 'website' => 'http://backbonejs.org/', +@@ -1862,6 +1862,35 @@ function system_library_info() { + ), + ); + ++ // VIE. ++ $libraries['vie.core'] = array( ++ 'title' => 'VIE.js core (excluding services, views and xdr)', ++ 'website' => 'http://viejs.org/', ++ 'version' => '2.0.0-dev', ++ 'js' => array( ++ 'core/misc/vie/vie-core.js' => array('group' => JS_LIBRARY), ++ ), ++ 'dependencies' => array( ++ array('system', 'jquery'), ++ array('system', 'underscore'), ++ array('system', 'backbone'), ++ ), ++ ); ++ ++ // Create. ++ $libraries['create.editonly'] = array( ++ 'title' => 'Create.js edit-only (editing features only)', ++ 'website' => 'http://backbonejs.org/', ++ 'version' => '1.0.0-dev', ++ 'js' => array( ++ 'core/misc/create/create-editonly.js' => array('group' => JS_LIBRARY), ++ ), ++ 'dependencies' => array( ++ array('system', 'vie.core'), ++ array('system', 'jquery.ui.widget'), ++ ), ++ ); ++ + // Cookie. + $libraries['jquery.cookie'] = array( + 'title' => 'Cookie',