-/ <% end %>
-/
-/ <% f.fields_for :parts do |g| %>
-/ <% unless g.object.disabled? %>
-/
<%= g.text_area :body, :class => 'big' %>
-/ <% end %>
-/ <% end %>
-/
-/
-/
-/
-/ <% content_for :head do %>
-/ <%= javascript_include_tag 'admin/plugins/iphoneSwitch', 'admin/plugins/checkbox', 'admin/plugins/carousel', 'codemirror/codemirror', 'admin/pages' %>
-/ <%= stylesheet_link_tag 'carousel', 'admin/page_parts' %>
-/ <% end %>
-/ <% end %>
\ No newline at end of file
+ .wrapper
+ %ul{ :id => "parts" }
+ = f.fields_for :parts do |g|
+ %li{ :style => "#{'display: none' if g.object.disabled?}" }
+ %code= g.text_area :value
+ = g.hidden_field :name
+ = g.hidden_field :slug
+ = g.hidden_field :disabled, :class => 'disabled'
\ No newline at end of file
diff --git a/app/views/admin/pages/edit.html.haml b/app/views/admin/pages/edit.html.haml
index 7c4bfb1c..866b62ab 100644
--- a/app/views/admin/pages/edit.html.haml
+++ b/app/views/admin/pages/edit.html.haml
@@ -1,8 +1,5 @@
- title link_to(@page.title.blank? ? @page.title_was : @page.title, '#', :rel => 'page_title', :title => t('.ask_for_title'), :class => 'editable')
-- content_for :head do
- = javascript_include_tag 'admin/pages'
-
- content_for :submenu do
= render 'admin/shared/menu/contents'
diff --git a/app/views/admin/pages/new.html.haml b/app/views/admin/pages/new.html.haml
index 5868abae..d30452e8 100644
--- a/app/views/admin/pages/new.html.haml
+++ b/app/views/admin/pages/new.html.haml
@@ -1,8 +1,5 @@
- title t('.title')
-- content_for :head do
- = javascript_include_tag 'admin/pages'
-
- content_for :submenu do
= render 'admin/shared/menu/contents'
diff --git a/config/cucumber.yml b/config/cucumber.yml
index b7acd380..621a14ce 100644
--- a/config/cucumber.yml
+++ b/config/cucumber.yml
@@ -1,7 +1,8 @@
<%
rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
-rerun_opts = rerun.to_s.strip.empty? ? "--format progress features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
-std_opts = "#{rerun_opts} --format rerun --out rerun.txt --strict --tags ~@wip"
+rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
+std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} --strict --tags ~@wip"
%>
-default: <%= std_opts %>
+default: <%= std_opts %> features
wip: --tags @wip:3 --wip features
+rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
diff --git a/config/database.yml b/config/database.yml
index 0b10b23d..8b145ac5 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -5,7 +5,7 @@ development:
<<: *defaults
database: locomotive_dev
-test:
+test: &test
<<: *defaults
database: locomotive_test
@@ -15,3 +15,6 @@ production:
username: user
password: pass
database: fanboy
+
+cucumber:
+ <<: *test
\ No newline at end of file
diff --git a/config/initializers/mongoid.rb b/config/initializers/mongoid.rb
index 7e77f27b..25b16194 100644
--- a/config/initializers/mongoid.rb
+++ b/config/initializers/mongoid.rb
@@ -14,8 +14,10 @@ end
## various patches
-# Enabling scope in validates_uniqueness_of validation
+
module Mongoid #:nodoc:
+
+ # Enabling scope in validates_uniqueness_of validation
module Validations #:nodoc:
class UniquenessValidator < ActiveModel::EachValidator
def validate_each(document, attribute, value, scope = nil)
@@ -26,4 +28,14 @@ module Mongoid #:nodoc:
end
end
end
-end
+
+ # FIX BUG #71 http://github.com/durran/mongoid/commit/47a97094b32448aa09965c854a24c78803c7f42e
+ module Associations
+ module InstanceMethods
+ def update_embedded(name)
+ association = send(name)
+ association.to_a.each { |doc| doc.save if doc.changed? || doc.new_record? } unless association.blank?
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/config/locales/admin_ui_en.yml b/config/locales/admin_ui_en.yml
index 93c51024..0d02ebfc 100644
--- a/config/locales/admin_ui_en.yml
+++ b/config/locales/admin_ui_en.yml
@@ -20,7 +20,7 @@ en:
messages:
confirm: Are you sure ?
-
+
shared:
header:
welcome: Welcome, {{name}}
@@ -48,6 +48,9 @@ en:
title: Listing pages
no_items: "There are no pages for now. Just click here to create the first one."
new: new page
+ messages:
+ successful_create: "Page was successfully created."
+ successful_update: "Page was successfully updated."
layouts:
index:
diff --git a/config/routes.rb b/config/routes.rb
index 2ea5399b..7cec9adf 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -22,7 +22,9 @@ Locomotive::Application.routes.draw do |map|
get :get_path, :on => :collection
end
- resources :layouts
+ resources :layouts do
+ resources :page_parts, :only => :index
+ end
resources :snippets
# get 'login' => 'sessions#new', :as => :new_account_session
diff --git a/doc/TODO b/doc/TODO
index 5cbbf720..3d12f716 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -15,14 +15,19 @@ x store node closed or open in cookies
x snippets section
x menu items have to be translated
x layout needs at least content_for_layout
-- validates_uniqueness_of :slug, :scope => :id
-- page parts
-- can not delete index + 404 pages
+x parts js/css:
+ x codemirror
+ x change bg (separator)
+ x when a tab is selected, if we change layout, we should move to the first visible one
+x page parts
+x layout part should be always in first
+x pages section (CRUD)
- slug unique within a folder
-- pages section (CRUD)
+- validates_uniqueness_of :slug, :scope => :id
- refactoring page.rb => create module pagetree
- my account section (part of settings)
- layouts section
- create 404 + index pages once a site is created
+- can not delete index + 404 pages
- refactoring admin crud (pages + layouts + snippets)
- remove all pages, snippets, ...etc when destroying a website
\ No newline at end of file
diff --git a/features/admin/login.feature b/features/admin/login.feature
new file mode 100644
index 00000000..cf4f4dd0
--- /dev/null
+++ b/features/admin/login.feature
@@ -0,0 +1,19 @@
+@site_up
+Feature: Login
+ In order to access locomotive admin panel
+ As an administrator
+ I want to log in
+
+Scenario: Successful authentication
+ When I go to login
+ And I fill in "account_email" with "admin@locomotiveapp.org"
+ And I fill in "account_password" with "easyone"
+ And I press "Log in"
+ Then I should see "Listing pages"
+
+Scenario: Failed authentication
+ When I go to login
+ And I fill in "account_email" with "admin@locomotiveapp.org"
+ And I fill in "account_password" with ""
+ And I press "Log in"
+ Then I should not see "Listing pages"
\ No newline at end of file
diff --git a/features/admin/pages.feature b/features/admin/pages.feature
new file mode 100644
index 00000000..62ab2e98
--- /dev/null
+++ b/features/admin/pages.feature
@@ -0,0 +1,31 @@
+@site_up
+@authenticated
+Feature: Manage Skills
+ In order to manage pages
+ As an administrator
+ I want to add/edit/delete pages of my site
+
+Scenario: Pages list is not accessible for non authenticated accounts
+ Given I am not authenticated
+ When I go to pages
+ Then I should see "Login"
+
+Scenario: Creating a valid page
+ When I go to pages
+ And I follow "new page"
+ And I fill in "Title" with "Test"
+ And I fill in "Slug" with "test"
+ And I select "Home page" from "Parent"
+ And I fill in "page_parts_attributes_0_value" with "Lorem ipsum...."
+ And I press "Create"
+ Then I should see "Page was successfully created."
+ And I should have "Lorem ipsum...." in the test page layout
+
+Scenario: Updating a valid page
+ When I go to pages
+ And I follow "Home page"
+ And I fill in "Title" with "Home page !"
+ And I fill in "page_parts_attributes_0_value" with "My new content is here"
+ And I press "Update"
+ Then I should see "Page was successfully updated."
+ And I should have "My new content is here" in the index page layout
\ No newline at end of file
diff --git a/features/step_definitions/admin_steps.rb b/features/step_definitions/admin_steps.rb
new file mode 100644
index 00000000..4e8f69b1
--- /dev/null
+++ b/features/step_definitions/admin_steps.rb
@@ -0,0 +1,63 @@
+Before('@site_up') do
+ create_site_and_admin_account
+ create_layout_samples
+ create_index_page
+end
+
+Before('@authenticated') do
+ Given %{I am an authenticated user}
+end
+
+### Authentication
+
+Given /^I am not authenticated$/ do
+ visit('/admin/logout')
+end
+
+
+Given /^I am an authenticated user$/ do
+ Given %{I go to login}
+ And %{I fill in "account_email" with "admin@locomotiveapp.org"}
+ And %{I fill in "account_password" with "easyone"}
+ And %{I press "Log in"}
+end
+
+Then /^I am redirected to "([^\"]*)"$/ do |url|
+ assert [301, 302].include?(@integration_session.status), "Expected status to be 301 or 302, got #{@integration_session.status}"
+ location = @integration_session.headers["Location"]
+ assert_equal url, location
+ visit location
+end
+
+### Pages
+
+
+Then /^I should have "(.*)" in the (.*) page (.*)$/ do |content, page_slug, slug|
+ page = @site.pages.where(:slug => page_slug).first
+ part = page.parts.where(:slug => slug).first
+ part.should_not be_nil
+ part.value.should == content
+end
+
+## Common
+
+def create_site_and_admin_account
+ @site = Factory(:site, :name => 'Locomotive test website', :subdomain => 'test')
+ @admin = Factory(:account, { :name => 'Admin', :email => 'admin@locomotiveapp.org' })
+end
+
+def create_layout_samples
+ Factory(:layout, :site => @site, :name => 'One column', :value => %{
+
+ My website
+
+
+
\{\{ content_for_layout \}\}
+
+ })
+ Factory(:layout, :site => @site)
+end
+
+def create_index_page
+ Factory(:page, :site => @site, :layout => @site.layouts.last, :parts => [PagePart.new(:slug => 'layout', :name => 'Body', :value => 'Hello world')])
+end
\ No newline at end of file
diff --git a/features/step_definitions/web_steps.rb b/features/step_definitions/web_steps.rb
index 55ac5693..e5775052 100644
--- a/features/step_definitions/web_steps.rb
+++ b/features/step_definitions/web_steps.rb
@@ -6,6 +6,7 @@
require 'uri'
+require 'cgi'
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
module WithinHelpers
@@ -147,7 +148,8 @@ end
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should contain "([^\"]*)"$/ do |field, selector, value|
with_scope(selector) do
- field_value = find_field(field).value
+ field = find_field(field)
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
if field_value.respond_to? :should
field_value.should =~ /#{value}/
else
@@ -158,11 +160,12 @@ end
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should not contain "([^\"]*)"$/ do |field, selector, value|
with_scope(selector) do
- field_value = find_field(field).value
+ field = find_field(field)
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
if field_value.respond_to? :should_not
field_value.should_not =~ /#{value}/
else
- assert_no_match(/#{value}/, field_value.value)
+ assert_no_match(/#{value}/, field_value)
end
end
end
@@ -199,9 +202,11 @@ Then /^(?:|I )should be on (.+)$/ do |page_name|
end
Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
- actual_params = CGI.parse(URI.parse(current_url).query)
- expected_params = Hash[expected_pairs.rows_hash.map{|k,v| [k,[v]]}]
-
+ query = URI.parse(current_url).query
+ actual_params = query ? CGI.parse(query) : {}
+ expected_params = {}
+ expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
+
if actual_params.respond_to? :should
actual_params.should == expected_params
else
diff --git a/features/support/env.rb b/features/support/env.rb
index 7a2362f4..109bbabd 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -10,7 +10,6 @@ require File.expand_path(File.dirname(__FILE__) + '/../../config/environment')
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
require 'cucumber/rails/rspec'
require 'cucumber/rails/world'
-require 'cucumber/rails/active_record'
require 'cucumber/web/tableish'
require 'capybara/rails'
@@ -34,26 +33,27 @@ Capybara.default_selector = :css
# of your scenarios, as this makes it hard to discover errors in your application.
ActionController::Base.allow_rescue = false
-# If you set this to true, each scenario will run in a database transaction.
-# You can still turn off transactions on a per-scenario basis, simply tagging
-# a feature or scenario with the @no-txn tag. If you are using Capybara,
-# tagging with @culerity or @javascript will also turn transactions off.
-#
-# If you set this to false, transactions will be off for all scenarios,
-# regardless of whether you use @no-txn or not.
-#
-# Beware that turning transactions off will leave data in your database
-# after each scenario, which can lead to hard-to-debug failures in
-# subsequent scenarios. If you do this, we recommend you create a Before
-# block that will explicitly put your database in a known state.
-Cucumber::Rails::World.use_transactional_fixtures = true
+require 'factory_girl'
+require 'spec/factories'
-# How to clean your database when transactions are turned off. See
-# http://github.com/bmabey/database_cleaner for more info.
-if defined?(ActiveRecord::Base)
- begin
- require 'database_cleaner'
- DatabaseCleaner.strategy = :truncation
- rescue LoadError => ignore_if_database_cleaner_not_present
- end
+Before do
+ Mongoid.master.collections.each(&:drop)
end
+
+Locomotive.configure do |config|
+ config.default_domain = 'example.com'
+end
+
+# class ActionController::Integration::Session
+# def reset_with_test_subdomain!
+# self.reset_without_test_subdomain!
+# self.host = "test.example.com"
+# end
+# alias_method_chain :reset!, :test_subdomain
+# end
+#
+# class ActionDispatch::Integration::Session
+# DEFAULT_HOST = 'test.example.com'
+# end
+
+Capybara.default_host = 'test.example.com'
\ No newline at end of file
diff --git a/features/support/paths.rb b/features/support/paths.rb
index c575c075..3c015e7e 100644
--- a/features/support/paths.rb
+++ b/features/support/paths.rb
@@ -7,10 +7,16 @@ module NavigationHelpers
#
def path_to(page_name)
case page_name
-
+
when /the home\s?page/
'/'
-
+ when /login/
+ new_account_session_path
+ when /logout/
+ destroy_account_session_path
+ when /pages/
+ admin_pages_path
+
# Add more mappings here.
# Here is an example that pulls values out of the Regexp:
#
@@ -18,8 +24,14 @@ module NavigationHelpers
# user_profile_path(User.find_by_login($1))
else
- raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
- "Now, go and add a mapping in #{__FILE__}"
+ begin
+ page_name =~ /the (.*) page/
+ path_components = $1.split(/\s+/)
+ self.send(path_components.push('path').join('_').to_sym)
+ rescue Object => e
+ raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
+ "Now, go and add a mapping in #{__FILE__}"
+ end
end
end
end
diff --git a/lib/tasks/cucumber.rake b/lib/tasks/cucumber.rake
index 6d12f71f..522aafc8 100644
--- a/lib/tasks/cucumber.rake
+++ b/lib/tasks/cucumber.rake
@@ -14,18 +14,24 @@ begin
require 'cucumber/rake/task'
namespace :cucumber do
- Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
+ Cucumber::Rake::Task.new(:ok, 'Run features that should pass') do |t|
t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
t.fork = true # You may get faster startup if you set this to false
t.profile = 'default'
end
- Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
+ Cucumber::Rake::Task.new(:wip, 'Run features that are being worked on') do |t|
t.binary = vendored_cucumber_bin
t.fork = true # You may get faster startup if you set this to false
t.profile = 'wip'
end
+ Cucumber::Rake::Task.new(:rerun, 'Record failing features and run only them if any exist') do |t|
+ t.binary = vendored_cucumber_bin
+ t.fork = true # You may get faster startup if you set this to false
+ t.profile = 'rerun'
+ end
+
desc 'Run all features'
task :all => [:ok, :wip]
end
diff --git a/public/images/admin/form/header-left-on.png b/public/images/admin/form/header-left-on.png
new file mode 100644
index 00000000..6842c86d
Binary files /dev/null and b/public/images/admin/form/header-left-on.png differ
diff --git a/public/images/admin/form/header-right-on.png b/public/images/admin/form/header-right-on.png
new file mode 100644
index 00000000..ba18ec85
Binary files /dev/null and b/public/images/admin/form/header-right-on.png differ
diff --git a/public/javascripts/admin/application.js b/public/javascripts/admin/application.js
index ab076e0f..ce85514a 100644
--- a/public/javascripts/admin/application.js
+++ b/public/javascripts/admin/application.js
@@ -24,7 +24,7 @@ var addCodeMirrorEditor = function(type, el, parser) {
if (type == 'liquid') type = 'xml';
var editor = CodeMirror.fromTextArea(el.attr('id'), {
- height: "330px",
+ height: "400px",
parserfile: parserfile,
stylesheet: ["/stylesheets/admin/plugins/codemirror/" + type + "colors.css", "/stylesheets/admin/plugins/codemirror/liquidcolors.css"],
path: "/javascripts/admin/plugins/codemirror/",
diff --git a/public/javascripts/admin/page_parts.js b/public/javascripts/admin/page_parts.js
new file mode 100644
index 00000000..6786cf8e
--- /dev/null
+++ b/public/javascripts/admin/page_parts.js
@@ -0,0 +1,83 @@
+$(document).ready(function() {
+
+ // slider
+ var resetSlider = function() {
+ $('#page-parts .wrapper ul').wslide({
+ width: 880,
+ height: 400,
+ autolink: false,
+ duration: 300,
+ horiz: true
+ });
+ }
+
+ resetSlider();
+
+ // codemirror
+ $('#parts code textarea').each(function() { addCodeMirrorEditor('liquid', $(this)); });
+
+ var refreshParts = function(parts) {
+ console.log('refreshParts');
+ $('#page-parts .nav a').removeClass('enabled');
+
+ $(parts).each(function() {
+ console.log("iterating..." + this.slug);
+ var control = $('#control-part-' + this.slug);
+
+ // adding missing part
+ if (control.size() == 0) {
+ console.log('adding part');
+ var nbParts = $('#page-parts .nav a').size();
+ $('#page-parts .nav .clear').before('' + this.name + '');
+
+ var textareaInput = '';
+ var hiddenInputs = ''
+ hiddenInputs += ''
+ $('#page-parts .wrapper ul').append('
' + textareaInput + '' + hiddenInputs + '
');
+
+ resetSlider();
+ $('#parts li:last code textarea').each(function() { addCodeMirrorEditor('liquid', $(this)); });
+ } else {
+ var index = parseInt(control.attr('class').match(/part-(.+)/)[1]) + 1;
+ var wrapper = $('#parts-' + index);
+
+ // updating part
+ control.html('' + this.name + '').addClass('enabled').show();
+ wrapper.find('input.disabled').val('false');
+ wrapper.show();
+ }
+ });
+
+ // removing or hiding parts
+ $('#page-parts .nav a:not(.enabled)').each(function() {
+ var index = parseInt($(this).attr('class').match(/part-(.+)/)[1]) + 1;
+ var wrapper = $('#parts-' + index);
+ if (wrapper.hasClass('new')) {
+ $(this).remove(); wrapper.remove();
+ } else {
+ wrapper.find('input.disabled').val('true');
+ $(this).hide(); wrapper.hide();
+ }
+ });
+
+ // go to the first one if we hid the selected wrapper
+ var selectedNav = $('#page-parts .nav a.on');
+ if (selectedNav.size() == 1) {
+ var index = parseInt(selectedNav.attr('class').match(/part-(.+)/)[1]) + 1;
+ if ($('#parts-' + index + ':visible').size() == 0)
+ $('#page-parts .nav a:first').click();
+ } else
+ $('#page-parts .nav a:first').click();
+ }
+
+ var loadPartsFromLayout = function() {
+ if ($('#page_layout_id').val() == '')
+ return ;
+
+ var url = $('#page_layout_id').attr('data_url').replace('_id_to_replace_', $('#page_layout_id').val());
+ $.get(url, '', function(data) { refreshParts(data.parts) }, 'json');
+ }
+
+ $('#page_layout_id').change(loadPartsFromLayout);
+
+});
\ No newline at end of file
diff --git a/public/javascripts/admin/plugins/wslide.js b/public/javascripts/admin/plugins/wslide.js
new file mode 100644
index 00000000..5485ccb2
--- /dev/null
+++ b/public/javascripts/admin/plugins/wslide.js
@@ -0,0 +1,125 @@
+/**
+* wSlide 0.1 - http://www.webinventif.fr/wslide-plugin/
+*
+* Rendez vos sites glissant !
+*
+* Copyright (c) 2008 Julien Chauvin (webinventif.fr)
+* Licensed under the Creative Commons License:
+* http://creativecommons.org/licenses/by/3.0/
+*
+* Date: 2008-01-27
+*/
+(function($){
+ $.fn.wslide = function(h) {
+ h = jQuery.extend({
+ width: 150,
+ height: 150,
+ pos: 1,
+ col: 1,
+ effect: 'swing',
+ fade: false,
+ horiz: false,
+ autolink: true,
+ duration: 1500
+ }, h);
+ function gogogo(g){
+ g.each(function(i){
+ var a = $(this);
+ var uniqid = a.attr('id');
+ if(uniqid == undefined){
+ uniqid = 'wslide'+i;
+ }
+
+ if ($(this).parent().hasClass('wslide-wrap'))
+ a = $(this).parent();
+ else {
+ $(this).wrap('');
+ a = $('#'+uniqid+'-wrap');
+ }
+
+ var b = a.find('ul li');
+ var effets = h.effect;
+ if(jQuery.easing.easeInQuad == undefined && (effets!='swing' || effets!='normal')){
+ effets = 'swing';
+ }
+ var typex = h.width;
+ var typey = h.height;
+ function resultante(prop){
+ var tempcalc = prop;
+ tempcalc = tempcalc.split('px');
+ tempcalc = tempcalc[0];
+ return Number(tempcalc);
+ }
+ var litypex = typex-(resultante(b.css('padding-left'))+resultante(b.css('padding-right')));
+ var litypey = typey-(resultante(b.css('padding-top'))+resultante(b.css('padding-bottom')));
+ var col = h.col;
+ if(h.horiz){
+ col = Number(b.length+1);
+ }
+ var manip = '';
+ var ligne = Math.ceil(Number(b.length)/col);
+ a.css('overflow','hidden').css('position','relative').css('text-align','left').css('height',typey+'px').css('width',typex+'px').css('margin','0').css('padding','0');
+ a.find('ul').css('position','absolute').css('margin','0').css('padding','0').css('width',Number((col+0)*typex)+'px').css('height',Number(ligne*typey)+'px');
+ b.css('display','block').css('overflow','hidden').css('float','left').css('height',litypey+'px').css('width',litypex+'px');
+ b.each(function (i) {
+ var offset = a.offset();
+ var thisoffset = $(this).offset();
+ $(this).attr('id',uniqid+'-'+Number(i+1)).attr('rel', Number(thisoffset.left-offset.left)+':'+Number(thisoffset.top-offset.top));
+ manip += ' '+Number(i+1)+'';
+ });
+
+ if(typeof h.autolink == 'boolean'){
+ if(h.autolink){
+ a.after('